日常吐槽
經過不斷的調整和測試,關於 react 的 webpack 配置終於新鮮出爐。本次的重點主要集中在開發環境上,生產環境則是使用 webpack 的 production 默認模式。
本次配置主要有:
- eslint+prettier;
- optimization.splitChunks;
- happypack;
- DllReferencePlugin & DllPlugin;
- ...
文檔的重要性
講真,對於初次接觸 webpack 的同學,怕的可能不是 webpack 的配置,而是長長的 package.json。依賴那麼多,你怎麼就知道需要哪些依賴呢。不開玩笑,我還真知道。
webpack 的依賴主要是一些 loader 和 plugins。我們知道單頁面引用被打包後,原有的結構基本上不復存在了。而之前引用的圖片或字體資源還按照之前的路徑查找,肯定是找不到的。那麼我們就需要轉換工具(順便轉換資源)—— url-loader
|file-loader
。
大多數人寫樣式時,喜歡使用 css、less、sass。這時也會有對應的工具 style-loader
, css-loader
, less-loader
。
想要使用 JavaScript 新特性或處理兼容性,就用 babel-loader
。以上這些基本上可以應付一些簡單的項目。可實際上呢?
我信你個鬼,你這個糟老頭壞的很!
看文檔啊,看官方介紹啊。本次也是通過看 babel 文檔,和一些依賴文檔來配置 webpack 的,全程無壓力,而且很正宗。所以,文檔很重要。
eslint+prettier
如果時團隊合作,代碼規範是很重要的。可以通過 eslint+prettier 規範。這兩個工具各有側重點,不過官網也提供了兩者結合的方案。詳細介紹見官網。我個人不習慣創建太多的配置文件,所以都放在了 package.json 文件中。
// webpack.common.js
{
enforce: "pre",
test: /\.m?jsx?$/,
exclude: /node_modules/,
loader: "eslint-loader",
options: {
fix: true,
cache: true,
formatter: require("eslint-friendly-formatter"),
}
},
"eslintConfig": {
"parser": "babel-eslint",
"env": {
"browser": true,
"es6": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"extends": [
"plugin:prettier/recommended"
]
},
"prettier": {
"singleQuote": true,
"semi": true
},
開發環境
開發環境沒什麼好說的了,簡單易配置,官網很詳細。
// webpack.dev.js
plugins: {
//...
new webpack.HotModuleReplacementPlugin()
},
devtool: "eval-source-map",
devServer: {
contentBase: path.resolve(__dirname, '..', 'dist'),
port: APP_CONFIG.port,
hot: true,
open: true
}
// index.js
// 入口處要配置這些,別忘了。
// 因爲有冒泡的機制,所以在頂端加一個就好。
if (module.hot) {
module.hot.accept('./views/login/index.js', () => {
render(App) // 渲染應用
})
}
optimization.splitChunks
這個配置是用來分割包的。在性能優化上,請求數和請求包的大小也是很重要的優化點。請求數量和請求數據大小要控制在合理的範圍內。
不過通常情況下,我們會將包分割爲內容不變的部分和內容變化的部分。這不僅僅是爲了將大的包分割成更小的包,也是爲了能夠充分利用緩存機制。
// webpack.common.js
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
verdor: {
test: /[\\/]node_modules[\\/]/,
name: 'verdors',
chunks: 'all',
priority: -10,
},
common: {
name: 'common',
chunks: 'all',
minChunks: 2,
priority: -20,
}
}
}
happypack
轉換文件算是打包過程中比較耗時的事情,通過 happypack 可以將這件事分攤給多個 node 進程,這樣就會大大縮短了打包時間(同理,可以考慮使用 thread-loader
)。不過進程之間的通信是要開銷的,這是一個優化方向,要不要採用,還需要酌情考慮。
// loader
{
test: /\.m?jsx?$/,
exclude: /node_modules/,
use: 'happypack/loader?id=js',
}
// plugins
new HappyPack({
id: 'js',
threadPool: happyThreadPool,
loaders: [{
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: [['@babel/preset-env', {
"useBuiltIns": "usage",
"corejs": 3
}], "@babel/preset-react"],
plugins: ['@babel/transform-runtime',
"@babel/plugin-proposal-class-properties", [
"import",
{
"libraryName": "antd",
"style": true
}
]
]
}
}]
})
不喜歡單獨的 babel 文件,所以 babel 的配置都在這裏了。其實,關於 babel 要配置的內容還是挺多的。不過不要怕,babel 的官方文檔有詳細說明。
DllReferencePlugin & DllPlugin
之前也提到過,通常我們會使用 optimization.splitChunks
來處理第三方庫,將其分割成不變的部分。可是,每次打包的時候都需要重複這一步驟。
這時候我們就想啊,不變的部分打包一次不就可以了麼,之後就只打包那些經常變化的部分,這樣不就能提高效率了麼?是的, DllReferencePlugin & DllPlugin
基本上要做的就是這麼一回事。所以,我們會針對這兩部分做不同的配置。
// webpack.dll.js
new webpack.DllPlugin({
context: process.cwd(),
path: path.join(__dirname, '..', 'dist', 'dll', '[name]-manifest.json'),
name: '[name]_[hash]'
})
// webpack.common.js
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require(path.resolve(__dirname, '..', 'dist', 'dll', "vendor-manifest.json"))
}),
multi-spa-webpack-cli使用說明
multi-spa-webpack-cli
已經發布到 npm,只要在 node 環境下安裝即可。一路按 Enter,全部源碼都在裏面!!!
npm install multi-spa-webpack-cli -g
使用步驟如下:
# 1. 初始化項目
multi-spa-webpack-cli init spa-project
# 2. 進入文件目錄
cd spa-project
# 3. 安裝依賴
npm install
# 4. 打包不變的部分
npm run build:dll
# 5. 啓動項目(手動打開瀏覽器:localhost:8090)
npm start
webpack 系列: