# Code Split 代码分割

代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后便能按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle、控制资源加载优先级,如果使用合理,会极大减小加载时间。--- 官网原话 -- 废话

代码分割(Code Split)主要做了:

分割文件:将打包生成的文件进行分割,生成多个 js 文件

防止重复: 如果多入口文件中都引用了同一份代码,我们不希望这份代码被打包到两个文件中,导致代码重复体积更大

按需加载:需要哪个文件就加载哪个文件。(有点抽象:主要是体现在网络请求上,比如:大概是点击按钮,通过 script 标签加载需要的 JS 文件,按需的 JS 文件应该会被处理成 script 标签以便引入,以上均为猜测)

# 使用

通过内置的 optimization.splitChunks 属性进行配置

SplitChunks: 简单的来说就是 Webpack 中一个提取或分离代码的插件,主要作用是提取公共代码,防止代码被重复打包,拆分过大的 js 文件,合并零散的 js 文件,就是 CodeSplit 的具体配置和实现方式

先了解几个概念 - module、bundle、chunk 都是什么?

module:模块,在 webpack 中任何文件都可以作为一个模块,借用官网的图片,左侧的这些类型文件,都可以认为是一个模块,只是需要配置不同的 loader,将文件转换成 webpack 可以支持打包的文件。 chunk:编译完成准备输出时,webpack 将 module 按特定规则组成的一个个 chunk bundle:webpack 处理好 chunk 文件后,生成运行在浏览器中的代码

# 官方默认配置

