// Generated using webpack-cli https://github.com/webpack/webpack-cli
...
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const config = {
...
plugins: [
new HtmlWebpackPlugin({
template: "index.html",
}),
new MiniCssExtractPlugin(),
new CompressionWebpackPlugin(),
// Add your plugins here
// Learn more about plugins from https://webpack.js.org/configuration/plugins/
],
...
};
当你运行命令后,你就会发现打包后的文件有gzip的文件了
但是我们发现html以及map.js.map文件也被gizp压缩了,这是没有必要的
官方提供了一个exclude,可以排除某些文件不被gizp压缩
{
plugins: [
new HtmlWebpackPlugin({
template: "index.html",
}),
new MiniCssExtractPlugin(),
new CompressionWebpackPlugin({
exclude: /.(html|map)$/i // 排除html,map文件
})
// Add your plugins here
// Learn more about plugins from https://webpack.js.org/configuration/plugins/
],
}
webpack
如何打包资源优化你有了解吗?或者一个经常被问的面试题,首屏加载如何优化,其实无非就是从http
请求、文件资源
、图片加载
、路由懒加载
、预请求
,缓存
这些方向来优化,通常在使用脚手架中,成熟的脚手架已经给你做了最大的优化,比如压缩资源,代码的tree shaking
等。本文是笔者根据以往经验以及阅读官方文档总结的一篇关于
webpack打包
方面的长文笔记,希望在项目中有所帮助。正文开始...
在阅读之前,本文将从以下几个点去探讨 webpack 的打包优化
1、
webpack
如何做treeShaking
2、
webpack
的 gizp 压缩3、
css
如何做treeShaking
,4、入口依赖文件
拆包
5、
图片资源
加载优化treeShaking
在官网中有提到treeShaking,从名字上中文解释就是摇树,就是利用
esModule
的特性,删除上下文未引用的代码。因为 webpack 可以根据esModule
做静态分析,本身来说它是打包编译前输出,所以webpack
在编译esModule
的代码时就可以做上下文未引用的删除操作。那么如何做
treeshaking
?我们来分析下快速初始化一个 webpack 项目
在之前我们都是手动配置搭建
webpack
项目,webpack
官方提供了cli
快速构建基本模版,无需像之前一样手动配置entry
、plugins
、loader
等首先安装
npm i webpack webpack-cli
,命令行执行`一系列初始化操作后,就生成以下代码了 默认的
webpack.config.js
运行命令
npm run serve
现在修改一下
index.js
,并在src
中增加utils
目录index.js
在
index.js
中我只引入了add
,相当于square
这个函数在上下文中并未引用。usedExports
不过我还需要改下
webpack.config.js
注意我只增加了
devtool:source-map
与optimization.usedExports = true
我们看下
package.json
默认初始化已经给们预设了多个不同的打包环境,因此我只需要运行下面命令就可以选择开发环境了
此时我们看到打包后的代码未引入的
square
有一行注释square
上下文未引用,虽然给了标记,但是未真正清除。光使用
usedExports:true
还不行,usedExports 依赖于 terser 去检测语句中的副作用
,因此需要借助terser
插件一起使用,官方webpack5
提供了TerserWebpackPlugin
这样一个插件在
webpack.config.js
中引入你会发现,那个
square
函数就没有了 如果我将usedExports.usedExports = false
,你会发现square
没有被删除。官方解释,当我们设置
optimization.usedExports
必须为true
,当我们设置usedExports:true
,且必须开起minimize: true
,这样才会把上下文未使用的代码给清除掉,如果minimize: false
,那么压缩插件将会失效。当我们设置
usedExports: true
此时生成打包的代码会有一个这样的魔法注释,
square
未使用当我们设置
minimize: true
时,webpack5
会默认开启terser
压缩,然后发现有这样的unused harmony export square
就会删掉对应未引入的代码。sideEffects
这个是
usedExports
摇树的另一种方案,usedExports
是检查上下文有没有引用,如果没有引用,就会注入魔法注释
,通过terser
压缩进行去除未引入的代码而
slideEffects
是对没有副作用
的代码进行去除首先什么是
副作用
,这是一个不太好理解的词,在react
中经常有听到其实
副作用
就是一个纯函数中存在可变依赖的因变量,因为某个因变量会造成纯函数产生不可控的结果举个例子
没有副作用的函数,输入输出很明确
有副作用,函数体内有不确定性因素
我们在
index.js
中引入watch.js
然后运行
npm run build:dev
,打包后的文件有watch
的引入在
index.js
中引入watch.js
并没有什么使用,但是我们仍然打包了进去为了去除这引入但未被使用的代码,因此你需要在
optimization.sideEffects: true
,并且要在package.json
中设置sideEffects: false
,在optimization.sideEffects
设置 true,告知 webpack 根据 package.json 中的 sideEffects 标记的副作用或者规则,从而告知 webpack 跳过一些引入但未被使用的模块代码。具体参考optimization.sideEffects此时你运行命令
npm run build:dev
,查看打包文件 我们就会发现,引入的watch.js
就没有了在官方中有这么一段话
使用 mode 为 "production" 的配置项以启用更多优化项,包括压缩代码与 tree shaking。
因此在
webpack5
中只要你设置mode:production
那些代码压缩、tree shaking
通通默认给你做了做了最大的优化,你就无需操心代码是否有被压缩,或者tree shaking
了。对于能否被
tree shaking
还补充几点1、一定是
esModule
方式,也就是export xxx
或者import xx from 'xxx'
的方式2、
cjs
方式不能被tree shaking
3、线上打包生产环境
mode:production
自动开启多项优化,可以参考生产环境的构建productiongizp 压缩
首先是是在
devServer
下提供了一个开发环境的compress:true
需要安装对应插件
webpack.config.js
中引入插件当你运行命令后,你就会发现打包后的文件有
gzip
的文件了但是我们发现
html
以及map.js.map
文件也被gizp
压缩了,这是没有必要的官方提供了一个
exclude
,可以排除某些文件不被gizp
压缩对比开启
gizp
压缩与未压缩,加载时间很明显有提升css tree shaking
主要删除未使用的样式,如果样式未使用,就删除掉。
现在修改下
index.js
我在body
中插入一个class
对应的 css 如下
执行
npm run serve
但是我们发现,样式居然没了于是苦思瞑想,不得其解,于是一顿排查,当我们把
sideEffects: false
时,神奇的是,样式没有被删掉 原来是sideEffects:true
把引入的 css 当成没有副作用的代码给删除了,此时,你需要告诉webpack
不要删除我的这有用的代码,不要误删了,因为import 'xxx.css'
如果设置了sideEffects: true
,此时引入的css
会被当成无副作用的代码,就给删除了。当你设置完后,页面就可以正常显示 css 了
官方也提供了另外一种方案,你可以在
module.rules
中设置以上与在
package.json
设置一样的效果,都是让webpack
不要误删了无副作用的 css 的代码但是现在有这样的
css
代码title
页面没有被引用,但是也被打包进去了此时需要一个插件来帮助我们来完成 css 的摇树purgecss-webpack-plugin
未引用的 css 就已经被删除了
分包
主要是减少入口依赖文件包的体积,如果不进行拆包,那么我们根据
entry
的文件打包就很大。那么也会影响首页加载的性能。官方提供了两种方案:
引入
loadsh
main.js
中将loadsh
打包进去了,体积也非常之大72kb
我们现在利用
entry
进行分包此时我们再次运行
npm run build:dev
此时main.js
的大小1kb
,但是loadsh
已经被分离出来了 生成的loadsh-vendors.js
会被单独引入可以看下打包后的
index.html
optimzation.splitChunks
对于动态导入模块,在webpack4+
就默认采取分块策略当
optimization.splitChunks.chunks:'all'
,此时可以把loash
分包出来了关于
optimization.splitChunks
的设置非常之多,有对缓存的设置,有对chunk
大小的限制,最常用的还是设置chunks:all
,建议SplitChunksPlugin多读几遍,一定会找到不少收获。optimization.runtimeChunk
时,运行时依赖的代码会独立打包成一个runtime.xxx.js
main.js
有一部分代码移除到一个独立的runtime.js
中webpack
提供了一个外部扩展,将输出的bundle.js
排除第三方的依赖,参考Externals但是此时
loash
已经被我们移除了,我们还需在HtmlWebpackPlugin
中加入引入的cdn
地址修改模版,由于模版内容是ejs,所以我们循环取出
js
数组中的数据此时你运行命令
npm run build:dev
,然后打开 html 页面但是我们发现当我们运行
npm run serve
启动本地服务,此时页面还是会引入loadsh
,在开发环境,其实并不需要引入,本身生成的bundle.js
就是在内存中加载的,很显然不是我们需要的此时我需要做几件事
1、开发环境我不允许引入
externals
2、模版
html
中需要根据环境判断是否需要插入cdn
根据传入模版的
env
判断是否需要插入 cdn图片资源压缩
主要是有选择的压缩图片资源,我们可以看下
module.rules.parser
官方提供了一个ImageMinimizerWebpackPlugin 我们需要安装
在
webpack.config.js
中引入image-minimizer-webpack-plugin
,并且在plugins
中引入这个插件,注意webpack5
官网那份文档很旧,参考npm
上npm-image-minimizer-webpack-plugin按照官网的,就直接报错一些配置参数不存在,我估计文档没及时更新
未压缩前
压缩后
使用压缩后,图片无损压缩体积大小压缩大小缩小一半,并且网络加载图片时间从
18.87ms
减少到4.81ms
,时间加载上接近 5 倍的差距,因此可以用这个插件来优化图片加载。这个插件可以将图片转成
webp
格式,具体参考官方文档效果测试一下总结
1、
webpack
如何做treeShaking
,主要是两种usedExports:true
,但是要配合terser
压缩插件才会生效sideEffects: true
,在package.json
中设置sideEffects:false
去除无副作用的代码,但是注意css
引入会当成无副作用的代码,此时需要在 rules 的 css 规则中标记sideEffects: true
,这样就不会删除 css 了2、
webpack
的 gizp 压缩 主要是利用CompressionWebpackPlugin
官方提供的这个插件3、
css
如何做treeShaking
, 主要是利用PurgeCSSPlugin
这个插件,会将没有引用 css 删除4、入口依赖文件拆包
entry
中分包处理,将依赖的第三方库独立打包成一个公用的bundle.js
,入口文件不会把第三方包打包到里面去optimization.splitChunks
设置chunks:'all'
将同步或者异步的esModule
方式的代码进行分包处理,会单独打成一个公用的 jsexternals
将第三方包分离出去,此时第三方包不会打包到入口文件中去,不过注意要在ejs
模版中进行单独引入 5、图片资源
加载优化image-minimizer-webpack-plugin
做图片压缩处理 6、本文示例code-example