Webpack 分块优化

Flying
2018-05-25 / 0 评论 / 209 阅读 / 正在检测是否收录...

从 Webpack v4 开始,移除了CommonsChunkPlugin,取而代之的是SplitChunks。最初,chunks(以及内部导入的模块)是通过内部 Webpack 图谱中的父子关系关联的。CommonsChunkPlugin 曾被用来避免他们之间的重复依赖,但是不可能再做进一步的优化。

默认拆分

SplitChunksPlugin 开箱即用,对于大部分用户来说非常友好。默认情况下,Webpack 将根据以下条件自动拆分 chunks:

  • 新的 chunk 可以被共享,或者模块来自于node_modules文件夹
  • 新的 chunk 体积大于 30kb(在进行 min+gz 之前的体积)
  • 当按需加载 chunks 时,并行请求的最大数量小于或等于 5
  • 当加载初始化页面时,并发请求的最大数量小于或等于 3

下面这个配置对象代表 SplitChunksPlugin 的默认行为。

optimization: {
  splitChunks: {
    chunks: 'async',
    minSize: 30000,
    maxSize: 0,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    name: true,
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,
        priority: -10
      },
      default: {
        minChunks: 2,
        priority: -20,
        reuseExistingChunk: true
      }
    }
  }
}

优化 Vendor

大家可以从 Github 上下载示例代码。为了方便演示,在实例中我们在 Vue CLI 工程中使用了 Echarts、Element 这些比较重和 UI 库。

如下图,默认情况下, Webpack 会帮我们自动生成 chunk-vendors.jschunk-vendors.cssapp.js 三个 bundle。这是因为 maxInitialRequests(入口点的最大并行请求数)默认为 3,它们的体积大于 30kb

split-before.png

如果改成 1vendor 这个 chunk 会合并到 app 中,如下图所示:

split-maxInitialRequests.png

如果我们用到的依赖库存很小,可以通过减小 maxAsyncRequests 值来合并所有的 chunk 从而减小 bundle 数量,一定程序上通过减少 Http 请求来提高 Web 性能。

所有常见模块和 vendor 合并为一个 chunk。这可能会导致更大的初始下载量并减慢页面加载速度。因此我们要找到减少文件大小和减少 Http 请求之间的平衡。随着现代浏览器逐渐支持 Http2,减少 Http 请求已经变得不那么重要了。因此,最佳实践是将大体积 bundle 分块成多个 bundle 来提高 Web 性能。在用 Webpack 打包时,如果 chunk 文件大于 244 KB,会给出可能影响 Web 性能的警告。如果是 3G 以上的网络,1M 以内也应该是可以的。显然本实例中 1.4M 太大了,因此我们要将 Echarts 和 Element 拆分成单独的 chunk。可以通过设置 cacheGroups 缓存组来实现。

splitChunks. 可以继承或覆盖来自 splitChunks.* 的任何选项。但是 testpriorityreuseExistingChunk 只能在缓存组级别上进行配置。将它们设置为 false 以禁用任何默认缓存组。

一个模块可以属于多个缓存组。优化将优先考虑具有更高 priority(优先级)的缓存组。默认组的优先级为负,以允许自定义组获得更高的优先级(自定义组的默认值为 0

maxAsyncRequests 设置成 5,并做如下 cacheGroups 配置:

cacheGroups: {
  vendor: {
    test: /[\\/]node_modules[\\/]/,
    name: 'vendor',
    priority: 10,
    chunks: 'initial', // only package third parties that are initially dependent
  },
  echarts: {
    name: 'echarts', // split elementUI into a single package
    priority: 30, // the weight needs to be larger or it will be packaged into libs or app
    test: /[\\/]node_modules[\\/]echarts/,
    chunks: 'all'
  },
  elementUI: {
    name: 'element-ui',
    priority: 30,
    test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
    chunks: 'all'
  }
}

分块效果如下图:

分块效果

可以看到 Echarts、Element 已经从先前的 chunk-vendors 中分离出来。

优化 Element

Echarts 本身就很大,即使按需引入组件也有 700 多 KB。Element 不是按需引入的,JavaScript 和 CSS 都很大,得进一步处理。

  1. 在 Vue CLI 工程中按需引入 Element 组件,可以使用 babel-plugin-component 插件。
npm i -D  babel-plugin-component
  1. babel.config.js 指定 styleLibraryName 路径为自定义主题相对于 .babelrc 的路径,注意要加 ~
{
  "plugins": [
    [
      "component",
      {
         libraryName: 'element-ui',
         styleLibraryName: '~node_modules/element-ui/packages/theme-chalk/src',
         ext: '.scss'
      }
    ]
  ]
}
  1. 安装 chalk 主题,可以从 npm 安装或者从 GitHub 拉取最新代码。
  • 从 npm
npm i element-theme-chalk -D
  • 从 GitHub
npm i https://github.com/ElementUI/theme-chalk -D
  1. 修改 var.scss 文件中的 $--color-primary 变量值为 #008080, 自定义为 teal 主题。
我们也可以直接新建 element-variables.scss 文件,用新的主题颜色变量覆盖默认主题颜色。但 Element 组件样式会被两次打包,一次是默认的样式,一次是覆盖的样式。所以需要用到 Element 主题工具进行深层次的纯净主题定制,最简单的简单的办法是按需引入样式并在 babel.config.js 中将 styleLibraryName 指向自定义样式目录。
  1. 再次 build 可以看到 Element 的文件大小正确了。如下图:

优化后

另外,要让该工程兼容 IE 的话,得设置 transpileDependencies:

transpileDependencies: [
  'vue-echarts',
  'resize-detector',
  'vue-baidu-map'
]

参考资料

6

评论 (0)

取消