十六、加载构建优化
- 懒加载
// 按需加载
oBtn.addEventListener('click', () => {
import('./utils').then(({ default: element }) => {
console.log(element)
document.body.appendChild(element)
})
})
- 预获取 & 预读取
// 按需加载
oBtn.addEventListener('click', () => {
import(
/* webpackChunkName:'utils' */
/* webpackPreload:true */
'./utils').then(({ default: element }) => {
console.log(element)
document.body.appendChild(element)
})
})
// /* webpackPrefetch:true */
与 prefetch 指令相比,preload 指令有许多不同之处:
preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
浏览器支持程度不同。
preload 可以使用在首页很可能需要点击的按钮上面
- CDN优化
优化打包速度, 使用CDN加载提高性能.
// index.html
<!-- cdn可以在对应官网查找 -->
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
// webpack.common.js
output: {
// publicPath: '配置自己的CDN地址'
},
// 排除lodash,加快打包速度
// 使用CDN加载lodash提高性能
// '_'是固定写法
externals: {
lodash: '_'
}
- dll库
把大的, 不经常变的打包成一个dll库, 然后使用方直接加载使用。
const path = require('path')
const webpack = require('webpack')
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
mode: "production",
// 需要打包的东西
entry: {
react: ['react', 'react-dom']
},
output: {
// 输出路径
path: path.resolve(__dirname, 'dll'),
filename: 'dll_[name].js',
// 暴露从入口导出的内容, 给使用地方的命名
library: 'dll_[name]'
},
optimization: {
minimizer: [
new TerserPlugin({
// 使用Terser压缩
// minimize: true,
// 去除注释文件
extractComments: false
}),
],
},
plugins: [
// 生成dll的插件
new webpack.DllPlugin({
name: 'dll_[name]',
// manifest负责映射到具体文件
path: path.resolve(__dirname, './dll/[name].manifest.json')
})
]
}
- 使用dll库
注意dll库只是帮助我们避免打包了, js里面导入文件不变。
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
const commonConfig = {
/*省略*/
plugins: [
new webpack.DllReferencePlugin({
context: resolveApp('./'),
// 相对于context的路径
manifest: resolveApp('./dll/react.manifest.json')
}),
//
new AddAssetHtmlPlugin({
// 这个插件目前之后在html里面找auto目录
outputPath: 'auto',
// 拷贝一份到dist, 同时添加html引用, html从auto里面找
filepath: resolveApp('./dll/dll_react.js')
})
]
}
- css抽取和压缩
css 150KB再考虑分包,因为这样增加一次加载。
// webpack.prod.js
// 抽取独立css
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
// 压缩css
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
module.exports = {
mode: 'production',
optimization: {
minimizer: [
new CssMinimizerPlugin()
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[hash:8].css'
})
]
}
// webpack.common.js
// 抽取独立css
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
{
test: /\.css$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
options: {
// 代表向前找一个loader处理
esModule: false
}
}, 'postcss-loader'
]
}
- 作用域提升
作用域提升只能知道esModule的语法
// webpack.prod.js
const webpack = require('webpack')
module.exports = {
mode: 'production',
plugins: [
/* 省略 */
new webpack.optimize.ModuleConcatenationPlugin()
]
}
- TreeShaking
a. usedExports 标记不需要使用的代码
optimization: {
// 标记不需要使用的代码:/* unused harmony export foo2 */
usedExports: true,
// 树摇, TerserPlugin移除不使用的代码
minimize: true,
}
b. sideEffects 识别副作用
对于不需要使用的类,有选择的跳过,去除对应副作用
// utils.js
window.utils = '1111'
// index.js
import './utils'
//1. 是可以打印的
console.log(window.utils, '<------')
// package.json 代表代码都是没有副作用的, 如果没有使用就移除
"sideEffects": false
//2. 打印为undefined
console.log(window.utils, '<------')
// package.json 代表代码都是有副作用的,即使没有使用也不能移除
//3. 使用副作用
"sideEffects": [
"./src/title.js"
]
正常Css文件,我们是需要使用副作用的 ,可以在package.json里面配置,不过一般在webconfig文件里面配置
c. PurgeCSSPlugin 针对Css文件进行树摇
yarn add purgecss-webpack-plugin glob --dev
注意及时代码注释,主要注释里面有对应标签,也算使用。
// webpack.prod.js
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
const resolveApp = require('./paths')
const glob = require('glob')
module.exports = {
plugins: [
new PurgeCSSPlugin({
// 查找src里面所有文件
paths: glob.sync(`${resolveApp('./src')}/**/*`, { nodir: true }),
safelist: function () {
return {
// 下面css标签不会被树摇
standard: ['body', 'html', 'ef']
}
}
})
]
}
- 压缩部署,http请求的时候节省资源
yarn add compression-webpack-plugin --dev
// webpack.prod.js
const CompressionPlugin = require("compression-webpack-plugin")
module.exports = {
plugins: [
new CompressionPlugin({
// 只压缩css 和 js 文件
test: /\.(css|js)$/,
// 默认0.8 只有压缩比到0.8才生成压缩文件
minRatio: 0.8,
// 文件大小, 开始压缩
threshold: 0,
// 压缩格式
algorithm: 'gzip'
})
]
}
- inlineChunkHtmlPlugin 可以向Html注入内容
有时候比较小的文件,可以直接在html中使用,而不需要引入一次。
yarn add inline-chunk-html-plugin --dev
// webpack.prod.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
// 把runtime文件直接注入到 需要的Html文件中
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime.*\.js/])
]
}
- webpack 打包 三方库
output: {
filename: 'sy_utils.js',
path: path.resolve(__dirname, 'dist'),
/// umd 所有模块化结合
libraryTarget: 'umd',
// library使用的名称
library: 'syUtil',
// 调用全局变量
globalObject: 'this'
}
- 打包时间和内容分析
// 时间分析
// webpack.common.js
// 注意兼容性, 需要mini-css-extract-plugin降级到1.3.6
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin")
const smp = new SpeedMeasurePlugin()
module.exports = (env) => {
/**/
return smp.wrap(mergeConfig)
}
// 内容分析
// webpack.prod.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
plugins: [
/**/
new BundleAnalyzerPlugin()
]