原文:
High-performance webpack config for front-end delivery
webpack3 指南
高性能 Webpack 配置
Scope Hoisting (作用域提升)
官方文檔:ModuleConcatenationPlugin
過去 webpack 打包時的一個取舍是將 bundle 中各個模塊單獨打包成閉包委可。這些打包函數(shù)使你的 JavaScript 在瀏覽器中處理的更慢撬码。相比之下粉怕,一些工具像 Closure Compiler 和 RollupJS 可以提升(hoist)或者預(yù)編譯所有模塊到一個閉包中攒钳,提升你的代碼在瀏覽器中的執(zhí)行速度架曹。
在生產(chǎn)環(huán)境中配置:
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
],
};
下面放一張用戶使用之后包體的對比,大概減少了50%,對于模塊數(shù)量很多的項目來說提升較大。

Minification and Uglification (壓縮和丑化)
代碼壓縮和“丑化”是生產(chǎn)環(huán)境中必不可少的厌均,然而偶爾的會遺忘,所以在部署到生產(chǎn)環(huán)境之前告唆,首先要做的就是檢查代碼是否經(jīng)過壓縮和“丑化”
錯誤的方式
直接運行 webpack
命令進(jìn)行打包棺弊,查看包體積

正確的方式
只需要在 webpack
命令后面加上 -p
參數(shù)!

通過對比可以發(fā)現(xiàn)擒悬,減少了整整 60% 的體積模她!沒有壓縮前,充斥著空格懂牧、換行侈净、注釋尊勿!
?? -p
參數(shù)不會設(shè)置 node
環(huán)境變量為生產(chǎn)環(huán)境 production
,當(dāng)你需要在生產(chǎn)環(huán)境中執(zhí)行時畜侦,可使用該命令行:NODE_ENV=production PLATFORM=web webpack -p
?? 為了快速打包元扔,可以將參數(shù)添加到 package.json
中:
"scripts": {
"build": "webpack -p"
},
高級壓縮方式
使用 UglifyjsWebpackPlugin 插件
安裝
npm i -D uglifyjs-webpack-plugin
用法
webpack.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
plugins: [
new UglifyJsPlugin()
]
}
默認(rèn)的配置已經(jīng)很好的滿足大部分的項目需求,但如何你想更進(jìn)一步壓縮旋膳,減少一部分不必要的代碼澎语,可以使用 Webpack
版本 > 3.0:
plugins:[
new webpack.optimize.UglifyJsPlugin({/* options here */}),
],
Dynamic Imports for Lazy-loaded Modules (動態(tài)引入和懶加載)
生產(chǎn)環(huán)境下的使用
使用動態(tài)引入的項目,編譯時:

可以看到之前整個的 bundle.js
文件被拆分了多個验懊,在 index.html
中只引入了 index.bundle.js
作為入口擅羞,按需加載其他被拆分的js文件


安裝配置
安裝 Babel
:
yarn add babel-loader babel-core babel-preset-env
配置 webpack.config.js
,允許 Babel
處理你的 js
文件
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/,
},
],
},
完成以上設(shè)置后义图,可以安裝處理動態(tài)引入的插件了
yarn add babel-plugin-syntax-dynamic-import
創(chuàng)建或修改 .babelrc
在項目的根目錄
{
"presets": ["env"],
"plugins": ["syntax-dynamic-import", "transform-react-jsx"]
}
改造代碼
將需要被改造成懶加載的模塊做簡單的代替:
import Home from './components/Home';
with
const Home = import('./components/Home');
最終我們的 index.js
入口文件被改造成了這樣减俏,這種 import
的方式只是一種語法糖,在一些框架中如 vue
已經(jīng)得到支持
import React from 'react';
import Async from 'react-code-splitting';
const Nav = () => (<Async load={import('./components/Nav')} />);
const Home = () => (<Async load={import('./views/home')} />);
const Countdown = () => (<Async load={import('./views/countdown')} />);
webpack.config.js
配置文件的出口和出口
entry: {
index: './index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js',
publicPath: '/',
},
Deterministic Hashes for Caching(對緩存使用確定的Hash值)
webpack
在默認(rèn)的情況下不會自動添加 hash
碱工,例如 app.8087f8d9fed812132141.js
娃承,這意味著,數(shù)據(jù)保存在緩存?zhèn)€中怕篷,當(dāng)用戶刷新時不會得到更新历筝。
在 webpack
快速添加 hashes:
output: {
filename: '[name].[hash].js',
},
?? 注意這里有一個陷阱,每一次的構(gòu)建匙头,不管文件有沒有更改都會重新聲場新的
hash
值漫谷,例如在使用webpack -p
時,即使用戶已經(jīng)已經(jīng)下載相關(guān)文件蹂析,那么講不得不刷新舔示!
?? 使用哈希值構(gòu)建,將會降低編譯的速度电抚,請在生產(chǎn)環(huán)境中使用惕稻!
Deterministic Hashes 配置
??這里我們需要解決的一個問題是,當(dāng)內(nèi)容改變到時候用戶才會刷新緩存蝙叛,如果沒有改變俺祠,則不會去刷新緩存。
插件安裝:
yarn add chunk-manifest-webpack-plugin webpack-chunk-hash
chunk-manifest-webpack-plugin
: 允許導(dǎo)出一個json文件借帘,將id映射到其中蜘渣,webpack 會讀取該json,以確定需要刷新緩存的模塊
webpack-chunk-hash
: 使用自定義的(md5)代替標(biāo)準(zhǔn)的webpack 生成的 hash
修改 webpack.config.js
肺然,用于生產(chǎn)環(huán)境 :
const webpack = require('webpack');
const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
const WebpackChunkHash = require('webpack-chunk-hash');
const HtmlWebpackPlugin = require('html-webpack-plugin');
/* Shared Dev & Production */
const config = {
/* … our webpack config up until now */
plugins: [
// /* other plugins here */
//
// /* Uncomment to enable automatic HTML generation */
// new HtmlWebpackPlugin({
// inlineManifestWebpackName: 'webpackManifest',
// template: require('html-webpack-template'),
// }),
],
};
/* Production 指定了生產(chǎn)環(huán)境中使用 */
if (process.env.NODE_ENV === 'production') {
config.output.filename = '[name].[chunkhash].js';
config.plugins = [
...config.plugins, // ES6 array destructuring, available in Node 5+
new webpack.HashedModuleIdsPlugin(),
new WebpackChunkHash(),
new ChunkManifestPlugin({
filename: 'chunk-manifest.json',
manifestVariable: 'webpackManifest',
inlineManifest: true,
}),
];
}
module.exports = config;
?? Tip
對于上面的例子蔫缸,當(dāng)你使用 yarn add html-webpack-plugin html-webpack-template
模板時,會自動的添加注釋际起,如果沒有使用 webpack 的 HTML 模板拾碌,則需要手動的引入吐葱,引入方式為:
<head>
<script>
//<![CDATA[
window.webpackManifest = { /* contents of chunk-manifest.json */ };
//]]>
</script>
</head>
同樣的 manifest.js
也需要引入,當(dāng)你設(shè)置好了這兩個文件校翔,那么一切準(zhǔn)備就緒弟跑。
CommonsChunkPlugin for Vendor Caching
CommonsChunkPlugin :
CommonsChunkPlugin
插件,是一個可選的用于建立一個獨立文件(又稱作 chunk)的功能防症,這個文件包括多個入口chunk
的公共模塊孟辑。通過將公共模塊拆出來,最終合成的文件能夠在最開始的時候加載一次告希,便存起來到緩存中供后續(xù)使用扑浸。這個帶來速度上的提升烧给,因為瀏覽器會迅速將公共的代碼從緩存中取出來燕偶,而不是每次訪問一個新頁面時,再去加載一個更大的文件础嫡。
一行代碼的簡單配置:
module.exports = {
entry: {
app: './app.js',
vendor: ['react', 'react-dom', 'react-router'],
},
};
然后運行 webpack -p

