分类
Javascript

Webpack v4分包优化

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

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

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

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

module.exports = {
  //...
  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
        }
      }
    }
  }
};

大家可以从Github上下载示例代码。下面实例我们d Vue cli工程中使用了Echarts、Element-ui这些比较重和UI库。

如上图,默认webpack下自动生成了chunk-vendors.js、chunk-vendors.css和app.js三个bundle。这是因为maxInitialRequests(入口点的最大并行请求数)默认为3,它们的体积大于30kb。如果改成1,vendor这个chunk会合并到app中。如下图:

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

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

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-ui已经从先前的chunk-vendors中分离出来。

Echarts本身就很大,即使使用按需引入组件也有700多KB。Element-ui也是按需引入的,但js和css都很大,得进一步处理。

splitChunks.可以继承和/或覆盖来自splitChunks.* 的任何选项。但是testpriorityreuseExistingChunk只能在缓存组级别上进行配置。将它们设置为 false以禁用任何默认缓存组。
一个模块可以属于多个缓存组。优化将优先考虑具有更高priority(优先级)的缓存组。默认组的优先级为负,以允许自定义组获得更高的优先级(自定义组的默认值为 0)

1、在Vue cli工程中按需引入Element-ui组件,可以使用babel-plugin-component插件。

npm i -D  babel-plugin-component

2、在babel.config.js指定styleLibraryName路径为自定义主题相对于.babelrc的路径,注意要加~。

{
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "~theme"
      }
    ]
  ]
}

3、安装chalk主题,可以从npm安装或者从GitHub拉取最新代码。

# 从 npm
npm i element-theme-chalk -D

# 从 GitHub
npm i https://github.com/ElementUI/theme-chalk -D

4、修改var.scss文件中的$--color-primary变量值为#008080自定义主题。

也只可以直接新建element-variables.scss文件,用新的主题颜色变量覆盖默认主题颜色。但Element UI组件样式会被两次打包,一次是默认的样式,一次是覆盖的样式。所以需要用到Element ui主题工具进行深层次的纯净主题定制,最简单的简单的办法是按需引入样式并在babel.config.js中将styleLibraryName指向自定义样式目录。

5、再次build可以看到Element UI的文件大小正确了。如下图:

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

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

参考资料:https://v4.webpack.js.org/plugins/split-chunks-plugin/