webpack 最佳配置指北(推薦)
前言
對(duì)于入門選手來講,webpack 配置項(xiàng)很多很重,如何快速配置一個(gè)可用于線上環(huán)境的 webpack 就是一件值得思考的事情。其實(shí)熟悉 webpack 之后會(huì)發(fā)現(xiàn)很簡(jiǎn)單,基礎(chǔ)的配置可以分為以下幾個(gè)方面: entry 、 output 、 mode 、 resolve 、 module 、 optimization 、 plugin 、 source map 、 performance 等,本文就來重點(diǎn)分析下這些部分。
內(nèi)有一張webpack零配置對(duì)比圖片,在公眾號(hào)后臺(tái)回復(fù)【webpack】免費(fèi)領(lǐng)取pdf文件。
一、配置入口 entry
1、單入口和多入口
將源文件加入到 webpack 構(gòu)建流程,可以是單入口:
module.exports = {
entry: `./index.js`,
}
構(gòu)建包名稱 [name] 為 main ;
或多入口:
module.exports = {
entry: {
"index": `./index.js`,
},
}
key:value 鍵值對(duì)的形式:
- key:構(gòu)建包名稱,即
[name],在這里為index - value:入口路徑
入口決定 webapck 從哪個(gè)模塊開始生成依賴關(guān)系圖(構(gòu)建包),每一個(gè)入口文件都對(duì)應(yīng)著一個(gè)依賴關(guān)系圖。
2. 動(dòng)態(tài)配置入口文件
動(dòng)態(tài)打包所有子項(xiàng)目
當(dāng)構(gòu)建項(xiàng)目包含多個(gè)子項(xiàng)目時(shí),每次增加一個(gè)子系統(tǒng)都需要將入口文件寫入 webpack 配置文件中,其實(shí)我們讓webpack 動(dòng)態(tài)獲取入口文件,例如:
// 使用 glob 等工具使用若干通配符,運(yùn)行時(shí)獲得 entry 的條目
module.exports = {
entry: glob.sync('./project/**/index.js').reduce((acc, path) => {
const entry = path.replace('/index.js', '')
acc[entry] = path
return acc
}, {}),
}
則會(huì)將所有匹配 ./project/**/index.js 的文件作為入口文件進(jìn)行打包,如果你想要增加一個(gè)子項(xiàng)目,僅僅需要在 project 創(chuàng)建一個(gè)子項(xiàng)目目錄,并創(chuàng)建一個(gè) index.js 作為入口文件即可。
這種方式比較適合入口文件不集中且較多的場(chǎng)景。
動(dòng)態(tài)打包某一子項(xiàng)目
在構(gòu)建多系統(tǒng)應(yīng)用或組件庫時(shí),我們每次打包可能僅僅需要打包某一模塊,此時(shí),可以通過命令行的形式請(qǐng)求打印某一模塊,例如:
npm run build --project components
在打包的時(shí)候解析命令行參數(shù):
// 解析命令行參數(shù)
const argv = require('minimist')(process.argv.slice(2))
// 項(xiàng)目
const project = argv['project'] || 'index'
然后配置入口:
module.exports = {
entry: {
"index": `./${project}/index.js`,
}
}
相當(dāng)于:
module.exports = {
entry: {
"index": `./components/index.js`,
}
}
當(dāng)然,你可以傳入其它參數(shù),也可以應(yīng)用于多個(gè)地方,例如 resolve.alias 中。
二、配置出口 output
用于告知 webpack 如何構(gòu)建編譯后的文件,可以自定義輸出文件的位置和名稱:
module.exports = {
output: {
// path 必須為絕對(duì)路徑
// 輸出文件路徑
path: path.resolve(__dirname, '../../dist/build'),
// 包名稱
filename: "[name].bundle.js",
// 或使用函數(shù)返回名(不常用)
// filename: (chunkData) => {
// return chunkData.chunk.name === 'main' ? '[name].js': '[name]/[name].js';
// },
// 塊名,公共塊名(非入口)
chunkFilename: '[name].[chunkhash].bundle.js',
// 打包生成的 index.html 文件里面引用資源的前綴
// 也為發(fā)布到線上資源的 URL 前綴
// 使用的是相對(duì)路徑,默認(rèn)為 ''
publicPath: '/',
}
}
在 webpack4 開發(fā)模式下,會(huì)默認(rèn)啟動(dòng) output.pathinfo ,它會(huì)輸出一些額外的注釋信息,對(duì)項(xiàng)目調(diào)試非常有用,尤其是使用 eval devtool 時(shí)。
filename : [name] 為 entry 配置的 key ,除此之外,還可以是 [id] (內(nèi)部塊 id )、 [hash] 、 [contenthash] 等。
1. 瀏覽器緩存與 hash 值
對(duì)于我們開發(fā)的每一個(gè)應(yīng)用,瀏覽器都會(huì)對(duì)靜態(tài)資源進(jìn)行緩存,如果我們更新了靜態(tài)資源,而沒有更新靜態(tài)資源名稱(或路徑),瀏覽器就可能因?yàn)榫彺娴膯栴}獲取不到更新的資源。在我們使用 webpack 進(jìn)行打包的時(shí)候,webpack 提供了 hash 的概念,所以我們可以使用 hash 來打包。
在定義包名稱(例如 chunkFilename 、 filename ),我們一般會(huì)用到哈希值,不同的哈希值使用的場(chǎng)景不同:
hash
build-specific, 哈希值對(duì)應(yīng)每一次構(gòu)建( Compilation ),即每次編譯都不同,即使文件內(nèi)容都沒有改變,并且所有的資源都共享這一個(gè)哈希值,此時(shí),瀏覽器緩存就沒有用了, 可以用在開發(fā)環(huán)境,生產(chǎn)環(huán)境不適用。
chunkhash
chunk-specific, 哈希值對(duì)應(yīng)于 webpack 每個(gè)入口點(diǎn),每個(gè)入口都有自己的哈希值。如果在某一入口文件創(chuàng)建的關(guān)系依賴圖上存在文件內(nèi)容發(fā)生了變化,那么相應(yīng)入口文件的 chunkhash 才會(huì)發(fā)生變化, 適用于生產(chǎn)環(huán)境
contenthash
content-specific,根據(jù)包內(nèi)容計(jì)算出的哈希值,只要包內(nèi)容不變, contenthash 就不變, 適用于生產(chǎn)環(huán)境
但我們會(huì)發(fā)現(xiàn),有時(shí)內(nèi)容沒有變更,打包時(shí) [contenthash] 反而變更了的問題,
webpack 也允許哈希的切片。如果你寫 [hash:8] ,那么它會(huì)獲取哈希值的前 8 位。
注意:
- 盡量在生產(chǎn)環(huán)境使用哈希
- 按需加載的塊不受
filename影響,受chunkFilename影響 - 使用
hash/chunkhash/contenthash一般會(huì)配合html-webpack-plugin(創(chuàng)建 html ,并捆綁相應(yīng)的打包文件) 、clean-webpack-plugin(清除原有打包文件) 一起使用。
2. 打包成庫
當(dāng)使用 webapck 構(gòu)建一個(gè)可以被其它模塊引用的庫時(shí):
module.exports = {
output: {
// path 必須為絕對(duì)路徑
// 輸出文件路徑
path: path.resolve(__dirname, '../../dist/build'),
// 包名稱
filename: "[name].bundle.js",
// 塊名,公共塊名(非入口)
chunkFilename: '[name].[chunkhash].bundle.js',
// 打包生成的 index.html 文件里面引用資源的前綴
// 也為發(fā)布到線上資源的 URL 前綴
// 使用的是相對(duì)路徑,默認(rèn)為 ''
publicPath: '/',
// 一旦設(shè)置后該 bundle 將被處理為 library
library: 'webpackNumbers',
// export 的 library 的規(guī)范,有支持 var, this, commonjs,commonjs2,amd,umd
libraryTarget: 'umd',
}
}
三、配置模式 mode(webpack4)
設(shè)置 mode ,可以讓 webpack 自動(dòng)調(diào)起相應(yīng)的內(nèi)置優(yōu)化。
module.exports = {
// 可以是 none、development、production
// 默認(rèn)為 production
mode: 'production'
}
或在命令行里配置:
"build:prod": "webpack --config config/webpack.prod.config.js --mode production"
在設(shè)置了 mode 之后,webpack4 會(huì)同步配置 process.env.NODE_ENV 為 development 或 production 。
webpack4 最引人注目的主要是:
- 減小編譯時(shí)間
打包時(shí)間減小了超過 60%
- 零配置
我們可以在沒有任何配置文件的情況下將 webpack 用于各種項(xiàng)目
webpack4 支持零配置使用,這里的零配置就是指, mode 以及 entry (默認(rèn)為 src/index.js )都可以通過入口文件指定,并且 webpack4 針對(duì)對(duì)不同的 mode 內(nèi)置相應(yīng)的優(yōu)化策略。
1. production
配置:
// webpack.prod.config.js
module.exports = {
mode: 'production',
}
相當(dāng)于默認(rèn)內(nèi)置了:
// webpack.prod.config.js
module.exports = {
performance: {
// 性能設(shè)置,文件打包過大時(shí),會(huì)報(bào)警告
hints: 'warning'
},
output: {
// 打包時(shí),在包中不包含所屬模塊的信息的注釋
pathinfo: false
},
optimization: {
// 不使用可讀的模塊標(biāo)識(shí)符進(jìn)行調(diào)試
namedModules: false,
// 不使用可讀的塊標(biāo)識(shí)符進(jìn)行調(diào)試
namedChunks: false,
// 設(shè)置 process.env.NODE_ENV 為 production
nodeEnv: 'production',
// 標(biāo)記塊是否是其它塊的子集
// 控制加載塊的大?。虞d較大塊時(shí),不加載其子集)
flagIncludedChunks: true,
// 標(biāo)記模塊的加載順序,使初始包更小
occurrenceOrder: true,
// 啟用副作用
sideEffects: true,
// 確定每個(gè)模塊的使用導(dǎo)出,
// 不會(huì)為未使用的導(dǎo)出生成導(dǎo)出
// 最小化的消除死代碼
// optimization.usedExports 收集的信息將被其他優(yōu)化或代碼生成所使用
usedExports: true,
// 查找模塊圖中可以安全的連接到其它模塊的片段
concatenateModules: true,
// SplitChunksPlugin 配置項(xiàng)
splitChunks: {
// 默認(rèn) webpack4 只會(huì)對(duì)按需加載的代碼做分割
chunks: 'async',
// 表示在壓縮前的最小模塊大小,默認(rèn)值是30kb
minSize: 30000,
minRemainingSize: 0,
// 旨在與HTTP/2和長(zhǎng)期緩存一起使用
// 它增加了請(qǐng)求數(shù)量以實(shí)現(xiàn)更好的緩存
// 它還可以用于減小文件大小,以加快重建速度。
maxSize: 0,
// 分割一個(gè)模塊之前必須共享的最小塊數(shù)
minChunks: 1,
// 按需加載時(shí)的最大并行請(qǐng)求數(shù)
maxAsyncRequests: 6,
// 入口的最大并行請(qǐng)求數(shù)
maxInitialRequests: 4,
// 界定符
automaticNameDelimiter: '~',
// 塊名最大字符數(shù)
automaticNameMaxLength: 30,
cacheGroups: { // 緩存組
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
},
// 當(dāng)打包時(shí),遇到錯(cuò)誤編譯,將不會(huì)把打包文件輸出
// 確保 webpack 不會(huì)輸入任何錯(cuò)誤的包
noEmitOnErrors: true,
checkWasmTypes: true,
// 使用 optimization.minimizer || TerserPlugin 來最小化包
minimize: true,
},
plugins: [
// 使用 terser 來優(yōu)化 JavaScript
new TerserPlugin(/* ... */),
// 定義環(huán)境變量
new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
// 預(yù)編譯所有模塊到一個(gè)閉包中,提升代碼在瀏覽器中的執(zhí)行速度
new webpack.optimize.ModuleConcatenationPlugin(),
// 在編譯出現(xiàn)錯(cuò)誤時(shí),使用 NoEmitOnErrorsPlugin 來跳過輸出階段。
// 這樣可以確保輸出資源不會(huì)包含錯(cuò)誤
new webpack.NoEmitOnErrorsPlugin()
]
}
2. development
配置:
// webpack.dev.config.js
module.exports = {
mode: 'development',
}
相當(dāng)于默認(rèn)內(nèi)置了:
// webpack.dev.config.js
module.exports = {
devtool: 'eval',
cache: true,
performance: {
// 性能設(shè)置,文件打包過大時(shí),不報(bào)錯(cuò)和警告,只做提示
hints: false
},
output: {
// 打包時(shí),在包中包含所屬模塊的信息的注釋
pathinfo: true
},
optimization: {
// 使用可讀的模塊標(biāo)識(shí)符進(jìn)行調(diào)試
namedModules: true,
// 使用可讀的塊標(biāo)識(shí)符進(jìn)行調(diào)試
namedChunks: true,
// 設(shè)置 process.env.NODE_ENV 為 development
nodeEnv: 'development',
// 不標(biāo)記塊是否是其它塊的子集
flagIncludedChunks: false,
// 不標(biāo)記模塊的加載順序
occurrenceOrder: false,
// 不啟用副作用
sideEffects: false,
usedExports: false,
concatenateModules: false,
splitChunks: {
hidePathInfo: false,
minSize: 10000,
maxAsyncRequests: Infinity,
maxInitialRequests: Infinity,
},
// 當(dāng)打包時(shí),遇到錯(cuò)誤編譯,仍把打包文件輸出
noEmitOnErrors: false,
checkWasmTypes: false,
// 不使用 optimization.minimizer || TerserPlugin 來最小化包
minimize: false,
removeAvailableModules: false
},
plugins: [
// 當(dāng)啟用 HMR 時(shí),使用該插件會(huì)顯示模塊的相對(duì)路徑
// 建議用于開發(fā)環(huán)境
new webpack.NamedModulesPlugin(),
// webpack 內(nèi)部維護(hù)了一個(gè)自增的 id,每個(gè) chunk 都有一個(gè) id。
// 所以當(dāng)增加 entry 或者其他類型 chunk 的時(shí)候,id 就會(huì)變化,
// 導(dǎo)致內(nèi)容沒有變化的 chunk 的 id 也發(fā)生了變化
// NamedChunksPlugin 將內(nèi)部 chunk id 映射成一個(gè)字符串標(biāo)識(shí)符(模塊的相對(duì)路徑)
// 這樣 chunk id 就穩(wěn)定了下來
new webpack.NamedChunksPlugin(),
// 定義環(huán)境變量
new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
]
}
3. none
不進(jìn)行任何默認(rèn)優(yōu)化選項(xiàng)。
配置:
// webpack.com.config.js
module.exports = {
mode: 'none',
}
相當(dāng)于默認(rèn)內(nèi)置了:
// webpack.com.config.js
module.exports = {
performance: {
// 性能設(shè)置,文件打包過大時(shí),不報(bào)錯(cuò)和警告,只做提示
hints: false
},
optimization: {
// 不標(biāo)記塊是否是其它塊的子集
flagIncludedChunks: false,
// 不標(biāo)記模塊的加載順序
occurrenceOrder: false,
// 不啟用副作用
sideEffects: false,
usedExports: false,
concatenateModules: false,
splitChunks: {
hidePathInfo: false,
minSize: 10000,
maxAsyncRequests: Infinity,
maxInitialRequests: Infinity,
},
// 當(dāng)打包時(shí),遇到錯(cuò)誤編譯,仍把打包文件輸出
noEmitOnErrors: false,
checkWasmTypes: false,
// 不使用 optimization.minimizer || TerserPlugin 來最小化包
minimize: false,
},
plugins: []
}
4. production、 development、none 總結(jié)
production 模式下給你更好的用戶體驗(yàn):
- 較小的輸出包體積
- 瀏覽器中更快的代碼執(zhí)行速度
- 忽略開發(fā)中的代碼
- 不公開源代碼或文件路徑
- 易于使用的輸出資產(chǎn)
development 模式會(huì)給予你最好的開發(fā)體驗(yàn):
- 瀏覽器調(diào)試工具
- 快速增量編譯可加快開發(fā)周期
- 運(yùn)行時(shí)提供有用的錯(cuò)誤消息
盡管 webpack4 在盡力讓零配置做到更多,但仍然是有限度的,大多數(shù)情況下還是需要一個(gè)配置文件。我們可以在項(xiàng)目的初期使用零配置,在后期業(yè)務(wù)復(fù)雜的時(shí)候再配置。
5. 環(huán)境變量 process.env.NODE_ENV
第三方框架或庫,以及我們的業(yè)務(wù)代碼,都會(huì)針對(duì)不同的環(huán)境配置,執(zhí)行不同的邏輯代碼,例如:
我們可以通過以下方式定義環(huán)境變量:
方法一:webpack4 中 mode: 'production' 已經(jīng)默認(rèn)配置了 process.env.NODE_ENV = 'production' ,所以 webapck4 可以不定義
盡管 webpack4 中定義 mode 會(huì)自動(dòng)配置 process.env.NODE_ENV ,那么我們就不需要手動(dòng)配置環(huán)境變量了嗎?
其實(shí)不然, mode 只可以定義成 development 或 production ,而在項(xiàng)目中,我們不僅僅只有開發(fā)或生產(chǎn)環(huán)境,很多情況下需要配置不同的環(huán)境(例如測(cè)試環(huán)境),此時(shí)我們就需要手動(dòng)配置其它環(huán)境變量(例如測(cè)試環(huán)境,就需要定義 process.env.NODE_ENV 為 'test' ),你可以采取以下方式:
方法二:webpack.DefinePlugin
// webpack編譯過程中設(shè)置全局變量process.env
new webpack.DefinePlugin({
'process.env': require('../config/dev.env.js')
}
config/prod.env.js :
module.exports ={
// 或 '"production"' ,環(huán)境變量的值需要是一個(gè)由雙引號(hào)包裹的字符串
NODE_ENV: JSON.stringify('production')
}
方法三:webpack 命令時(shí), NODE_ENV=development
在 window 中配置 NODE_ENV=production 可能會(huì)卡住,所以使用 cross-env:
cross-env NODE_ENV=production webpack --config webpack.config.prod.js
方法四:使用 new webpack.EnvironmentPlugin(['NODE_ENV'])
EnvironmentPlugin 是一個(gè)通過 webpack.DefinePlugin 來設(shè)置 process.env 環(huán)境變量的快捷方式。
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
});
注意:上面其實(shí)是給 NODE_ENV 設(shè)置一個(gè)默認(rèn)值 'production' ,如果其它地方有定義 process.env.NODE_ENV ,則該默認(rèn)值無效。
四、配置解析策略 resolve
自定義尋找依賴模塊時(shí)的策略(例如 import _ from 'lodash' ):
module.exports = {
resolve: {
// 設(shè)置模塊導(dǎo)入規(guī)則,import/require時(shí)會(huì)直接在這些目錄找文件
// 可以指明存放第三方模塊的絕對(duì)路徑,以減少尋找,
// 默認(rèn) node_modules
modules: [path.resolve(`${project}/components`), 'node_modules'],
// import導(dǎo)入時(shí)省略后綴
// 注意:盡可能的減少后綴嘗試的可能性
extensions: ['.js', '.jsx', '.react.js', '.css', '.json'],
// import導(dǎo)入時(shí)別名,減少耗時(shí)的遞歸解析操作
alias: {
'@components': path.resolve(`${project}/components`),
'@style': path.resolve('asset/style'),
},
// 很多第三方庫會(huì)針對(duì)不同的環(huán)境提供幾份代碼
// webpack 會(huì)根據(jù) mainFields 的配置去決定優(yōu)先采用那份代碼
// 它會(huì)根據(jù) webpack 配置中指定的 target 不同,默認(rèn)值也會(huì)有所不同
mainFields: ['browser', 'module', 'main'],
},
}
五、配置解析和轉(zhuǎn)換文件的策略 module
決定如何處理項(xiàng)目中不同類型的模塊,通常是配置 module.rules 里的 Loader:
module.exports = {
module: {
// 指明 webpack 不去解析某些內(nèi)容,該方式有助于提升 webpack 的構(gòu)建性能
noParse: /jquery/,
rules: [
{
// 這里編譯 js、jsx
// 注意:如果項(xiàng)目源碼中沒有 jsx 文件就不要寫 /\.jsx?$/,提升正則表達(dá)式性能
test: /\.(js|jsx)$/,
// 指定要用什么 loader 及其相關(guān) loader 配置
use: {
loader: "babel-loader",
options: {
// babel-loader 支持緩存轉(zhuǎn)換出的結(jié)果,通過 cacheDirectory 選項(xiàng)開啟
// 使用 cacheDirectory 選項(xiàng)將 babel-loader 的速度提高2倍
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
compact: true,
}
},
// 排除 node_modules 目錄下的文件
// node_modules 目錄下的文件都是采用的 ES5 語法,沒必要再通過 Babel 去轉(zhuǎn)換
exclude: /node_modules/
// 也可以配置 include:需要引入的文件
}
]
}
}
1. noParse
指明 webpack 不去解析某些內(nèi)容,該方式有助于提升 webpack 的構(gòu)建性能。
2. rules
常見的 loader 有:
babel-loader :解析 .js 和 .jsx 文件
// 配置 .babelrc
{
"presets": [
[
"@babel/preset-env",
],
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
],
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
}
],
]
}
tsx-loader :處理 ts 文件
less-loader :處理 less 文件,并將其編譯為 css
sass-loader :處理 sass、scss 文件,并將其編譯為 css
postcss-loader:
// postcss.config.js
module.exports = { // 解析CSS文件并且添加瀏覽器前綴到 CSS 內(nèi)容里
plugins: [require('autoprefixer')],
};
css-loader :處理 css 文件
style-loader :將 css 注入到 DOM
file-loader :將文件上的 import / require 解析為 url ,并將該文件輸出到輸出目錄中
url-loader :用于將文件轉(zhuǎn)換成 base64 uri 的 webpack 加載程序
html-loader :將 HTML 導(dǎo)出為字符串, 當(dāng)編譯器要求時(shí),將 HTML 最小化
更多 loaders 可查看LOADERS 。
六、配置優(yōu)化 optimization(webpack4)
webapck4 會(huì)根據(jù)你所選擇的 mode 進(jìn)行優(yōu)化,你可以手動(dòng)配置,它將會(huì)覆蓋自動(dòng)優(yōu)化,詳細(xì)配置請(qǐng)見Optimization 。
主要涉及兩方面的優(yōu)化:
- 最小化包
- 拆包
1. 最小化包
使用 optimization.removeAvailableModules 刪除已可用模塊
使用 optimization.removeEmptyChunks 刪除空模塊
使用 optimization.occurrenceOrder 標(biāo)記模塊的加載順序,使初始包更小
使用 optimization.providedExports 、 optimization.usedExports 、 concatenateModules 、 optimization.sideEffects 刪除死代碼
使用 optimization.splitChunks 提取公共包
使用 optimization.minimizer || TerserPlugin 來最小化包
2. 拆包
當(dāng)包過大時(shí),如果我們更新一小部分的包內(nèi)容,那么整個(gè)包都需要重新加載,如果我們把這個(gè)包拆分,那么我們僅僅需要重新加載發(fā)生內(nèi)容變更的包,而不是所有包,有效的利用了緩存。
拆分 node_modules
很多情況下,我們不需要手動(dòng)拆分包,可以使用 optimization.splitChunks :
const path = require('path');
module.exports = {
entry: path.resolve(__dirname, 'src/index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
},
optimization: {
splitChunks: {
// 對(duì)所有的包進(jìn)行拆分
chunks: 'all',
},
},
};
我們不必制定拆包策略, chunks: all 會(huì)自動(dòng)將 node_modules 中的所有內(nèi)容放入一個(gè)名為 vendors〜main.js 的文件中。
拆分業(yè)務(wù)代碼
module.exports = {
entry: {
main: path.resolve(__dirname, 'src/index.js'),
ProductList: path.resolve(__dirname, 'src/ProductList/ProductList.js'),
ProductPage: path.resolve(__dirname, 'src/ProductPage/ProductPage.js'),
Icon: path.resolve(__dirname, 'src/Icon/Icon.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash:8].js',
},
};
采用多入口的方式,當(dāng)有業(yè)務(wù)代碼更新時(shí),更新相應(yīng)的包即可
拆分第三方庫
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: path.resolve(__dirname, 'src/index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// 獲取第三方包名
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm 軟件包名稱是 URL 安全的,但是某些服務(wù)器不喜歡@符號(hào)
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
},
};
當(dāng)?shù)谌桨聲r(shí),僅更新相應(yīng)的包即可。
注意,當(dāng)包太多時(shí),瀏覽器會(huì)發(fā)起更多的請(qǐng)求,并且當(dāng)文件過小時(shí),對(duì)代碼壓縮也有影響。
動(dòng)態(tài)加載
現(xiàn)在我們已經(jīng)對(duì)包拆分的很徹底了,但以上的拆分僅僅是對(duì)瀏覽器緩存方面的優(yōu)化,減小首屏加載時(shí)間,實(shí)際上我們也可以使用按需加載的方式來進(jìn)一步拆分,減小首屏加載時(shí)間:
import React, { useState, useEffect } from 'react';
import './index.scss'
function Main() {
const [NeighborPage, setNeighborPage] = useState(null)
useEffect(() => {
import('../neighbor').then(({ default: component }) => {
setNeighborPage(React.createElement(component))
});
}, [])
return NeighborPage
? NeighborPage
: <div>Loading...</div>;
}
export default Main
七、配置 plugin
配置 Plugin 去處理及優(yōu)化其它的需求,
module.exports = {
plugins: [
// 優(yōu)化 require
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en|zh/),
// 用于提升構(gòu)建速度
createHappyPlugin('happy-babel', [{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', "@babel/preset-react"],
plugins: [
['@babel/plugin-proposal-class-properties', {
loose: true
}]
],
// babel-loader 支持緩存轉(zhuǎn)換出的結(jié)果,通過 cacheDirectory 選項(xiàng)開啟
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
compact: true,
}
}])
]
}
常用 plugins:
html-webpack-plugin :生成 html 文件,并將包添加到 html 中
webpack-parallel-uglify-plugin :壓縮 js(多進(jìn)程并行處理壓縮)
happypack :多線程loader,用于提升構(gòu)建速度
hard-source-webpack-plugin :為模塊提供中間緩存步驟,顯著提高打包速度
webpack-merge :合并 webpack 配置
mini-css-extract-plugin :抽離 css
optimize-css-assets-webpack-plugin :壓縮 css
add-asset-html-webpack-plugin :將 JavaScript 或 CSS 資產(chǎn)添加到 html-webpack-plugin 生成的 HTML 中
更多插件可見:plugins
八、配置devtool:source map
配置 webpack 如何生成 Source Map,用來增強(qiáng)調(diào)試過程。不同的值會(huì)明顯影響到構(gòu)建(build)和重新構(gòu)建(rebuild)的速度:
生產(chǎn)環(huán)境:默認(rèn)為 null ,一般不設(shè)置( none )或 nosources-source-map
開發(fā)環(huán)境:默認(rèn)為 eval ,一般設(shè)置為 eval 、 cheap-eval-source-map 、 cheap-module-eval-source-map
策略為:
- 使用 cheap 模式可以大幅提高 souremap 生成的效率。沒有列信息(會(huì)映射到轉(zhuǎn)換后的代碼,而不是映射到原始代碼),通常我們調(diào)試并不關(guān)心列信息,而且就算 source map 沒有列,有些瀏覽器引擎(例如 v8) 也會(huì)給出列信息。
- 使用 eval 方式可大幅提高持續(xù)構(gòu)建效率。參考官方文檔提供的速度對(duì)比表格可以看到 eval 模式的編譯速度很快。
- 使用 module 可支持 babel 這種預(yù)編譯工具(在 webpack 里做為 loader 使用)。
如果默認(rèn)的 webpack minimizer 已經(jīng)被重定義(例如 terser-webpack-plugin ),你必須提供 sourceMap:true 選項(xiàng)來啟用 source map 支持。
更多可查看:devtool
九、配置性能 performance
當(dāng)打包是出現(xiàn)超過特定文件限制的資產(chǎn)和入口點(diǎn), performance 控制 webpack 如何通知:
module.exports = {
// 配置如何顯示性能提示
performance: {
// 可選 warning、error、false
// false:性能設(shè)置,文件打包過大時(shí),不報(bào)錯(cuò)和警告,只做提示
// warning:顯示警告,建議用在開發(fā)環(huán)境
// error:顯示錯(cuò)誤,建議用在生產(chǎn)環(huán)境,防止部署太大的生產(chǎn)包,從而影響網(wǎng)頁性能
hints: false
}
}
十、配置其它
1. watch 與 watchOptions
watch
監(jiān)視文件更新,并在文件更新時(shí)重新編譯:
module.export = {
// 啟用監(jiān)聽模式
watch: true,
}
在 webpack-dev-server 和 webpack-dev-middleware 中,默認(rèn)啟用了監(jiān)視模式。
或者我們可以在命令行里啟動(dòng)監(jiān)聽( --watch ):
webpack --watch --config webpack.config.dev.js
watchOptions
module.export = {
watch: true,
// 自定義監(jiān)視模式
watchOptions: {
// 排除監(jiān)聽
ignored: /node_modules/,
// 監(jiān)聽到變化發(fā)生后,延遲 300ms(默認(rèn)) 再去執(zhí)行動(dòng)作,
// 防止文件更新太快導(dǎo)致重新編譯頻率太高
aggregateTimeout: 300,
// 判斷文件是否發(fā)生變化是通過不停的去詢問系統(tǒng)指定文件有沒有變化實(shí)現(xiàn)的
// 默認(rèn) 1000ms 詢問一次
poll: 1000
}
}
2. externals
排除打包時(shí)的依賴項(xiàng),不納入打包范圍內(nèi),例如你項(xiàng)目中使用了 jquery ,并且你在 html 中引入了它,那么在打包時(shí)就不需要再把它打包進(jìn)去:
<script src="/a1/uploads/allimg/200110/143P41150-0.jpg" integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=" crossorigin="anonymous"> </script>
配置:
module.exports = {
// 打包時(shí)排除 jquery 模塊
externals: {
jquery: 'jQuery'
}
};
3.target
構(gòu)建目標(biāo),用于為 webpack 指定一個(gè)環(huán)境:
module.exports = {
// 編譯為類瀏覽器環(huán)境里可用(默認(rèn))
target: 'web'
};
4. cache
緩存生成的 webpack 模塊和塊以提高構(gòu)建速度。在開發(fā)模式中,緩存設(shè)置為 type: 'memory' ,在生產(chǎn)模式中禁用。 cache: true 是 cache: {type: 'memory'} 的別名。要禁用緩存?zhèn)鬟f false :
module.exports = {
cache: false
}
在內(nèi)存中,緩存僅在監(jiān)視模式下有用,并且我們假設(shè)你在開發(fā)中使用監(jiān)視模式。在不進(jìn)行緩存的情況下,內(nèi)存占用空間較小。
5. name
配置的名稱,用于加載多個(gè)配置:
module.exports = {
name: 'admin-app'
};
十一、總結(jié)
本文僅僅是列出一些常用的配置項(xiàng),所有的配置文件架構(gòu)可見:https://github.com/webpack/webpack/blob/master/schemas/WebpackOptions.json,你也可以進(jìn)入 webpack 官網(wǎng)了解更多。
參考:
https://webpack.js.org/
https://medium.com/hackernoon/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
https://medium.com/webpack/webpack-4-mode-and-optimization-5423a6bc597a
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:Vue+abp微信掃碼登錄的實(shí)現(xiàn)代碼示例
欄 目:JavaScript
下一篇:java Bean與json對(duì)象間的轉(zhuǎn)換實(shí)例講解
本文標(biāo)題:webpack 最佳配置指北(推薦)
本文地址:http://www.jygsgssxh.com/a1/JavaScript/9280.html
您可能感興趣的文章
- 01-10使用webpack/gulp構(gòu)建TypeScript項(xiàng)目的方法示例
- 01-10Webpack設(shè)置環(huán)境變量的一些誤區(qū)詳解
- 01-10webpack打包html里面img后src為“[object Module]”問題
- 01-10Vue中axios攔截器如何單獨(dú)配置token
- 01-10Vue通過配置WebSocket并實(shí)現(xiàn)群聊功能
- 01-10使用webpack搭建vue環(huán)境的教程詳解
- 01-10nodeJs的安裝與npm全局環(huán)境變量的配置詳解
- 01-10nuxt配置通過指定IP和端口訪問的實(shí)現(xiàn)


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02javascript點(diǎn)線,點(diǎn)線的代碼
- 04-02javascript潛力,javascript強(qiáng)大嗎
- 04-02javascript替換字符串,js字符串的替換
- 04-02javascript移出,js 移入移出
- 04-02包含javascript舍的詞條
- 04-02javascript并行,深入理解并行編程 豆瓣
- 04-02javascript匿名,js匿名方法
- 04-02javascript警報(bào),JavaScript警告
- 04-02javascript遮蓋,JavaScript遮蓋PC端頁面
- 04-02javascript前身,javascript的前身
隨機(jī)閱讀
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10delphi制作wav文件的方法
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 04-02jquery與jsp,用jquery


