什么是Tree Shaking
什么是TreeShaking呢?
- TreeShaking是一个术语,在计算机中表示消除死代码(dead_code)
- 最早的想法起源于LISP,用于消除未调用的代码
Js的TreeShaking:
- 对JS进行TreeShaking是源自打包工具rollup
- 这是因为TreeShaking依赖于ES Module的静态语法分析
- webpack2正式内置支持了ES2015模块,和检测未使用模块的能力
- webpack4扩展了这个能力,并且通过package.json的sideEffects属性作为标记,告知webpack在编译时,哪些文件可以安全的删除
- webpack5中,也提供了对部分CommonJs的treeshaking支持
webpack实现Tree Shaking
事实上webpack实现Tree Shaking采用了两种不同的方案:
将mode设置为development模式:
- 为了可以看到usedExports带来的效果,我们需要设置为development模式
- 因为在production模式下,webpack默认的一些优化会带来很大的影响
设置usedExports为true和false对比打包后的代码:
- 在usedExports设置为true时,会有一段注释:unused harmony export mul;
- 这段注释的意义是什么呢?告知Terser在优化时,可以删除这段代码
这个时候,我们将minimize设置为true:
- usedExports设置为false时,mul函数没有被移除掉
- usedExports设置为true时,mul函数被移除了
所以,usedExports实现TreeShaking是结合Terser来完成的
注意:使用usedExports对于引入了文件却没有使用文件内任何变量的情况,是无法做到不打包引入的文件的,想要实现这个效果,需要使用sideEffects
sideEffects
sideEffects用于告知webpack compiler哪些模块是有副作用的:
- 副作用的意思是这里面的代码有执行一些特殊的任务,不能仅仅通过export来判断这段代码的意义
在package.json中设置sideEffects的值:
- 如果我们将sideEffects设置为false,就是告知webpack可以安全的删除未用到的exports
- 如果有一些我们希望保留,可以设置为数组
比如我们有一个format.js、style.css文件:
- 该文件在导入时没有使用任何的变量来接收
- 那么打包后的文件,不会保留format.js、style.css相关的任何代码
如果是符合模块化的JS代码,那么这没有问题,比如format.js中我们访问window,给window添加了一个abc变量,这一步就会被删除掉,这是符合js的模块化开发的。
但是如果是css,就会导致样式失效,这时有两个解决方案
package.json中添加变量sideEffects,值是一个数组,把style.css做为数组项
"sideEffects": [
"./src/style.css"
],
如果是全部CSS文件都不被删除,就这样配置
"sideEffects": [ "**.css" ],
webpack中配置css-loader
{ test: /\.css$/, use: [ isProduction ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader', ], sideEffects: true, },
webpack中tree shaking的设置
- 在optimization中配置usedExports为true,来帮助Terser进行优化
- 在package.json中配置sideEffects,直接对模块进行优化
CSS实现TreeShaking
上面我们通过webpack实现了对js代码的tree shaking,css同样可以进行tree shaking
- CSS的TreeShaking需要借助于一些其他的插件
- 在早期的时候,我们会使用PurifyCss插件来完成CSS的tree shaking,但是目前该库已经不在维护了
- 目前我们可以使用另外一个库来完成CSS的TreeShaking:PurgeCSS,也是一个帮助我们删除未使用的CSS的工具
安装PurgeCss的webpack插件:
npm install purgecss-webpack-plugin -D
配置PurgeCss:
- paths表示要检测那些目录下的内容,这里我们可以使用glob(glob是一个库,帮我们指定全局)
- 默认情况下,PurgeCss会将我们的body、html标签的样式移除掉,如果我们需要保留,可以添加一个safelist属性
- safelist属性是一个函数,返回一个对象,对象有一给standard属性,值是一个数组
在现代的前端开发中,CSS tree shaking 没有太大意义,因为 CSS tree shaking 的原理就是分析 html 或其他代码中对 css 选择器的使用情况,将未被使用的 css 删除。但是因为现代开发时 css 选择器多为动态的,所以会有误删的情况,因此不被使用。new PurgeCssPlugin({ paths: glob.sync(`${resolveApp('./src')}/**/*`, { nodir: true }), safelist: function () { return { standard: ['body', 'html'], }; }, }),
HTTP压缩
- safelist属性是一个函数,返回一个对象,对象有一给standard属性,值是一个数组
Http压缩是一种内置在浏览器和客户端之间的,以改进传输速度和宽带利用率的方式
Http压缩的流程是什么呢?
- 第一步:HTTP数据在服务器发送前就已经被压缩了;(可以在webpack中完成)
- 第二步:兼容的浏览器在向服务器发送请求时,会告知服务器自己支持哪些压缩格式
- 第三步:服务器在浏览器支持的压缩格式下,直接返回对应的压缩后的文件,并且在响应头中告知浏览器
目前的压缩格式
目前的压缩格式非常多
- compress:UNIX的 compress 程序的方法,不推荐使用,一般使用gzip或deflate
- defalte:基于deflate算法的压缩,使用zlib数据格式封装
- gzip:GNU zip 格式,是目前使用比较广泛的压缩算法
- br:一种新的开源压缩算法,专为HTTP内容的编码而设计
Webpack对文件压缩
webpack中相当于是实现了HTTP压缩的第一步操作,我们可以使用CompressionPlugin。
第一步,安装CompressionPlugin:
npm i compression-webpack-plugin -D
第二步,配置并使用
// 引入
const CompressionPlugin = require('compression-webpack-plugin');
plugins:{
// ...
// 配置 compression
new CompressionPlugin({
threshold: 0,
test: /\.(css|js)$/i,
minRatio: 0.8,
algorithm: 'gzip',
}),
}
InlineCheckHtmlPlugin
这个插件可以将一些chunk出来的模块,内联到html中
- 比如runtime的代码,代码量不大,但是必须要加载的
- 那么我们就可以直接内联到html中
这个插件是在react-dev-utils中实现的,我们首先需要安装一下
npm install react-dev-utils -D
然后引入并配置
// 引入相关插件
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime.*\.js$/]),
webpack打包library
看着很唬人,其实就是我们自己写个包,然后给别人用,因为不知道用的那个人是在什么环境使用,所以我们需要给这个我们自己的包做一些配置
const path = require('path');
module.exports = {
mode: 'production',
entry: './index.js',
output: {
path: path.resolve(__dirname, './build'),
filename: 'jujuul_utils.js',
libraryTarget: 'umd',
library: 'jujuulUtils',
globalObject: 'this',
},
};
关键点就是 libraryTarget ,配置成 umd 就可以在任意环境使用