基本原理

  1. 不同与手动分包,自动分包是从 实际的角度 出发,从一个更加 宏观的角度 来控制分包,而一般不对具体哪个包要分出去进行控制,因此使用自动分包,不仅非常方便,而且更加贴合实际的开发需要;

  2. 要控制自动分包,关键是要配置一个合理的 分包策略 ,有了分包策略之后,不需要额外安装任何插件,webpack 会自动的按照策略进行分包;

  3. 从分包流程中至少可以看出以下几点:

    • 分包策略至关重要,它决定了如何分包;
    • 分包时 webpack 开启了一个 新的chunk ,对分离的模块进行打包;
    • 打包结果中,公共的部分被提取出来形成了一个单独的文件,它是新 chunk 的产物;

实际上 webpack 在内部是使用 SplitChunksPlugin 进行分包的过去有一个库 CommonsChunkPlugin 也可以实现分包,不过由于该库某些地方并不完善,到了 webpack4 之后,已被 SplitChunksPlugin 取代

自动分包的流程

自动分包的原理其实并不复杂,主要经过以下步骤:

  1. 检查每个 chunk 编译的结果;
  2. 根据分包策略,找到那些满足策略的模块;
  3. 根据分包策略,生成新的 chunk 打包这些模块(代码有所变化);
  4. 把打包出去的模块从原始包中移除,并修正原始包代码;

在代码层面,有以下变动:

  1. 分包的代码中,加入一个数组类型的全局变量,将公共模块的代码放入里面;

  2. 原始包的代码中,使用全局变量数组中的公共代码;

分包策略的基本配置

  1. webpack 提供了 optimization 配置项,用于配置一些优化信息,其中 splitChunks 是分包策略的配置;

    module.exports = {
      optimization: {
        splitChunks: {
          // 分包策略
        }
      }
    }
    
  2. 事实上,分包策略有其默认的配置,只需要轻微的改动,即可应对大部分分包场景;

chunks

  1. 该配置项用于配置需要应用分包策略的 chunk ,分包是从已有的 chunk 中分离出新的 chunk ,那么哪些 chunk 需要分离呢

  2. chunks 有三个取值,分别是:

    • all:对于所有的 chunk 都要应用分包策略;
    • async:「默认」仅针对异步 chunk 应用分包策略;
    • initial:仅针对普通 chunk 应用分包策略;
  3. 所以,你只需要配置 chunks: all 即可;

maxSize

  1. 该配置可以控制包的最大字节数,如果某个包(包括分出来的包)超过了该值,则 webpack 会尽可能的将其分离成多个包;

  2. 但是不要忽略的是,分包的 基础单位是模块 ,如果一个完整的模块超过了该体积,它是无法做到再切割的,因此,尽管使用了这个配置,完全有可能某个包还是会超过这个体积;

  3. 另外,该配置看上去很美妙,实际意义其实不大,因为分包的目的是 提取大量的公共代码 ,从而减少总体积和充分利用浏览器缓存,虽然该配置可以把一些包进行再切分,但是实际的总体积和传输量并没有发生变化;

如果要进一步减少公共模块的体积,只能是 压缩tree shaking

分包策略的其他配置

如果不想使用其他配置的默认值,可以手动进行配置:

  • automaticNameDelimiter:新 chunk 名称的分隔符,默认值 ~
  • minChunks:一个模块被多少个 chunk 使用时,才会进行分包,默认值 1
  • minSize:当分包达到多少字节后才允许被真正的拆分,默认值 30000

缓存组

  1. 之前配置的分包策略是全局的,而实际上,分包策略是基于缓存组的;每个缓存组提供一套独有的策略,webpack 按照缓存组的优先级依次处理每个缓存组,被缓存组处理过的分包不需要再次分包;

  2. 默认情况下 webpack 提供了两个缓存组:

    module.exports = {
      optimization:{
        splitChunks: {
          //全局配置
          cacheGroups: {
            // 属性名是缓存组名称,会影响到分包的 chunk 名
            // 属性值是缓存组的配置,缓存组继承所有的全局配置,也有自己特殊的配置
            vendors: { 
              test: /[\\/]node_modules[\\/]/, // 当匹配到相应模块时,将这些模块进行单独打包
              priority: -10 // 缓存组优先级,优先级越高,该策略越先进行处理,默认值为 0
            },
            default: {
              minChunks: 2,  // 覆盖全局配置,将最小 chunk 引用数改为 2
              priority: -20, // 优先级
              reuseExistingChunk: true // 重用已经被分离出去的 chunk,无需再次分之前分好的包
            }
          }
        }
      }
    }
    
  3. 很多时候,缓存组对于我们来说没什么意义,因为默认的缓存组就已经够用了,但是我们同样可以利用缓存组来完成一些事情,比如对公共样式的抽离;

    module.exports = {
      optimization: {
        splitChunks: {
          chunks: "all",
          cacheGroups: {
            styles: {
              test: /\.css$/, // 匹配样式模块
              minSize: 0, // 覆盖默认的最小尺寸,这里仅仅是作为测试
              minChunks: 2 // 覆盖默认的最小 chunk 引用数
            }
          }
        }
      },
      module: {
        rules: [{ test: /\.css$/, use: [ MiniCssExtractPlugin.loader, "css-loader" ] }]
      },
      plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
          template: "./public/index.html",
          chunks: ["index"]
        }),
        new MiniCssExtractPlugin({
          filename: "[name].[hash:5].css",
          // chunkFilename 是配置来自于分割 chunk 的文件名
          chunkFilename: "common.[hash:5].css" 
        })
      ]
    }
    

配合多页应用

  1. 虽然现在单页应用是主流,但免不了还是会遇到多页应用,由于在多页应用中需要为每个 html 页面指定需要的 chunk ,这就造成了问题;

    new HtmlWebpackPlugin({
      template: "./public/index.html",
      chunks: ["index~other", "vendors~index~other", "index"]
    })
    
  2. 必须手动的指定被分离出去的 chunk 名称,这不是一种好办法,幸好 html-webpack-plugin 的新版本中解决了这一问题

    npm i -D html-webpack-plugin@next
    
  3. 做出以下配置即可,它会自动的找到被 index 分离出去的 chunk 并完成引用

    new HtmlWebpackPlugin({
      template: "./public/index.html",
      chunks: ["index"]
    })
    
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于产品、设计、开发的问题和看法,还有技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程

了解更多

目录

  1. 1. 基本原理
  2. 2. 自动分包的流程
  3. 3. 分包策略的基本配置
    1. 3.1. chunks
    2. 3.2. maxSize
  4. 4. 分包策略的其他配置
  5. 5. 缓存组
  6. 6. 配合多页应用