Webpack打包优化

Posted by WWJ Blog on December 5, 2019

webpack打包优化

初始状态

Alt text

打包时间一共花了90多s(每台电脑的运行时间会有不同,CPU好一点的运行速度就会快一点,但都不会快到哪里去),大概需要一分多钟才项目才可以启动成功,在这之前就只能等……

上一篇从模块学习到深入webpack源码说到,webpack打包的过程其实就是一堆plugin和loader文件,在编译的过程中,依赖类似于发布订阅的模式按照流程执行这一系列plugin和loader,那么优化自然也是从这两方面入手。

首先可以借助一个工具插件speed-measure-webpack-plugin(简称SMP)。SMP可以分析除Webpack整个打包过程钟在各个loader和plugin上耗费的时间,这将有助于找出构建过程中的性能瓶颈。

安装命令如下

yarn add speed-measure-webpack-plugin -D

SMP的使用方法也很简单,只要用它的wrap将webpack的配置对象包裹即可


const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();
const baseConfig = {
	...
}

module.exports = smp.wrap(baseConfig);

添加speed-measure-webpack-plugin之后我们再来看一下控制台的输出 Alt text

可以看到,每个loader所花费的时间都非常清楚,不过我们也发现,最后的打包时间较没有引入speed-measure-webpack-plugin之前是更长的,这个不用在意,因为优化完成之后就不需要这个插件了,到时候卸载就可以

优化loader

1) 添加exclude/include

在配置loader的时候,如果没有exclude/include这两个属性,那么项目中引入的第三方库也会被打包,通常我的做法就是使用exclude过滤到node_modules目录。那么我们来检查一下配置的loader是否都加上了exclude属性 Alt text

居然只有ts-loader才使用了exclude属性,前面通过speed-measure-webpack-plugin分析得出,耗时长的loader为less-loader、css-loader、babel-loader、url-loader,给这些loader加上exclude属性再来看 Alt text

嗯,快了6s,继续加油

2)添加缓存 有些loader会有一个cacheDirectory配置项,用来在编译代码后同时保存一份缓存,在执行下一次编译前会检查源码文件是否有变动,如果没有就直接采用缓存,也就是上次编译的结果。这样相当于实际编译的只有变化了的文件,整体在速度上会有一定的提升。

添加cacheDirectory配置项

const baseConfigv = {
	...
	module: {
		rules: [
			{
				test: /\.tsx?$/,
		        use: [
		          {
		            loader: 'ts-loader',
		            options: {
		              cacheDirectory: true,
		            }
		          }
		        ],
		        exclude:/node_modules/
			},
			...
		]	
	}
}

放在options对象里,其他loader也是同样的处理,添加缓存属性之后再来看一下打包速度 Alt text

直接快了30s……振奋人心……^_^

3) DllPlugin和Code Splitting DllPlugin和Code Splitting都是用来提取公共模块的,但本质上是有一些区别的。Code Splitting的思路是设置一些特定的规则并在打包的过程中根据这些规则提取公共模块。DLLPlugin则是将vendor完全拆出来,有自己的一套Webpack配置并独立打包,在实际工程构建时就不再对它进行任何处理,直接取用即可。

因为项目中已经使用了DllPlugin优化方法,笔者在这里就不讨论了。

4)HappyPack 在打包过程中有一项非常耗时的工作,就是使用loader将各种资源进行转译处理。最常见的包括使用babel-loader转译ES6+语法和ts-loader转译TypeScript。

上一篇从模块学习到深入webpack源码说到,webpack打包的原理其实就是从配置中获取打包入口,对入口文件进行处理,对处理后的模块进行依赖查找,对找到后的模块继续处理,并递归进行依赖查找…..直到没有新的模块依赖。

不难看出,webpack需要一步步地获取更深层次的资源,然后逐个处理。这里的问题在于webpack是单线程的。假设一个模块依赖其他几个模块,webpack需要将这些被依赖的模块逐个进行处理,虽然这个被依赖的模块之间没有任何的依赖关系,却必须串行执行。

Happypack的原理就是它可以开启多个线程,以此来提升打包速度。

  • happypack适用于转译任务比较重的模块,比如babel-loader、ts-loader
  • happypack对less-loder、sass-loader效果一般
  • happypack不能用于postcss-loader(这个笔者也没搞明白为什么)

配置happypack

const HappyPack = require('happypack');
const baseConfigv = {
	...
	module: {
		rules: [
			{
				test: /\.tsx?$/,
		        use: [
		          {
		            loader: 'happypack/loader?id=ts-loader',
		          }
		        ],
		        exclude:/node_modules/
			},
			...
		]	
	},
	plugins: [
		...
		new HappyPack({
			id: 'ts-loader',
			loaders: [
				{
					loader: 'ts-loader',
					options: {
			            cacheDirectory: true,
				    }
				}
			]
		})
	]
}

其他loader的配置也是同样的,happy/loader?id=loaderName 注:在使用HappyPack优化多个loader时,需要为每个loader配置一个id(id不可重复),否则HappyPack无法直到rules与plugin如何一一对应的。

再来看下打包速度 Alt text

直接从一分钟减到了17s,这比最开始的100s快了将近6倍,虽然还是没有达到理想的状态,但优化后的速度是显而易见的

总结

  • 打包时间从100-105s降低到17-20s,降低80-88s左右,提升速度大约80%左右
  • 二次打包速度从40s-50s降低到7s-10s,降低了30-43s左右,提升速度大约75%左右