性能优化 - 产出代码
# 性能优化 - 产出代码
Webpack 性能优化主要有两个方向:优化打包构建速度 - 提高开发体验和效率,优化产出代码 - 提升产品性能,本文主要介绍如何优化产出代码。
优化产出代码后可以带来的效果:
- 体积更小,加载更快
- 合理分包,不重复加载
- 速度更快,内存使用更少
产出代码的优化措施有 8 点:
- 小图片 base64 编码
- bundle 加 hash
- 懒加载
- 提取公共代码
- IgnorePlugin
- 使用 CDN 加速
- 使用 production
- 使用 Scope Hosting
前 5 个其实在前面两章中已经介绍过了。
# 小图片 base64 编码
在生产环境下(webpack.prod.js
),将小于某个大小的图片(例如 5kb)转成 base64 格式产出,减少一个网络请求。如下代码所示:
// 图片 - 考虑 base64 编码的情况
{
test: /\.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
options: {
// 小于 5kb 的图片用 base64 格式产出
// 否则,依然延用 file-loader 的形式,产出 url 格式
limit: 5 * 1024,
// 打包到 img 目录下
outputPath: '/img/',
// 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)
// publicPath: 'http://cdn.abc.com'
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# bundle 加 hash
在生产环境下(webpack.prod.js
),对于出口文件,根据文件的内容计算出一个 hash 值(下述示例为 8 位 hash),如果文件内容更新后,缓存会失效,重新请求新的文件;如果代码没有变化,hash 值不变,就会使用缓存,从而提高加载效率。如下代码所示:
const { merge } = require('webpack-merge')
const webpackCommonConf = require('./webpack.common.js')
const { srcPath, distPath } = require('./paths')
module.exports = merge(webpackCommonConf, {
mode: 'production',
output: {
// filename: 'bundle.[contenthash:8].js', // 打包代码时,加上 hash 戳
filename: '[name].[contenthash:8].js', // name 即多入口时 entry 的 key
path: distPath,
}
})
2
3
4
5
6
7
8
9
10
11
12
# 懒加载
通过 import
语法,先加载重要的文件,然后异步加载大的文件。这个逻辑与 Vue 和 React 中组件的异步加载类似,如下代码所示:
setTimeout(() => {
// 定义 chunk
import('./dynamic-data.js').then(res => {
console.log(res.default.message)
})
}, 1500)
2
3
4
5
6
# 提取公共代码
在生产环境下(webpack.prod.js
),将第三方模块和公用引用的代码单独拆分出去。
参考 高级配置-抽离公共代码 这一章的内容。
# IgnorePlugin
参考 IgnorePlugin 避免引入无用模块 这一章的内容。
# 使用 CDN 加速
在生产环境下(webpack.prod.js
),设置 output.publicPath
后,打包出来的 html 里都会引用 CDN 的静态资源文件。
需要注意,在打包完后,需要将结果文件(dist
目录)都上传到 CDN 服务器,保证这些静态资源都是可访问的。
const { merge } = require('webpack-merge')
const webpackCommonConf = require('./webpack.common.js')
const { srcPath, distPath } = require('./paths')
module.exports = merge(webpackCommonConf, {
mode: 'production',
output: {
// filename: 'bundle.[contenthash:8].js', // 打包代码时,加上 hash 戳
filename: '[name].[contenthash:8].js', // name 即多入口时 entry 的 key
path: distPath,
publicPath: 'http://cdn.abc.com' // 修改所有静态文件 url 的前缀(如 cdn 域名)
}
})
2
3
4
5
6
7
8
9
10
11
12
13
同理,图片也可以设置 CDN 地址,如下代码所示:
// 图片 - 考虑 base64 编码的情况
{
test: /\.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
options: {
// 小于 5kb 的图片用 base64 格式产出
// 否则,依然延用 file-loader 的形式,产出 url 格式
limit: 5 * 1024,
// 打包到 img 目录下
outputPath: '/img/',
// 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)
publicPath: 'http://cdn.abc.com'
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 使用 production
前面说过,一般会将 Webpack 配置文件拆分成三份:
- webpack.common.js
- webpack.dev.js
- webpack.prod.js
使用 production 就是指的使用 mode: 'production'
的方式(即使用 webpack.prod.js
这份配置文件)去打包生产环境的代码。
它具有如下好处:
- 无需配置,production 默认自动开启代码压缩(webpack4.x 后的功能)
- Vue、React 等会自动删掉调试代码(如开发环境的 warning)
- 会自动启动 Tree-Shaking
Tree-Shaking
在 production 模式下,打包时会自动删除没有被调用的函数,从而减小打包后的代码体积。
只有 ES6 Module(静态引入,编译时引入)才能实现 Tree-Shaking,CommonJS(动态引入,执行时引入)不能够静态分析,无法实现 Tree-Shaking。
# 使用 Scope Hosting
默认的 Webpack 打包结果中,多个 JS 文件会被打包生成多个函数。我们知道,每个函数都会产生一个作用域,那么打包前的文件越多打包后的函数就会越多,这对整个 JS 代码的执行及内存消耗很不友好。
我们希望将文件合并在一个函数里执行,就能减少作用域数量,提高代码执行效率,这就需要开启 Scope Hosting。
开启方式:
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin')
module.exports = {
resolve: {
// 针对 NPM 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
plugins: [
// 开启 Scope Hosting
new ModuleConcatenationPlugin()
]
}
2
3
4
5
6
7
8
9
10
11
12
使用 Scope Hosting 的好处:
- 代码体积更小
- 创造函数作用域更少
- 代码可读性更好
(完)