学会这招,轻松优化webpack构建性能
webpack
webpack
本质上是一个静态资源打包工具,静态资源打包是指 webpack
会将文件及其通过 import
、require
等方式引入的各项资源,处理成一个资源依赖关系图,也称为 chunk
,这些资源包括 js
,css
,jpg
, 等等。
然后将这个 chunk
内的资源分别进行处理 ,如 less
编译成 css
,es6
编译成 es5
,等等。这个处理过程就是打包,最终将这些处理后的文件输出,输出的文件集合便称为 bundle
。
bundle 分析
学会优化 webpack
构建性能等优化,我们需要先学会如何分析 bundle
,通过对产出的分析,才能有针对性的对过程进行优化。
webpack
官方提供了一个非常好用的 bundle
可视化分析工具:webpack-bundle-analyzer
。这个工具会将 bundle
处理一个可视化页面,呈现出资源的依赖关系和体积大小等信息。
这个工具的使用方式也很简单,这需要在通过 npm install webpack-bundle-analyzer
或 yarn install webpack-bundle-analyzer
安装这个插件,然后在 webpack
配置文件的 plugins
配置项中加上这一行代码:
plugins: [
new BundleAnalyzerPlugin(),
]
复制代码
运行 webpack
打包后,会自动在 http://127.0.0.1:8888/
打开一个可视化页面:
优化小妙招
接下来我们将会结合对 bundle
的分析,进行一些优化操作。
在讲解如何优化之前,我们需要明确 chunk
和 bundle
的关系:chunk
是一组依赖关系的集合,它不单单指一个文件,可以包含一个或多个文件。而 bundle
是 webpack
打包的输出结果,它可以包含一个或多个 chunk
。而 webpack
打包执行时会以一个个 chunk
进行处理,前端在加载 webpack
打包的资源时,也往往是以一个 chunk
为单位加载的(无论它是一个或多个文件)。
splitChunks
从可视化界面中我们可以看到,经过 webpack
打包后我们得到一个 app.bundle.js
,这是个 bundle
中包含了我们项目的所有代码以及从 node_modules
中引入的依赖,而这个 bundle
中包含了项目内的所以依赖关系,因此这个 bundle
也是我们项目中唯一一个 chunk
。
那么我们在加载页面时,便是加载这一整个 chunk
,即需要在页面初始时加载全部的代码。
而 splitChunks
,是由 webpack
提供的插件,通过它能够允许我们自由的配置 chunk
的生成策略,当然也包括允许我们将一个巨大的 chunk
拆分成多个 chunk
。
在使用 splitChunks
之前我们先介绍一个重要的配置属性 cacheGroups
(如果需要,可以在官方文档 splitChunks 中了解更多):
cacheGroups
配置提取 chunk
的方案。里面每一项代表一个提取 chunk
的方案。下面是 cacheGroups
每项中特有的选项:
test
选项:用来匹配要提取的 chunk
的资源路径或名称,值是正则或函数。name
选项:生成的 chunk
名称。chunks
选项,决定要提取那些内容。priority
选项:方案的优先级,值越大表示提取 chunk
时优先采用此方案,默认值为0。enforce
选项:true
/false
。为true
时,chunk
的大小和数量限制。接下来我们便通过实际的配置操作,将 node_modules 的内容提取成单独的 chunk
,下面是我们的配置代码:
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
enforce: true,
priority: -3,
},
},
},
},
配置完成后,重新运行 webpack
,可以看到,node_modules
相关的依赖关系被提取成一个单独的 chunk
vendors.bundle.js
,最终我们得到了两个 chunk
:vendors.bundle.js
,app.bundle.js
那么通过这样的chunk
提取,有什么好处呢?
node_modules
下往往是在项目中不会的变化的第三方依赖,我们将这些固定不变的提取成单独的chunk
处理,webpack
便可以将这个chunk
进行一定的缓存策略,而不需要每次都做过多的处理,减少了性能消耗。- 网页加载资源时不需要一次性加载太多的资源,可以通过不同
chunk
分批次加载,从而减少首屏加载的时间。
除了这里介绍的对 node_modules
的处理外,在实际的项目中也可以根据需要对更多的资源采取这样的提取chunk
策略。
、
externals + CDN
通过对 bundle
的分析,我们不难发现:在我们输出的 bundle
中 react.development.js
、 react-dom.development.js
以及 react-router.js
这三个文件特别的显眼。这表示这几个文件的体积在我们总的输出文件中占的比例特别大,那么有什么方法可以解决这些碍眼的家伙呢?
当然有! 下面将要介绍的 external
+ CDN
策略,便可以很好的帮助我们做到这点。
external
是 webpack
的一个重要的配置项,顾名思义,它可以帮助我们将某些资源在 webpack
打包时将其剔除,不参与到资源的打包中。
external
是一个有多项 key-value
组成的对象,它的的每一项属性表示不需要经过 webpack
打包的资源,key
表示的是我们需要排除在外的依赖名称,value
则告诉 webpack
,需要从 window
对象的哪个属性获取到这些被排除在外的依赖。
下面的代码就是将react
、react-dom
、react-router-dom
三个依赖不进行 webpack
打包的配置,它告诉 webpack
,不将 react
、react-dom
和react-router-dom
打包进最终的输出中,需要用到这些依赖时从 window
对象下的 React
、ReactDOM
和 ReactRouterDOM
属性获取。
externals: {
react: 'React',
'react-dom': 'ReactDOM',
'react-router-dom': 'ReactRouterDOM',
},
复制代码
那么这些被剔除的依赖,为什么可以从 window
对象获取到呢?答案就是 CDN
!
我们将这些剔除的依赖,通过 script
标签引入对应的 CDN
资源( CDN
即 内容分发网络,我们可以将这些静态资源存储到 CDN
网络中,以便更快的获取资源)。
这需要我们将引入这些资源的script
标签加在入口 HTML
文件中,这些加载进来的js文件,会将资源挂载在对应的 window
属性 React
、ReactDOM
和 ReactRouterDOM
上。
<script src="https://cdn.staticfile.org/react/0.0.0-0c756fb-f7f79fd/cjs/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/0.0.0-0c756fb-f7f79fd/cjs/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/react-router-dom/0.0.0-experimental-ffd8c7d0/react-router-dom.development.js"></script>
接下来看下通过 external
+ CDN
策略处理后,我们最终输出的bundle:
react.development.js
、 react-dom.development.js
以及 react-router.js
这三个文件消失了!
作者:Promise
链接:https://juejin.cn/post/7034181462106570759