Webpack效能優化「六」-- 優化打包構建速度 ***
技術標籤:框架外掛工具篇
本篇講的是 Webpack 對於優化打包構建速度,也就是對於開發體驗和效率的優化。
有如下幾處可以優化:
優化 babel-loader
IgnorePlugin 避免引入無用模組
noParse 避免重複打包
happyPack //多程序打包工具
ParalleUglifyPlugin //多程序打包壓縮
自動重新整理
熱更新
DLLPlugin
1.優化 babel-loader
在babel-loader後加cacheDirectory,開啟快取,
沒有改動的es6程式碼會啟用快取,不會重新編譯
[webpack.dev.js]:
module: { rules: [{ test: /\.js$/, loader: ['babel-loader?cacheDirectory'], include: srcPath, exclude: /node_modules/ }, ] },
2.IgnorePlugin 避免引入無用模組
使用場景:
日期處理類庫moment中,包含很多種語言,預設引入所有語言的JS程式碼,程式碼體積過大;只需要使用中文,那就需要配置忽略對整個類庫的引入,手動按需引入中文。
當全部引入配置
[index.js]配置
import moment from 'moment' //全部引入
moment.locale('zh-cn') //設定語言為中文
當按需引入配置
① 忽略 moment 引用的所有語言包,忽略 moment 下的 /locale 目錄
[webpack.prod.js]配置:
plugins: [ // 忽略 moment 下的 /locale 目錄 new webpack.IgnorePlugin(/\.\/locale/, /moment/), ]
② 手動動態引入中文語言
[index.js]:
import moment from 'moment' // moment的引入被忽略
import 'moment/locale/zh-cn' //手動引入中文語言包
綜上,IgnorePlugin 避免引入無用模組,減少了打包體積,提升了打包速度。
3.noParse 避免重複打包
像vue.min.js已經模組化處理過了,需要忽略對此型別檔案的打包。
webpack.prod.js:
rules: [{
test: /\.vue$/,
loader: ['vue-loader'],
include: srcPath
}, ]
IgnorePlugin vs noParse
- IgnorePlugin 直接不引入,程式碼中沒有,減少了產出程式碼體積;
- noParse 引入了,但是未打包;
4.happyPack 多程序打包
js是單執行緒,開啟多執行緒打包,提高構建速度(特別是多核CPU)。
webpack.prod.js:
① 安裝 HappyPack,並引入:
const HappyPack = require('happypack')
② plugins中引入HappyPack規則
new HappyPack({
// 用唯一的識別符號 id 來代表當前的 HappyPack 是用來處理一類特定的檔案
id: 'babel',
// 如何處理 .js 檔案,用法和 Loader 配置中一樣
loaders: ['babel-loader?cacheDirectory']
}),
③ rules 中 配置js 轉換規則為happypack:
rules: [{
test: /\.js$/,
// 把對 .js 檔案的處理轉交給 id 為 babel 的 HappyPack 例項
use: ['happypack/loader?id=babel'],
include: srcPath,
// exclude: /node_modules/
}, ]
5.ParalleUglifyPlugin 多程序壓縮JS
JS單執行緒, 開啟多程序壓縮更快,webpack 內建 Uglify工具壓縮JS
webpack.prod.js:
① 安裝webpack-parallel-uglify-plugin 並引入配置檔案中
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
② plugins中引入ParallelUglifyPlugin規則
new ParallelUglifyPlugin({
// 傳遞給 UglifyJS 的引數
// (還是使用 UglifyJS 壓縮,只不過幫助開啟了多程序)
uglifyJS: {
output: {
beautify: false, // 最緊湊的輸出
comments: false, // 刪除所有的註釋
},
compress: {
// 刪除所有的 `console` 語句,可以相容ie瀏覽器
drop_console: true,
// 內嵌定義了但是隻用到一次的變數
collapse_vars: true,
// 提取出出現多次但是沒有定義成變數去引用的靜態值
reduce_vars: true,
}
}
})
happyPack 放在生產環境下,也可以放在本地環境下;
但是 ParallelUglifyPlugin 的壓縮就只能放在生產環境下,開發環境下沒有必要去做壓縮
專案較大, 打包較慢, 開啟多程序能提高速度
專案較小, 打包很快, 開啟多程序會降低速度( 程序開銷)
6.自動重新整理
自動重新整理用於開發環境
webpack.dev.js:
module.export = {
watch: true, // 開啟監聽,預設為 false
watchOptions: {
ignored: /node_modules/, // 忽略哪些
// 監聽到變化發生後會等300ms再去執行動作,防止檔案更新太快導致重新編譯頻率太高
// 預設為 300ms
aggregateTimeout: 300,
// 判斷檔案是否發生變化是通過不停的去詢問系統指定檔案有沒有變化實現的
// 預設每隔1000毫秒詢問一次
poll: 1000
}
}
若開發環境開啟devserver,自動重新整理便預設不開啟
7.熱更新
webpack.dev.js:
① 引用webpack的外掛HotModuleReplacementPlugin
const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');
② 配置入口
entry: {
// index: path.join(srcPath, 'index.js'),
index: [
'webpack-dev-server/client?http://localhost:8080/',
'webpack/hot/dev-server',
path.join(srcPath, 'index.js')
],
other: path.join(srcPath, 'other.js')
},
③ 在 plugins中 new HotModuleReplacementPlugin()
④ 配置 devServer中 的 hot: true
devServer: {
port: 8080,
progress: true, // 顯示打包的進度條
contentBase: distPath, // 根目錄
open: true, // 自動開啟瀏覽器
compress: true, // 啟動 gzip 壓縮
hot: true,
// 設定代理
proxy: {
// 將本地 /api/xxx 代理到 localhost:3000/api/xxx
'/api': 'http://localhost:3000',
// 將本地 /api2/xxx 代理到 localhost:3000/xxx
'/api2': {
target: 'http://localhost:3000',
pathRewrite: {
'/api2': ''
}
}
}
},
⑤ 開發環境中註冊哪些模組能觸發熱更新
index.js:
// 增加,開啟熱更新之後的程式碼邏輯
if (module.hot) {
module.hot.accept(['./math'], () => {
const sumRes = sum(10, 30)
console.log('sumRes in hot', sumRes)
})
}
自動重新整理 VS 熱更新
自動重新整理:整個網頁全部重新整理,速度較慢;狀態會丟失
熱更新:新程式碼生效,網頁不重新整理,狀態不丟失
8.DLLPlugin 動態連結庫外掛
背景:
前端框架如 vue React ,體積大,構建慢
較穩定,不常升級版本
同一個版本之構建一次就可以,不用每次都重新構建
DLLPlugin 將常用框架打包成dll檔案,然後再引用
webpack 已內建DLLPlugin支援:
- DLLPlugin 打包出 dll 檔案
- DllReferencePlugin -使用 dll 檔案
Stage1. 打包生成 dll 檔案
① 單獨配置 webpack.dll.js,用於生成dll檔案(DllPlugin外掛)
const path = require('path')
const DllPlugin = require('webpack/lib/DllPlugin')
const { srcPath, distPath } = require('./paths')
module.exports = {
mode: 'development',
// JS 執行入口檔案
entry: {
// 把 React 相關模組的放到一個單獨的動態連結庫
react: ['react', 'react-dom']
},
output: {
// 輸出的動態連結庫的檔名稱,[name] 代表當前動態連結庫的名稱,
// 也就是 entry 中配置的 react 和 polyfill
filename: '[name].dll.js',
// 輸出的檔案都放到 dist 目錄下
path: distPath,
// 存放動態連結庫的全域性變數名稱,例如對應 react 來說就是 _dll_react
// 之所以在前面加上 _dll_ 是為了防止全域性變數衝突
library: '_dll_[name]',
},
plugins: [
// 接入 DllPlugin
new DllPlugin({
// 動態連結庫的全域性變數名稱,需要和 output.library 中保持一致
// 該欄位的值也就是輸出的 manifest.json 檔案 中 name 欄位的值
// 例如 react.manifest.json 中就有 "name": "_dll_react"
name: '_dll_[name]',
// 描述動態連結庫的 manifest.json 檔案輸出時的檔名稱
path: path.join(distPath, '[name].manifest.json'),
}),
],
}
② 在package.json裡配置dll,並執行:npm run dll
package.json:
script :{
"dll":"webpack --config build/webpack.dll.js"
}
產出檔案:
react.dll.js檔案、//react打包後的dll檔案
react.manifast.json.js檔案//索引 找到對應關係
Stage2. 使用 dll 檔案
webpack.dev.js配置引入:
① 引入外掛 DllReferencePlugin
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
② 忽略 /node_modules/ 程式碼的loader
module: {
rules: [
{
test: /\.js$/,
loader: ['babel-loader'],
include: srcPath,
exclude: /node_modules/ // 第二,不要再轉換 node_modules 的程式碼
},
]
},
③ plugin配置 DllReferencePlugin 外掛,告訴 Webpack 使用了哪些動態連結庫
plugins: [
// 第三,告訴 Webpack 使用了哪些動態連結庫
new DllReferencePlugin({
// 描述 react 動態連結庫的檔案內容
manifest: require(path.join(distPath, 'react.manifest.json')),
}),
],
④ index.html 裡引用 react.dll.js:
<script src="./react.dll.js"></script>
webpack優化構建速度總結:
可用於生產環境:
- 優化 babel-loader 快取(主要用於開發)
- IgnorePlugin // 避免引入無用模組
- noParse // 避免重複打包
- happyPack // 多程序打包
- ParalleUglifyPlugin(主要用於生產環境) // 多程序壓縮JS
不能用於生產環境:
- 自動重新整理
- 熱更新
- DLLPlugin(開發)