【webpack 从入门到使用】代码分割

141 阅读2分钟

说明

  1. 这套笔记是对于 webpack 5.x 进行阐述的。(webpack <= 4 用法稍有不同)

概念

代码分割 (code spilting), 是指将一个体积比较大的入口脚本文件拆分成合适大小体积的若干个脚本文件。

代码分割的方式:

一、全局拆分库

将使用到的库或者框架拆分成 lib 导入

|- externals
    |- lodash.js
|- src
    |- index.js
|- webpack.config.js

  • webpack.config.js
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const config = {
  mode: 'production',
  
  entry: {
    index: resolve(__dirname, 'src/index.js'),
    lodash: resolve(__dirname, 'externals/lodash.js'),
  },

  output: {
    path: resolve(__dirname, 'dist'),
    filename: 'js/[name]-[contenthash].js',
    publicPath: '/',
    clean: true
  },

  plugins: {
    new HtmlWebpackPlugin({
      template: resolve(__dirname, 'public/index.html'),
      favicon: resolve(__dirname, 'public/favicon.ico'),
      title: 'Index',
      chunks: ['lodash', 'index'],
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  }
};

module.exports = config;
  • externals/lodash.js
import _ from 'lodash';

window._ = _;
  • src/index.js
const arr = ['张三', '李四', '王五', '赵六', '田七'];

const arrStr = _.join(arr, '-');

console.log(arrStr);

二、动态导入(依赖的懒加载)

const oBtn = document.getElementsByTagName('button')[0];
const oDiv = document.getElementsByTagName('div')[0];

const arr = ['zhangsan', 'lisi', 'wangwu'];

oBtn.addEventListener('click', handleBtnClick, false);

function getLodash() {
  return new Promise((resolve) => {
    import('lodash').then(({ default: _ }) => {
      resolve(_);
    })
  });
}

async function handleBtnClick() {
  const _ = await getLodash();
  const arrStr = _.join(arr, ' & ');
  oDiv.textContent = arrStr;
}

三、optimization.splitChunks

splitChunks 是 webpack 提出的可以整体配置懒加载的选项,webpack 会根据 splitChunks 里面的选项要求进行打包。

举个例子:

module.exports = {
  //...
  optimization: { // 打包优化项 (usedExports, splitChunks, minimizer, ...)
    splitChunks: {
      chunks: 'async', // 需要分割的 chunk 类型 (全部, 异步, 同步)
      minSize: 20000, // 代码快的大小至少为 xxx kb, 才需要进行代码分割
      minRemainingSize: 0, // 最小的 chunk 剩余大小
      minChunks: 1, // chunk 至少要被引入多少次才会进行代码分割
      maxAsyncRequests: 30, // 最大的异步 chunk 请求数
      maxInitialRequests: 30, // 最大的同步 chunk 请求数
      enforceSizeThreshold: 50000, // 线程强制运行的大小
      cacheGroups: { // 缓存组 (里面使用一组组对象对打包的 chunks 进行规则列举)
        defaultVendors: { // 对 node_modules 中的依赖进行处理
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: { // 对 src 中的依赖进行处理
          minChunks: 2, // 最小使用次数(超过这个次数才按照此规则进行代码分割)
          priority: -20, // 打包优先级
          reuseExistingChunk: true, // 使用沿用已有的 chunk
        },
      },
    },
  },
};

splitChunks 常用的配置项

配置名称配置类型配置描述
splitChunks.automaticNameDelimiterstring生成 chunk 名称的分隔符
splitChunks.chunksstring指定哪些 chunk 进行优化 ('initial' | 'async' | 'all')
splitChunks.minSizenumber生成 chunk 的最小体积(以 bytes 为单位,默认为 20000)。
splitChunks.maxSizenumber生成的 chunk 的最小体积 (优先级低于 minSize)
splitChunks.maxAsyncRequestsnumber最大的并行请求异步 chunk 的数量 (默认为 30)
splitChunks.maxInitialRequestsnumber最大的并行请求同步 chunk 的数量 (默认为 30)
splitChunks.minChunksnumberchunk 最少使用多少次才会被进行代码分割
splitChunks.enforceSizeThresholdnumber强制执行拆分的体积阈值
splitChunks.layernumber按模块层将模块分配给缓存组。
splitChunks.namestring拆分 chunk 的名称。
splitChunks.usedExportsboolean是否开启 tree-shaking ?
splitChunks.cacheGroupsobject代码分割的规则缓存组