随笔

渐进式重温 Webpack

一,配置基础代码

增加配置文件 webpack.base.js

module.exports = {
    mode: 'development',
    entry: path.resolve(__dirname, 'src', 'index.js'),
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
        publicPath: '/',
                clean: true,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, './', 'index.html'),
            filename: 'index.html',
            hash: true,
        }),
    ],
}

二,配置 JS Loader

module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /node_modules/,
      use: {
        loader: "babel-loader",
        options: {
          presets: ["@babel/preset-env", "@babel/preset-react"]
        }
      }
    },
  ]
}

三,第一次执行 Webpack

先新建一个组件

import React, { Suspense }  from 'react';
import dayjs from 'dayjs';

function App() {
    return <div>
        Hello! {dayjs(new Date()).format('yyyy')}
    </div>
}

执行编译

webpack —config=webpack.base.js

得到 bundle.js 但是发现 React,dayjs 库代码打包到一起了

四,分离库文件

把 output 中固定的 bundle.js 改为 [name].bundle.js 然后加入以下代码

optimization: {
  splitChunks: {
    chunks: 'all',
  },
},

编译后得到两个 JS 文件,一个 main.bundle.js 是组件代码,另一个很长 vendors-node_modules_dayjs_dayjs_min_js-node_modules_react-dom_index_js.bundle.js 是库代码集合。

五,懒加载组件

const Child = React.lazy(()=>import('./child'));

import React from 'react';
import _ from 'lodash';

function Child() {
    return <div>
        Child {_.camelCase('Foo Bar')}
    </div>
}

打包后发现又多了两个文件 src_child_js.bundle.js 和 vendors-node_modules_lodash_lodash_js.bundle.js 尝试在App组件中import lodash,会发现 vendors 开头的文件变成了一个 vendors-node_modules_dayjs_dayjs_min_js-node_modules_lodash_lodash_js-node_modules_react-dom_-423d76.bundle.js。

六,集成开发服务器

不想更新完还要丢到服务器才能看,所以需要一个可以编译后刷新页面就能看到最新效果的工具。 增加如下配置

devServer: {
  contentBase: './dist',
}

sourcemap 自然也不能忘,从前端到后台调试 sourcemap 是必备

devtool: 'inline-source-map'

启动集成的服务器

webpack serve —config=webpack.base.js

然后就可以在浏览器看到啦,而且改了代码还会自动刷新页面哇

七,增加样式文件支持

直接使用 styl 吧,现在不会个 CSS 预处理器都不跟你打招呼 rules 规则里面增加配置

{
      test: /\.styl$/,
    include: /src/,
    use: [
      "style-loader",
      "css-loader",
      "stylus-loader",
    ]
}

然后,给 App 组件的按钮加一个样式

.btn {
    padding: 4px 10px;    
}

看下页面,好像没问题哎~ 别急,给 child 组件的按钮也加一个不同的样式 啊 发现了没! 类名冲突了,大家都是按钮,凭什么你可以叫 .btn 我不可以叫呢?(虽然严格遵守BEM就不会出现这种问题 啊哈哈哈哈)

{
  test: /\.styl$/,
  include: /src/,
  use: [
    "style-loader",
    {
      loader: "css-loader",
      options: {
        modules: true,
      },
    },
    "stylus-loader",
  ]
},

组件使用也不是直接 import 进来就完了,变成了 import styles

八,使用三方 UI 库

对于 to B 来说,一套完整的 UI 是很庞大的,这里使用 antd。 上一步的 rule 要再加一点,之前只针对了 src 目录下的 css 文件,antd 的 css 也要支持

{
  test: /\.css$/,
  include: /node_modules/,
  use: [
    "style-loader",
    "css-loader",
  ]
},

然后,再全局 css 中 import,或者入口组件中 import 都可以啦 啊哈,感觉还不错的样子! 但是...! 看看控制台! 没有 .css 文件加载,样式都放在 JS 里了,这不是徒增工作嘛?

该 MiniCssExtractPlugin 上场了,之前规则里的 "style-loader" 全部替换成 MiniCssExtractPlugin.loader,这样每个组件导入的 CSS 和库的 CSS 就全都是分开的啦~

九,热更新 HMR

每次改代码都要刷新页面,好不方便啊,所以上热更新

devServer: {
    hot: true,
},
plugins: [
    new webpack.HotModuleReplacementPlugin(),
]

入口也要添加

if(module.hot) {
    module.hot.accept('./app.js', function(){
        const App = require("./App").default;
        ReactDOM.render(<App />, document.getElementById('root'));
    });
}

哎这样改些代码呀,样式文件呀都可以实时局部更新啦 但是,react 的状态没了,react 要用专用库

import { hot } from 'react-hot-loader/root';
export default hot(App);

啊哈 这样状态就也能保持啦,还带一个小小的loading状态~

另外呀,Webpack 5 更新了缓存系统,所以之前的 dll,HardSource 等手段基本可有可无啦,方便了许多~

代码仓库:React Startkit

本文链接:https://note.lilonghe.net/post/sembiz.html

-- EOF --