module.exports = {
  //...
  optimization: {
    splitChunks: {
      // [initial(初始块)、async(按需加载块)、all(全部块)], 默认只对异步模块分割
      chunks: "async",

      // 公共配置属性--以下是默认值
      minSize: 20000, // 分割代码最小的大小
      minRemainingSize: 0, // 类似于minSize,最后确保提取的文件大小不能为0
      minChunks: 1, // 至少被引用的次数,满足条件才会代码分割
      maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量
      maxInitialRequests: 30, // 入口js文件最大并行请求数量
      enforceSizeThreshold: 50000, // 超过50kb一定会单独打包(此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests)

      // 单独组,哪些模块要打包到一个组 , 以上的属性都会被单独的 cacheGroups 属性设置继承,但是优先级最低
      cacheGroups: {
        // 组名--chunk名
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
          priority: -10, // 权重(越大越高)
          reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
        },
        default: {
          minChunks: 2, // 这里的minChunks权重更大
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 极简版

  • 可以直接使用,其他配置就是默认的,想定制看下面 默认配置注解版
module.exports = {
  // ...其他配置
  optimization: {
    // 代码分割配置
    splitChunks: {
      chunks: "all", // 对所有模块都进行分割、[initial(初始块)、async(按需加载块)、all(全部块)]
    },
  },
};
1
2
3
4
5
6
7
8
9

# 默认配置注解版

  • 可以直接使用,配置就是默认的,也可以自行更改
module.exports = {
  // ...其他配置
  optimization: {
    // 代码分割配置
    splitChunks: {
      chunks: "all", // 对所有模块都进行分割、

      // 公共配置属性--以下是默认值
      minSize: 20000, // 分割代码最小的大小
      minRemainingSize: 0, // 类似于minSize,最后确保提取的文件大小不能为0
      minChunks: 1, // 至少被引用的次数,满足条件才会代码分割
      maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量
      maxInitialRequests: 30, // 入口js文件最大并行请求数量
      enforceSizeThreshold: 50000, // 超过50kb一定会单独打包(此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests)

      // 单独组,哪些模块要打包到一个组 , 以上的属性都会被单独的 cacheGroups 属性设置继承,但是优先级最低
      cacheGroups: {
        // 组名
        defaultVendors: {
          // 指定chunks名称
          name: "chunk-libs",
          test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
          priority: -10, // 权重(越大越高)
          reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
        },
        default: {
          // 其他没有写的配置会使用上面的默认值
          minSize: 0, // 打包的最小文件体积,比如这里的的minSize就会 覆盖上面的 `minSize: 20000`

          minChunks: 2, // 这里的minChunks权重更大
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# cacheGroups 配置的概念

我们自己的其他非异步加载的代码和 node_modules 中三方包的代码仍然混合在一起了,这样显然不利于浏览器缓存,因为业务代码改动是会很频繁的, 但是诸多第三方代码的改动是很少的,所以我们需要进一步将业务代码和 node_modules 代码拆分出来

其他比如:UI 库、依赖库等不常变化的代码都可以使用 cacheGroups 配置成单独的文件,这样这些依赖对应的 JS 文件 名称(主要是哈希值)就不行变动, 部署到线上时,客户端就能尽量使用客户端的缓存 JS 文件,无需重复请求

# 多入口文件引入

  • 不详细写了,业务场景接触的少,不过 chrome 插件开发的话应该会用到,因为 chrome 插件开发的基础文件结构就很多
// webpack.config.js
const path = require("path");

module.exports = {
  // 多入口
  entry: {
    main: "./src/main.js",
    app: "./src/app.js",
  },
  output: {
    // ...其他配置
  },
  plugins: [
    // ... 其他配置
  ],
  optimization: {
    // 代码分割配置
    splitChunks: {
      chunks: "all", // 对所有模块都进行分割、

      // 公共配置属性--以下是默认值
      minSize: 20000, // 分割代码最小的大小
      minRemainingSize: 0, // 类似于minSize,最后确保提取的文件大小不能为0
      minChunks: 1, // 至少被引用的次数,满足条件才会代码分割
      maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量
      maxInitialRequests: 30, // 入口js文件最大并行请求数量
      enforceSizeThreshold: 50000, // 超过50kb一定会单独打包(此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests)

      // 单独组,哪些模块要打包到一个组 , 以上的属性都会被单独的 cacheGroups 属性设置继承,但是优先级最低
      cacheGroups: {
        defaultVendors: {
          // 组名
          test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
          priority: -10, // 权重(越大越高)
          reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
        },
        default: {
          // 其他没有写的配置会使用上面的默认值
          minSize: 0, // 打包的最小文件体积,比如这里的的minSize就会 覆盖上面的 `minSize: 20000`

          minChunks: 2, // 这里的minChunks权重更大
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
  mode: "production",
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 单入口文件引入

const path = require("path");

module.exports = {
  // 单入口
  entry: "./src/main.js",
  output: {
    // ...其他配置
  },
  plugins: [
    // ...其他配置
  ],
  mode: "production",
  optimization: {
    // 代码分割配置
    splitChunks: {
      chunks: "all", // 对所有模块都进行分割
      // 以下是默认值
      minSize: 20000, // 分割代码最小的大小
      minRemainingSize: 0, // 类似于minSize,最后确保提取的文件大小不能为0
      minChunks: 1, // 至少被引用的次数,满足条件才会代码分割
      maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量
      maxInitialRequests: 30, // 入口js文件最大并行请求数量
      enforceSizeThreshold: 50000, // 超过50kb一定会单独打包(此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests)

      cacheGroups: { // 组,哪些模块要打包到一个组
        defaultVendors: { // 组名
          filename: "static/js/bundle-libs.js", // 指定打包到文件夹
          test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
          priority: -10, // 权重(越大越高)
          reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
        },
        default: { // 其他没有写的配置会使用上面的默认值
          filename: "static/js/bundle_[contenthash:6].js", // 指定打包到文件夹
          minChunks: 1, // 这里的minChunks权重更大, 引用一次就拆分
          priority: -20,
          reuseExistingChunk: true,
        },
      },
  },
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

输出文件目录如下

- dist
  - static
  - css
  - imgs
  - js
    - mainjs
    - bundle-libs.js
    - bundle_5392b7.js
  - media
  - index.html
1
2
3
4
5
6
7
8
9
10