- 优化构建速度
- 优化产出体积
WDS Webpack DevServer
HMR Hot Module Replacement
Live Reloading 自动刷新
vue:HMR
性能优化主要是对开发环境而言的,因为生产一般只构建一次
文件内容不变,计算的hash值就不变,可以利用浏览器缓存加快速度
1. 优化babel-loader
- 适用于开发/生产环境
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
// 对编译的文件缓存
cacheDirectory: true,
}
},
// 不编译node_modules
exclude: path.resolve(__dirname, 'node_modules')
// 或者include src下的代码
},
2. 优化第三方库如moment
- 适用于开发/生产环境
index.js中引入了moment.js(但只需要引入中文的语言),给打包后的index.js造成了不必要的体积负担
① IgnorePlugin
- 不引入相应代码,最终打包生成的自然也没有
const webpack = require('webpack')
plugins:[
// 忽略moment库的文件夹./locale 不引入任何语言包,若需要使用则手动引入
new webpack.IgnorePlugin(/\.\/locale/,/moment/)
]
② noParse
- 不做打包,最终打包生成的有这部分代码
- 减少打包的时间,但没有减小体积
module.exports = {
// 不做打包的配置
noParse: [/vue\.min\.js$/]
}
- index.js
// 手动引入中文语言包
import 'moment/locale/zh-cn'
3. 多进程打包1(加快打包速度)
- JS/NodeJS/Webpack都是单线程的
- 可以开启多进程打包
- happyPack - 和babel-loader相关
- 开启多进程也是有开销的
const HappyPack= require('happypack')
...
{
test: /\.js$/,
// 将对js的处理交给id为babel的happyPack的实例
use: ['happypack/loader?id=babel'],
// 不编译node_modules
exclude: path.resolve(__dirname, 'node_modules')
},
...
plugins:[
// 创建实例
new HappyPack({
id: 'babel',
loaders: ['babel-loader?cacheDirectory']
})
]
4. 开启多进程2
- webpack-parallel-uglify-plugin
const ParallelUglifyPlugin= require('webpack-parallel-uglify-plugin')
...
plugins:[
new ParallelUglifyPlugin({
uglifyJS: {
beautify: false,
comments: false // 关闭注释
}
})
]
自动刷新watch 热更新HMR
- 不要用在生产环境下
module.exports = {
// 这是webpack devServer自带的
watch: true, // 监听 自动刷新
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300, // 监听到变化后300ms再编译
poll: 1000, // 每隔1s看代码是否变化
}
}
热更新配置写法2
-
css自动配置了热更新、js要手动配置
-
devServer 3.9.0
-
webpack.config.js
const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin')
const path = require('path')
moudule.exports = {
entry: {
// index: path.join(srcPath, 'index.js') 原先写法
index: [
'webpack-dev-server/client?http://localhost:8080/',
'webpack/hot/dev-server',
path.join(srcPath, 'index.js')
]
},
plugins: [
new HotModuleReplacementPlugin ()
],
devServer:{
hot: true
}
}
- index.js
if (module.hot) {
console.log('module', module)
// accept接受监听的范围 第一个参数是数组或字符串
module.hot.accept(['./number'], () => {
console.log('【不要清空counter,且重新执行number】')
const oldNumber = document.getElementById('number')
document.body.removeChild(oldNumber)
number()
})
}
5. DLL动态链接库
-
不要用在生产环境下
-
提高构建速度
-
React Vue代码体积大 打包速度慢
-
版本变化不会很频繁
-
可以预先打包一次 放在xx地方 再引用
-
DLLPlugin
-
DLLReferencePlugin
-
版本信息
生成文件
- webpack.dll.conf.js (设在根目录下)
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'development',
entry: {
react: [
'react',
'react-dom'
]
},
output: {
// 生成1
filename: '[name]_[chunkhash].dll.js',
path: path.join(__dirname, 'dist'),
library: '_dll_[name]' // 全局变量_dll_react
},
plugins: [
// 生成2
new webpack.DllPlugin({
name: '_dll_[name]', // 和library中全局变量一样全
// 在根目录/dist/ 下生成 react.manifest.json文件
path: path.join(__dirname, 'dist/[name].manifest.json')
})
]
}
- package.josn
"scripts": {
"dll": "webpack --config webpack.dll.conf.js"
}
- 生成1 react_hashxxx.dll.js(相关的react代码和react-dom代码被预先打包到这里)
- 生成2 react.manifest.json (当业务代码要使用react或react-dom的某些模块,就会到这里去找索引 → 找到代码)
使用dll
- index.html
- 模板 打包后dist目录下的index就会去读dist中的react.dll.js
<script src="./react.dll.js"></script>
// 这样就能使用全局变量_dll_react
- webpack.dev.conf.js 使用react.manifest.json
const webpack = require('webpack');
// 或直接const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
module.exports = {
// ...
plugins: [
new webpack.DllReferencePlugin({
manifest: require(path.resolve(__dirname_, './dist/react.manifest.json'))
})
]
}
- npm run dll一次后,再npm run dev,就不会再打包react和react-dom了
6. 开启production模式
- 开启代码压缩
- 对于vue、react项目,mode开启production模式,打包后的代码会剔除warning相关的代码,减小包体积,加快速度。同时vue也不用去检查错误,运行更快。
- production模式自动做tree shaking,去除引入模块中未使用的代码
- 注意:tree shaking只对ESM有效,对CJS无效
- ESM是静态引入,编译时引入
- CJS是动态引入,执行时引入
- TS在编译时能提示出错误.
- ESM import语句要放在top level
7. Scope Hoisting
- 用在生产环境
- 打包后文件只有一个IIFE(合并函数),mode:production已经默认合并
- webpack.prod.conf.js
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin')
plugins: [
new ModuleConcatenationPlugin()
],
// 优先引入ES6模块的代码
resolve: {
mainFields: ['jsnext: main', 'browser', 'main']
}