7.代码压缩
代码压缩可以减少代码体积;破坏代码的可读性,提升破解成本;通常只是用于生产环境
目前最流行的代码压缩工具主要有两个:UglifyJs
和Terser
UglifyJs
是一个传统的代码压缩工具,已存在多年,曾经是前端应用的必备工具,但由于它不支持ES6
语法,所以目前的流行度已有所下降。
Terser
是一个新起的代码压缩工具,支持ES6+
语法,因此被很多构建工具内置使用。webpack
安装后会内置Terser
,当启用生产环境后即可用其进行代码压缩。
因此,我们选择Terser
,它的官网:https://terser.org/
关于副作用 side effect
副作用:函数运行过程中,可能会对外部环境造成影响的功能
如果函数中包含以下代码,该函数叫做副作用函数:
- 异步代码
- localStorage
- 对外部数据的修改
如果一个函数没有副作用,同时,函数的返回结果仅依赖参数,则该函数叫做纯函数(pure function)
webpack+Terser
webpack自动集成了Terser
如果你想更改、添加压缩工具,又或者是想对Terser进行配置,使用下面的webpack配置即可
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
optimization: {
// 是否要启用压缩,默认情况下,生产环境会自动开启
minimize: true,
minimizer: [ // 压缩时使用的插件,可以有多个
new TerserPlugin(), // 压缩js代码
new OptimizeCSSAssetsPlugin() // 压缩css代码
],
},
};
8. tree shaking
代码压缩可以移除模块内部的无效代码,而tree shaking
可以移除模块之间的无效代码,例如下面这个例子:
// myMath.js
export function add(a, b){
console.log("add")
return a+b;
}
export function sub(a, b){
console.log("sub")
return a-b;
}
// index.js
import {add} from "./myMath"
console.log(add(1,2));
在myMath.js
这个工具模块有两个导出方法,但是我们的项目只使用了add
方法,如果在打包的时候两个方法都打包的话无疑会增加无效代码量,tree shaking
的作用就是移除无效的代码块,只针对我们使用的部分进行打包处理。
(在例如我们常用的lodash这个库,我们通常只是用它里边的部分方法,并没有全部使用,打包的话也无疑是增加了很多无效的代码,这时候tree shaking
就很有必要。)
其实webpack2就开始支持了tree shaking
。只要是生产环境,tree shaking
就会自动开启。
原理
webpack会从入口模块出发寻找依赖关系,当解析一个模块时,webpack会根据ES6的模块导入语句来判断,该模块依赖了另一个模块的哪个导出。
webpack之所以选择ES6的模块导入语句,是因为ES6模块有以下特点:
导入导出语句只能是顶层语句
import的模块名只能是字符串常量
import绑定的变量是不可变的
这些特征都非常有利于分析出稳定的依赖
在具体分析依赖时,webpack坚持的原则是:保证代码正常运行,然后再尽量tree shaking
所以,如果你依赖的是一个导出的对象,由于JS语言的动态特性,以及webpack
还不够智能,为了保证代码正常运行,它不会移除对象中的任何信息
因此,我们在编写代码的时候,尽量:
- 使用
export xxx
导出,而不使用export default {xxx}
导出 - 使用
import {xxx} from "xxx"
导入,而不使用import xxx from "xxx"
导入
依赖分析完毕后,webpack
会根据每个模块每个导出是否被使用,标记其他导出为dead code
,然后交给代码压缩工具处理,代码压缩工具最终移除掉那些dead code
代码。
使用第三方库
某些(很多)第三方库可能使用的是commonjs
的方式导出,比如lodash
,又或者没有提供普通的ES6方式导出,对于这些库,tree shaking
是无法发挥作用的。
因此要寻找这些库的es6
版本,好在很多流行但没有使用的ES6
的第三方库,都发布了它的ES6
版本,比如lodash-es
作用域分析(深度分析)
tree shaking
本身并没有完善的作用域分析,可能导致在一些dead code
函数中的依赖仍然会被视为依赖
插件webpack-deep-scope-plugin
提供了作用域分析,可解决这些问题(这个库慎用,它的个人开发的,维护难免不及时,)
副作用问题
webpack在tree shaking
的使用,有一个原则:一定要保证代码正确运行
在满足该原则的基础上,再来决定如何tree shaking
因此,当webpack
无法确定某个模块是否有副作用时,它往往将其视为有副作用
因此,某些情况可能并不是我们所想要的
//common.js
var n = Math.random();
//index.js
import "./common.js"
虽然我们根本没用有common.js
的导出,但webpack
担心common.js
有副作用,如果去掉会影响某些功能
如果要解决该问题,就需要标记该文件是没有副作用的
在package.json
中加入sideEffects
// package.json 文件设置
{
"sideEffects": false
}
有两种配置方式:
- false:当前工程中,所有模块都没有副作用。注意,这种写法会影响到某些css文件的导入
- 数组:设置哪些文件拥有副作用,例如:
["!src/common.js"]
,表示只要不是src/common.js
的文件,都有副作用
这种方式我们一般不处理,通常是一些第三方库在它们自己的
package.json
中标注
css tree shaking
webpack
无法对css
完成tree shaking
,因为css
跟es6
没有半毛钱关系
因此对css
的tree shaking
需要其他插件完成,例如:purgecss-webpack-plugin
注意:
purgecss-webpack-plugin
对css module
无能为力
9. 懒加载
懒加载就是动态加载,按需加载,当我们需要的时候再加载。使用的是 import()
语法,它会返回一个promise
,
实现思路:最开始会有一个webpackJsonp
数组,等我们加载完需要的模块后,它会把加载的模块放进webpackJsonp
数组中。
// index.js
const btn = document.querySelector("button");
btn.onclick = async function() { // 当按钮被点击后才会去读取 util这个文件
//动态加载
//import 是ES6的草案
//浏览器会使用JSOP的方式远程去读取一个js模块
//import()会返回一个promise (* as obj)
// const { chunk } = await import(/* webpackChunkName:"lodash" */"lodash-es");
const { chunk } = await import("./util");
const result = chunk([3, 5, 6, 7, 87], 2);
console.log(result);
};
//util.js
export { chunk } from "lodash-es"; // 静态的方式,从 lodash-es中导出chunk方法
util.js会等到执行时才会引入,当我们执行到import
时才会到服务端请求该模块的js文件,而不是在页面加载的时候就去请求,这样可以减少页面刚开始加载时东西过多而导致时间很长的问题。
10. gzip
gzip
是一种压缩文件的算法
B/S结构中的压缩传输
优点:传输效率可能得到大幅提升
缺点:服务器的压缩需要时间,客户端的解压需要时间
使用webpack进行预压缩
使用compression-webpack-plugin
插件对打包结果进行预压缩,可以移除服务器的压缩时间
下载安装:npm i -D compression-webpack-plugin
https://www.npmjs.com/package/compression-webpack-plugin
https://www.webpackjs.com/plugins/compression-webpack-plugin/
//webpack.config.js配置
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
new CmpressionWebpackPlugin({
test: /\.js/, //只针对匹配到的文件进行压缩
minRatio: 0.8 //压缩后是原来体积的0.8,才会进行压缩(只有压缩率比这个值小的资源才会被处理)
})
]
};
11. ESlint(代码风格检查)
ESLint是一个针对JS的代码风格检查工具,当不满足其要求的风格时,会给予警告或错误
民间中文网:https://eslint.bootcss.com/
eslint规则集 rules
https://eslint.bootcss.com/docs/rules/
具体使用查看课件。
12. bundle analyzer
webpack-bundle-analyzer
https://www.npmjs.com/package/webpack-bundle-analyzer
使用交互式可缩放树图可视化webpack输出文件的大小。便于直观地比较各个bundle文件的大小,以达到优化性能的目的。
//webpack.config.js 配置
const WebpackBundleAnalyzer = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = {
mode: "production",
plugins: [new WebpackBundleAnalyzer()]
};
优化部分就此完结,具体还要情况具体分析,不明白的地方还需查看课件与官方文档。