Notes

Webpack 代码分割

从 Webpack v4 开始,原来用来进行代码分割的插件 CommonsChunkPlugin 已经被弃用,而使用 SplitChunksPlugin 取代

CommonsChunkPlugin 我也不懂,直接就从 SplitChunksPlugin 开始了

不进行分割

我们原有的 webpack.config.dev.js 配置中,只定义了一个入口

...
module.exports = {
  entry: path.join(rootPath, 'src/index.js'),
  ...
};
...

执行 npm run build, 可以看到控制台的输出

Hash: 08ad500b6b32afb44d30
Version: webpack 4.17.2
Time: 1654ms
Built at: 09/12/2018 3:30:30 PM
                       Asset       Size  Chunks             Chunk Names
main.08ad500b6b32afb44d30.js    716 KiB    main  [emitted]  main
                  index.html  306 bytes          [emitted]
Entrypoint main = main.08ad500b6b32afb44d30.js
[./src/app.jsx] 2.57 KiB {main} [built]
[./src/index.js] 153 bytes {main} [built]
    + 21 hidden modules
Child html-webpack-plugin for "index.html":
     1 asset
    Entrypoint undefined = index.html
    [./node_modules/html-webpack-plugin/lib/loader.js!./public/index.html] 437 bytes {0} [built]
    [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 509 bytes {0} [built]
    [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 519 bytes {0} [built]
        + 1 hidden module

可以看到 JS 文件 main.08ad500b6b32afb44d30.js 有 700+ KB 的大小,而这当中包含了:

我们也可以通过浏览器的监控看到,整个页面的请求只有一个

Webpack 对代码分割的操作

代码分割第一步

配置入口选项

...
- entry: path.join(rootPath, 'src/index.js'),
+ entry: {
+   app: path.join(rootPath, 'src/index.js'),
+   react: ['react', 'react-dom'],
+ },
...

修改 entry 值为一个对象,即业务代码打包成 app 代码块,React 相关的库,打包成 react 代码块

执行 npm run build, 控制台的输出为

Hash: 106ac7643eeeaa8fc19c
Version: webpack 4.17.2
Time: 2042ms
Built at: 09/12/2018 4:19:04 PM
                        Asset       Size  Chunks             Chunk Names
  app.106ac7643eeeaa8fc19c.js    716 KiB     app  [emitted]  app
react.106ac7643eeeaa8fc19c.js    712 KiB   react  [emitted]  react
                   index.html  381 bytes          [emitted]
Entrypoint app = app.106ac7643eeeaa8fc19c.js
Entrypoint react = react.106ac7643eeeaa8fc19c.js
[./src/app.jsx] 2.57 KiB {app} [built]
[./src/index.js] 153 bytes {app} [built]
[0] multi react react-dom 40 bytes {react} [built]
    + 21 hidden modules
Child html-webpack-plugin for "index.html":
     1 asset
    Entrypoint undefined = index.html
    [./node_modules/html-webpack-plugin/lib/loader.js!./public/index.html] 437 bytes {0} [built]
    [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 509 bytes {0} [built]
    [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 519 bytes {0} [built]
        + 1 hidden module

可以看到,打包后得到的代码块是 app.106ac7643eeeaa8fc19c.js, react.106ac7643eeeaa8fc19c.js

但可以看到,app 代码块还是与 react 代码块的大小差不多,都是 700+ kb, react 部分的代码块并没有从 app 中分离出来,还是有重复的部分

同时,我们也可以从浏览器的监控中看到,页面请求了两个 JS 文件,因为 index.html 中插入了两个 <script>

<script type="text/javascript" src="app.106ac7643eeeaa8fc19c.js"></script><script type="text/javascript" src="react.106ac7643eeeaa8fc19c.js"></script></body>

下面,就需要将重复的代码抽离出来,避免可以模块化的代码进行了重复的请求

代码分割第二步

关于代码分割的配置项,位于

optimization: {
  splitChunks: {
    ...
  },
},

修改一下 webpack.config.dev.js 文件

module.exports = {
  ...
  optimization: {
    splitChunks: {
      chunks: 'all', 
    },
  },
};

执行 npm run build, 控制台中得到以下输出

Hash: 15bb4e959185399a4f63
Version: webpack 4.17.2
Time: 1774ms
Built at: 09/12/2018 4:28:40 PM
                                    Asset       Size             Chunks             Chunk Names
              app.15bb4e959185399a4f63.js   10.7 KiB                app  [emitted]  app
            react.15bb4e959185399a4f63.js   6.58 KiB              react  [emitted]  react
vendors~app~react.15bb4e959185399a4f63.js    708 KiB  vendors~app~react  [emitted]  vendors~app~react
                               index.html  469 bytes                     [emitted]
Entrypoint app = vendors~app~react.15bb4e959185399a4f63.js app.15bb4e959185399a4f63.js
Entrypoint react = vendors~app~react.15bb4e959185399a4f63.js react.15bb4e959185399a4f63.js
[./src/app.jsx] 2.57 KiB {app} [built]
[./src/index.js] 153 bytes {app} [built]
[0] multi react react-dom 40 bytes {react} [built]
    + 21 hidden modules
Child html-webpack-plugin for "index.html":
     1 asset
    Entrypoint undefined = index.html
    [./node_modules/html-webpack-plugin/lib/loader.js!./public/index.html] 437 bytes {0} [built]
    [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 509 bytes {0} [built]
    [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 519 bytes {0} [built]
        + 1 hidden module

可以看到,打包后得到的代码块有 3 个

我们可以从浏览器的监控中看到,页面请求了 3 个 JS 文件

关于 chunks 的选项

对于 chunks 属性,其取值可以为字符串或一个函数

chunks 为字符串时,取值有 all, async, initial

chunks 为函数时,函数签名为

chunks(chunk) {
  // 针对 chunk, 即每一模块,进行判断,返回 true/false 指明是否需要优化
  return aBoolean;
}

实际上

从 Webpack v4 开始,Webpack 会自动根据上文提到过的默认条件进行代码分割,而且是开发者零配置的代码分割

而上面说的这些代码分割的步骤,是针对项目庞大起来后需要进行自定义的代码分割

比如:现在有模块 a, b, c

若使用 Webpack 默认的代码分割方法,是会将 a, b, c 分别打包成块。但现在不想这样,我们需要将 a,b 打成一个块,c 自己一个块,那此时,我们就需要通过重写 optimization.splitChunks.cacheGroups 来进行控制了

References