但是這種配置有個問題指么,這里的 'react', 'react-dom', 'react-router'
, 會同時打包到 index.bundle.js
和 vendor.bundle.js
中
要解決上述問題榴鼎,我們需要使用 CommonsChunkPlugin 插件伯诬,將文件分離出來,在 webpack.config.js
中的配置
const webpack = require('webpack');
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
}),
],
?? 需要注意的是巫财,插件中的name 要是 入口的名字匹配
配置完成之后盗似,重新打包,發(fā)現(xiàn)包體被拆分了平项!

Offline Plugin for webpack(PWA)
使用offline-plugin搭配webpack輕松實現(xiàn)PWA
此插件用于實現(xiàn)PWA赫舒,新技術(shù),是否需要使用和探索闽瓢,這里記錄配置的方法接癌,這并不復(fù)雜
安裝:
yarn add offline-plugin
添加到r= webpack config
:
const OfflinePlugin = require('offline-plugin');
module.exports = {
entry: {
// Adding to vendor recommended, but optional
vendor: ['offline-plugin/runtime', /* … */],
},
plugins: [
new OfflinePlugin({
AppCache: false,
ServiceWorker: { events: true },
}),
],
};
然后,在 app
中的入口文件扣讼,在開始渲染之前使用:
/* index.js */
if (process.env.NODE_ENV === 'production') {
const runtime = require('offline-plugin/runtime');
runtime.install({
onUpdateReady() {
runtime.applyUpdate();
},
onUpdated() {
window.location.reload();
},
});
}
webpack Bundle Analyzer(包分析工具)

yarn add --dev webpack-bundle-analyzer
添加到開發(fā)環(huán)境
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
config = { /* shared webpack config */ };
if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
config.plugins = [
...config.plugins,
new BundleAnalyzerPlugin(),
];
}
運行并監(jiān)聽 8888 端口
node_module/.bin/webpack --profile --json > stats.json
Multi-entry Automatic CommonsChunk Plugin(多頁面應(yīng)用程序)
應(yīng)用場景不多缺猛,只記錄下來。
webpack.config.js
const config = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
};
這是什么椭符?我們告訴 webpack 需要 3 個獨立分離的依賴圖荔燎。
為什么?在多頁應(yīng)用中销钝,(譯注:每當(dāng)頁面跳轉(zhuǎn)時)服務(wù)器將為你獲取一個新的 HTML 文檔有咨。頁面重新加載新文檔,并且資源被重新下載曙搬。然而摔吏,這給了我們特殊的機(jī)會去做很多事:
使用 CommonsChunkPlugin 為每個頁面間的應(yīng)用程序共享代碼創(chuàng)建 bundle鸽嫂。由于入口起點增多,多頁應(yīng)用能夠復(fù)用入口起點之間的大量代碼/模塊征讲,從而可以極大地從這些技術(shù)中受益据某。
We can update CommonsChunk to just figure things out automatically:
/* Dev & Production */
new webpack.optimize.CommonsChunkPlugin({
name: 'commons',
minChunks: 2,
}),
/* Production */
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity,
}),