当我们去打包一个大型项目的时候,有的时候完成打包需要非常多的时间,遇到这种问题的时候,我们就要考虑用一些方法来提升
webpack
的打包效率
如何提升webpack打包速度呢?
一. 跟上技术的迭代,尽可能使用新版本的webpack
,node
,npm
, yarn
如果我们想提升
webpack
的打包速度,我们可以升级webpack
的版本,或者升级我们的node
版本,npm
包管理器的版本,或者yarn
管理工具的版本;为什么升级这些工具,可以提高
webpack
的打包速度呢?主要是因为
webpack
在做版本更新的时候,内部肯定会做一些优化,所以如果我们更新了webpack的版本,webpack
的打包速度肯定会有所提升;而webpack又是建立在
Node
这样一个JS运行环境之上的,如果node进行了升级,那就意味着node的运行效率会有提升。webpack运行在node之上,很显然它的速度也会有所提升;当我们安装了新版本的
npm ,yarn
这类的管理工具之后,模块之间如果遇到一些相互引用的情况下,新的包管理工具,可以更快的帮我们分析一些包依赖,或者做一些包引入,这也能间接提升webpack
的打包速度
二. 在尽可能少的模块上应用loader
来提升webpack的打包速度
- 比如,我们在打包
js
结尾的文件时,配置的exclude
,如果我们不配置exclude: /node_modules/
,然后我们在代码里引入了 node_modules里的第三方模块的js文件的时候,当我们运行打包命令的时候,这个第三方模块的JS文件会被打包编译,但其实这是没有必要的,因为一般情况下,第三方模块已经是被打包编译过了的,所以额外进行一次编译的话,就会降低打包的速度
- 当然了,出了使用
exclude
,我们还可以用include
来达到类似的效果;exclude: /node_modules/
的意思是,不对node_modules
目录下的JS文件进行编译;而include:path.resolve(__dirname,'../src')
这样配置的意思是,当我遇到一个JS文件或者JS模块的时候,只有它在src
源代码文件夹下,才会去使用babel-loader
来帮我们做语法的转换;如果引入了其他目录下的JS文件,就不会走这个loader了 - 合理的使用
include
或者exclude
可以降低loader
的使用频率,提高打包速度 - 那么图片文件是否需要配置
include
或者exclude
呢,实际上是不需要的,因为无论引入哪里的图片,实际上都需要url-loader
来帮我们打包到dist
目录下
三. Plugin
尽可能精简,并确保可靠
- 打包生成线上代码的时候,为了让线上代码尽可能的小,让用户加载速度尽可能的快,所以我们需要对代码做压缩,之前的配置中,我们在线上环境的配置文件中使用了
optimize-css-assets-webpack-plugin
这样一个插件,对我们的CSS文件进行了压缩,可是如果我们在开发环境下,是没有必要对代码进行压缩的。因为开- 发环境下,是我自己用,不需要考虑加载速度的问题。 - 由此,我们可以知道,如果不对代码进行压缩,那就解决了压缩代码的时间,我们的打包速度就会更快一些,所以在做
webpack
打包的时候, 没有必要使用的插件就尽量不用,它会降低我们的打包速度。 - 同时,我们在使用插件的时候,尽量使用webpack官网方式推荐使用的插件,因为这些插件它的性能经过了官网的测试,是比较快的;而一些第三方公司或个人的插件,也会可以帮我们解决一些问题,但是它的性能得不到保证,使用它们的话,很有可能会降低我们的打包速度,所以我们使用插件的时候,最好是社区里验证过的性能比较好的插件
四. resolve参数合理配置
- 通过合理的优化 resolve配置项,我们可以让
webpack
打包速度更快;
当我们在代码里引入一个模块的时候,而这个模块的后缀,并不是我们常见的js
结尾,而是jsx
的文件。然后我们还像平时那样引入,不带后缀;会报错,说找不到这个模块。
这种情况,我们可以配置一个resolve下的extensions
方便我们引入(不写后缀的引入)这种逻辑性的文件,当然如果在里边配置一些像
.jpg .png .css
之类的也不是不可以,只是会对性能有所影响,建议还是只配置js或者jsx这类逻辑性的文件后缀
- 如果我们引入child这个组件的时候,写的更简洁一点,比如这样
import Child from './child/';
我们的想法是,自动帮我们识别出child
目录里的child
组件;如果直接import Child from './child/'
这样写,可以吗? 很明显不行;如果我们把child.jsx
改为index.jsx
就好用了,这是因为,当我们去引入一个目录下东西的时候,默认会帮我们引入名字叫做index
这一类的文件
- 我们可以通过
resolve
配置来实现这个想法;当我们配置了mainFiles
之后,当我们再去引入一个目录下的东西的时候,会先帮我们找目录下index名字的文件,如果不存在,就找名字叫做child的文件
我们在每次修改了webpack配置后重启服务器,webpack都会帮我们打开一个新窗口展示,如果不想每次都开新窗口,可以把
devServer 里的 open:true
关闭掉就可以了
- 虽然
mainFiles
配置解决了我们的问题,但是它也会带来性能上的问题,因为需要在路径下不停的去找我们配置的名字的文件是否匹配,所以一般情况下我们不会配置这个参数 - 如果我们想这样引入
import Child from 'lee'
;正常情况下,是会报错的,因为我们lee不是一个第三方模块,而且我们也没有安装这样的模块,这样写法跟我们引入第三方模块是一样的,比如我们引入react;但是我们可以配置alias
参数
- 这样配置的意思是,当我们在引入的时候,看到的是
lee
这个路径的时候,实际上它是path.resolve(__dirname, '../src/child')
这个路径的别名
- 所以,通过这个例子我们应该明白,
alias
参数是我们可以经常用到的一个配置;它的使用场景如下,假如有这样一个路径src/a/b/c/child.js,正常情况下我们要引入child.js需要这样写 , import Child from './src/a/b/c/child'这样非常麻烦,通过刚才的alias我们就可以这样配置child:path.resolve(__dirname,'../src/a/b/c/child')
五. 使用DLLPlugin提升打包速度
-
我们在引入第三方模块的时候,每次重新打包的时候,webpack都要重新分析这些第三方模块,然后把它们打包到我们的项目之中。但是我们知道,一般情况下,这些第三方模块的代码是轻易不会变的
那我们怎么针对这个问题进行优化呢,我们可以把这些第三方模块单独打包生成一个文件,只在第一次打包的时候去分析这个文件里的代码,之后再打包的时候,直接用这个分析过的结果就可以了,这是一个最理想的优化方式。具体操作步骤如下
-
再创建一个配置文件,姑且命名为
webpack.dll.js
-
然后再在
package.json
里再配置一个命令
-
然后我们运行打包命令
npm run build:dll
,因为我们没有配置mode
属性,所以虽然打包成功了,还是给了我们警告
-
配置之后,我们再打包警告没有了
-
这个时候,我们已经把引入的三个模块,打包到了
dll
文件夹下的vendors.dll.js
文件里
-
但是,虽然我们把三个模块打包到了一个单独的文件里,我们还没有使用这个文件,我们要怎么使用这个文件,才能提升
webpack
的打包效率呢?我们可以把这个文件里的内容通过一个全局变量暴露出来,具体配置如下
-
现在我们有了这个全局变量,但是我们的index.html里,并没有引入
vendors.dll.js
文件,所以我们在控制台去打印vendors
这个全局变量,是找不到的
-
现在,只是引入了这3个JS文件,并没有引入
vendors.dll.js
文件
-
那我们怎么引入呢? 我们需要安装一个插件
npm install add-asset-html-webpack-plugin --save
,这个插件的作用就是往html-webpack-plugin
上再去增加一些静态的资源,安装之后,我们要先引入
-
引入之后,我们去调用
-
配置好以后,我们重新启动一下服务器
npm run dev
,我们可以看到,页面已经引入了我们生成的vendors.dll.js
文件,页面上控制台也可以访问到vendors
这个全局变量了
-
到了这一步,我们实现了 第三方模块打包一次,但是我们并没有实现第一次打包,后边就用打包的成果,因为我们现在无论是在
webpack.common.js
里,还是webpack.dev.js
和webpack.prod.js
里,我们打包的过程中,都是直接引入的第三方模块,而不是用我们生成的全局变量vendors
,要知道现在vendors
已经包含了我们要使用的三个模块,当我们运行src下的index.js的时候,里边的模块引入还是使用的node_modules
下的第三方模块,
-
所以我们引入第三方模块的时候,要使用
vendors.dll.js
文件引入,这个时候就需要用到DLLPlugin
;这个插件的时候,需要引入webpack
配置好以后,我们再运行打包命令
npm run build:dll
,可以看到在dll文件夹下,多了一个叫做vendors.manifest.json
的映射文件有了这个映射文件,在
webpack
做打包的时候,就可以结合生成的vendors.dll.js
文件和这个映射文件,对我们源代码进行分析,一旦分析出来,使用的模块内容,是在vendors.dll.js
里,那么它就会直接去使用vendors.dll.js
里的内容了,就不会去node_modules
引入第三方模块了-
那怎么结合全局变量和刚才生成的
vendors.manifest.json
文件,进行我们的webpack的配置呢?需要再借助一个插件DllReferencePlugin
-
经过上边的配置,我们已经实现了我们想要的功能,我们再去打包会发现,打包时间是
554ms
-
假如我们现在把插件注释掉,这样每次打包的时候,就会直接到node_modules引入模块,这个时候打包的时间会大于刚才,为
718ms
-
现在我们把
reat,react-dom,lodash
这三个第三方模块打包到一个文件了,假如我们想拆分一下,比如把reat,react-dom打包到一起,lodash单独放到vendors.dll.js
文件里;那么我们需要这样去配置
-
然后还需要通过
AddAssetHtmlWebpackPlugin
把react.dll.js文件也放到html-webpack-plugin
生成的index.html
,这样全局变量里才会多出一个react
; 也需要通过DllReferencePlugin
插件,去分析react.manifest.json
映射文件
在引入第三放模块比较少的情况下,我们可以像上边那样去配置,可以如果我们引入很多的第三方模块,而且我们希望把它们都单独打包到一个文件里,拿我们岂不是要配置很多的
AddAssetHtmlWebpackPlugin
和DllReferencePlugin
了,这样太麻烦了。如果我们可以去遍历dll
文件夹下有多少的文件,然后把它们都自动添加到plugins
里就好了-
我们可以这样配置,首先我们把通用的插件拿出来放到一个数组里
-
然后我们引入node的核心模块
fs(在 NodeJS 中,所有与文件操作都是通过 fs 核心模块来实现的,包括文件目录的创建、删除、查询以及文件的读取和写入)
,然后通过fs调用readdirSync
方法去读dll
文件夹里的内容
然后打印一下读取的内容,我们先把plugins配置里的内容注释掉先,然后运行npm run build
-
我们成功读取了
dll
文件里的所有文件,然后我们循环files
这个数组,把数组里的值取出根据情况放入到plugins
-
然后把生成的plugins数组,配置到plugins配置项就可以了
-
然后我们在运行
npm run dev
,发现是正常运行的
-
我们测试下,如果我们再加一个
jquery
的话(jquery要先安装),会正常运行吗?
-
我们再运行一下
npm run build:dll
-
然后再运行
npm run dev
,发现成功了
所以,经过上边配置,无论我们引入了多少第三方模块,要把这些模块拆分成几个文件打包,都可以通过
entry
配置去修改就可以了,剩下的工作webpack会按照我们配置自动帮我们完成
六.控制包文件的大小
- 我们在做项目打包的时候,我们应该让打包生成文件尽可能的小,我们在写代码的时候,经常会在页面里引入一些没用的模块,但是我们并没用使用这些模块,而我们也没配置
Tree Shaking
,这就导致代码里有很多冗余的代码,这些冗余代码就会拖累webpack
的打包速度。 - 在我们的源代码里,一些用不到的包,要通过
Tree Shaking
去除掉,或者直接不引入它,这样去控制webpack
打包生成文件的大小,从而提升打包的速度 - 我们还可以通过
splitchunksplugin
这样的插件对代码进行拆分,把一个大的文件拆分成小的文件,进行webpack的打包处理,这样也可以提升webpack的打包速度
七. thread-loader,parallel-webpack,happypack 多进程进行打包
- webpack默认是同构NodeJS来运行的,所以是一个单进程的打包过程,有时候,我们可以借助node里的多进程来帮助我们提升
webpack
的打包速度
八. 合理使用 sourceMap
- 打包的时候,生成的
sourceMap
越详细,打包的速度就越慢,所以我们要思考,不同环境打包的时候,什么样的sourceMap
是最合适的,既要保证我们及时发现代码里的问题。又可以尽可能的提升我们的打包速度
九. 结合stats 分析打包结果
也就是我们可以通过命令,来生成一个描述这次打包情况的Stats.json文件,通过借助一些线上或者本地的打包分析工具,我们可以分析这些打包过程中的一些情况,比如哪个模块打包时间比较长,哪个模块打包时间比较短,从而分析出哪些部分打包耗时比较多,从而做针对性的优化以提升打包速度
十.开发环境内存编译
- 我们在开发环境的时候使用的是
webpack dev server
,它在做打包的时候,不会生成dist目录,他会把编译生成的内容放到内存里,内存的读取,肯定要比硬盘的读取快的多,所以通过这种手段,也可以让我们在开发的过程中,webpack的性能得到很大的提升
十一. 开发环境,无用插件剔除
- 比如项目调试的时候,我们并不需要对代码进行压缩,我们就应该把mode设置成
development
而不是production
,如果开发环境下就压缩的话是没用意义的,这会降低webpack
的打包速度