terser
Terser是一个JavaScript的解释(Parser)、Mangler(绞肉机)/Compressor(压缩机)的工具集
Terser可以帮助我们压缩、丑化我们的代码,让我们的bundle变得更小
terser和postcss和babel一样都是独立的工具,即可以独立使用,也可以结合webpack等构建工具一起使用
npm install terser -g
在webpack中有一个minimizer属性,在production模式下,默认就是使用TerserPlugin来处理我们的代码的
如果我们对默认的配置不满意,也可以自己来创建TerserPlugin的实例,并且覆盖相关的配置
optimization: {
// 开启minimize后,webpack才会依次去调用minimizer中的插件进行使用
// production模式下 minimize的默认值即为true,可以不单独设置
minimize: true,
minimizer: [
new TerserPlugin({
// 不抽取代码
extractComments: false,
// 使用多进程并发运行提高构建的速度,默认值是true
// 并发运行的默认数量: os.cpus().length - 1
parallel: true,
// terser的配置项
// 具体配置文档 https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
// terserOptions: {}
})
]
}
}
压缩css
CSS压缩通常是去除无用的空格等,因为很难去修改选择器、属性的名称、值等
CSS的压缩我们可以使用另外一个插件:css-minimizer-webpack-plugin
css-minimizer-webpack-plugin是使用cssnano工具来优化、压缩CSS
npm install css-minimizer-webpack-plugin -D
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
optimization: {
minimize: true,
// 普通插件配置在plugins 选项下
// 压缩插件配置在minimizer 选项下
minimizer: [
new TerserPlugin({
extractComments: false,
}),
new CssMinimizerPlugin()
]
}
配置分离
将项目配置文件调整为
config # 位于项目根目录下 --- 用于存放项目相关的配置文件
├── common.config.js # 通用配置文件
├── prod.config.js # 生成环境配置文件
└── sit.config.js # 开发环境配置文件
// 调整package.json下的script命令值
"build": "webpack --config ./config/common.config.js --env prod",
"serve": "webpack serve --config ./config/common.config.js --env sit"
// common.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// ps: 不是默认导出,是具名导出
const { merge } = require('webpack-merge')
const devConfig = require('./dev.config')
const prodConfig = require('./prod.config')
const getConfig = function(env) {
return {
// entry是基于context的
// 即path.resolve(context的值, entry的值)
// context的值是项目根目录
entry: './src/main.js',
output: {
// 因为配置文件放置到了config下
// 所以为了让构建后的内容存放到项目根目录
// 需要将build修改为../build
path: path.resolve(__dirname, '../build'),
filename: 'js/[name]-[chunkhash]-bundle.js',
chunkFilename: 'js/[name]-[chunkhash]_chunk.js',
clean: true
},
resolve: {
extensions: ['.js', '.json', '.jsx', '.ts']
},
module: {
rules: [
{
test: /\.css$/,
use: [
env.prod ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'index.html'
})
]
}
}
// 可以直接导出一个函数
// 这个函数需要返回一个配置对象
// 参数是命令行运行时候传入的配置对象
module.exports = function(env) {
const config = env.prod ? prodConfig : devConfig
return merge(getConfig(env), config)
}
// dev.config.js
const path = require('path')
const WebpackDevServer = require('webpack-dev-server')
module.exports = {
mode: 'development',
devServer: {
port: 3000,
host: WebpackDevServer.internalIPSync('v4'),
compress: true,
static: ['public'],
historyApiFallback: true,
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {
'^/api': ''
},
changeOrigin: true
}
}
}
}
// prod.config.js
const path = require('path')
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
mode: 'production',
optimization: {
runtimeChunk: {
name: 'runtime'
},
splitChunks: {
chunks: 'all',
minSize: 10,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
filename: 'js/[name]_[chunkhash].js'
},
math: {
test: /[\\/]math[\\/]/,
filename: 'js/[name]_[chunkhash].js'
}
}
},
minimize: true,
// 普通插件配置在plugins 选项下
// 压缩插件配置在minimizer 选项下
minimizer: [
new TerserPlugin({
extractComments: false,
}),
new CssMinimizerPlugin()
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name]_[chunkhash].css',
chunkFilename: 'css/[name]_[chunkhash]_chunk.css'
})
]
}
Tree Shaking
Tree Shaking是一个术语,最早起源于LISP,在计算机中表示消除死代码(dead_code)
只有纯函数才可以被tree shaking,一个非纯函数,即使没有被使用,也无法被tree shaking
对JavaScript进行Tree Shaking是源自打包工具rollup
Tree Shaking依赖于ES Module的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系)来确定哪些代码可以被tree shaking
在webpack中有两种实现tree shaking的方案
- usedExports: 通过标记某些函数是否被使用,之后通过Terser来进行优化
- sideEffects: 跳过整个模块/文件,直接查看该文件是否有副作用
usedExports
在usedExports设置为true时,会有一段注释: unused harmony export mul
这是段魔法注释,用于告知Terser在优化时,可以删除掉这段代码
optimization: {
// 该属性在production模式下会被自动设置为true
// 所以一般不需要手动配置
usedExports: true
}
sideEffects
sideEffects 是一个 webpack 特有的选项,它可以用来告诉 webpack 在做代码分割时,哪些模块是有副作用(side effect)的
默认情况下,webpack会认为所有的模块都是副作用模块
// package.json
// 值为false的时候,表示所有模块都是纯模块
// 此时即使该模块有副作用,也会被移除
// 也可以指定某些特定的模块是纯模块
"sideEffects": [
"*.css"
]
Scope Hoisting
在开发模式下,webpack会降每一个模块中的内容放在一个IIFE中
无论是从最开始的代码运行,还是加载一个模块,都需要执行一系列的函数
作用域提升是指Webpack 会尽量把所有模块的代码打包到一个函数中,然后把这个函数放在最顶层
从而让代码在运行时更快,并且打出来的包也更小
在production模式下,默认这个模块就会启用
在development模式下,我们需要自己来打开该模块
const webpack = require('webpack')
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
http 压缩
HTTP压缩是一种内置在 服务器 和 客户端 之间的,以改进传输速度和带宽利用率的方式
- 将已经压缩的文件部署在服务器上
- 浏览器在请求的时候会通过
Accept-Encoding来告知浏览器支持哪些压缩格式 - 服务器会将浏览支持的对应压缩格式文件进行返回,并通过
Content-Encoding来告知浏览器返回的是那种压缩格式 - 浏览器会自动对对应的资源进行解压,并进行解析
目前常见的压缩格式
对http内容进行压缩的压缩算法和普通的文件压缩算法不同
http内容的压缩算法都是单文件压缩算法,一次只能压缩一个文件
| 格式 | 说明 |
|---|---|
| compress | UNIX的“compress”程序的方法 历史性原因,已被淘汰 |
| deflate | 基于deflate算法(定义于RFC 1951)的压缩,使用zlib数据格式封装 |
| gzip | GNU zip格式(定义于RFC 1952),是目前使用比较广泛的压缩算法 |
| bz | 一种新的开源压缩算法,专为HTTP内容的编码而设计 |
# 使用compression-webpack-plugin对文件进行压缩
# 打包后会存在原文件和打包后的文件
# 如果浏览器支持压缩格式,就传输压缩格式
# 如果浏览器不支持压缩格式,就传输原始文件
npm install compression-webpack-plugin -D
const CompressionPlugin = require("compression-webpack-plugin")
module.exports = {
plugins: [
// CompressionPlugin在配置中存在一个默认的压缩比
// 如果文件压缩后,压缩比小于配置中的压缩比
// 那么就表示文件压缩的意义并不大,因此就不会对其进行压缩
new CompressionPlugin({
// 对那些文件进行压缩
test: /\.(js|css)$/,
// 采用那种压缩算法
algorithm: 'gzip'
})
],
};
html压缩
plugins: [
// 在生产环境,会自动使用HtmlWebpackPlugin对html模板进行压缩
// 配置项 template --- 模板
// 配置项 inject --- true、 false 、body、head
// cache --- 默认值为true --- 只有当文件改变时,才会生成新的文件
// minify --- 默认会使用一个插件html-minifier-terser
// --- 可以对html的压缩进行自定义配置
new HtmlWebpackPlugin({
template: 'index.html'
})
]
打包分析
时间分析
如果我们需要查看每个包的构建时间,来分析那个模块或那个loader的构建时间比较长
可以使用speed-measure-webpack-plugin
# speed-measure-webpack-plugin 和 mini-css-extract-plugin 冲突 不能一起使用
npm install speed-measure-webpack-plugin -D
const speedMeasurePlugin = require('speed-measure-webpack-plugin')
const SMP = new speedMeasurePlugin()
module.exports = function(env) {
const config = env.prod ? prodConfig : devConfig
const finalConfig = merge(getConfig(env), config)
// SMP.wrap会对配置进行拦截,添加分析功能后返回一个新的配置对象
return SMP.wrap(finalConfig)
}
文件分析
stats.json
"scripts": {
# --profile --- 进行文件分析
# --json=<文件名> --- 将分析结果输出到那个文件中
"build": "webpack --config ./config/common.config.js --env prod --profile --json=stats.json",
"serve": "webpack serve --config ./config/common.config.js --env sit"
}
- 去github clone webpack/analyse
- 安装依赖并通过
npm run dev运行项目 - 将json文件传达到运行网页中,即可得到分析结果
webpack-bundle-analyzer
我们可以通过webpack-bundle-analyzer来非常直观查看包大小
npm install webpack-bundle-analyzer -D
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
plugins: [
new HtmlWebpackPlugin({
template: 'index.html'
}),
new BundleAnalyzerPlugin()
]