捨棄 create-react-app 之餘還架了個 astro blog 昭告天下:正式環境用的 webpack 設定
忙了幾天之後,你感覺專案稍微有模有樣了,打算先部署一版測試站讓其他人看看,所以——今天的主題就是分享個人在正式環境用的 webpack 設定。
相較於開發版,部署用的設定移除了 source map 並導入壓縮、分塊(chunking)的功能。有興趣歡迎參考,當然也很歡迎你留言分享自己的愛用設定或 plugin 唷 (・∀・)
完整設定
/* Package */
import { EsbuildPlugin } from 'esbuild-loader';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import Webpack from 'webpack';
/* Tool */
import { resolvePath } from '@/tool/resolvePath';
/* Data */
import alias from './data/alias';
import env from './data/env';
import type { WebpackConfiguration, EnvForBuildApp } from './data/types';
const envForBuildApp = env['process.env'] as EnvForBuildApp;
/* Main */
const webpackProductionConfig: WebpackConfiguration = {
mode: 'production',
entry: resolvePath('src/index'),
output: {
filename: 'static/js/[name].[contenthash:8].js',
path: resolvePath(JSON.parse(envForBuildApp.BUILD_DESTINATION)),
clean: true,
},
optimization: {
splitChunks: {
chunks: 'all',
maxSize: 20000, // 單位 bytes ,這裡是 20kb
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
},
default: {
minChunks: 10,
},
},
},
minimizer: [
new EsbuildPlugin({
target: 'es2015',
css: true,
}),
],
},
module: {
rules: [
{
test: /\.(js|ts|tsx)$/,
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'es2015',
},
},
{
test: /\.(png|svg|jpg)$/i,
type: 'asset/resource',
resourceQuery: /url/,
},
{
test: /\.svg$/i,
issuer: /\.tsx?$/,
resourceQuery: { not: [/url/] },
use: [
{
loader: '@svgr/webpack',
options: {
svgo: false,
ref: true,
},
},
],
},
{
test: /\.css$/,
exclude: /\.module\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
sourceMap: false,
},
},
],
},
{
test: /\.module\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
sourceMap: false,
modules: {
mode: 'local',
localIdentName: '[hash:base64]',
},
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: resolvePath('public/index.html'),
publicPath: '/',
}),
new Webpack.DefinePlugin({ ...env }),
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
],
resolve: {
extensions: ['.js', '.ts', '.tsx'],
alias,
},
};
export default webpackProductionConfig;
解說
Package 區塊:
此區列出部署時會用到的套件。相較於開發用設定,追加了 mini-css-extract-plugin
協助處理 css 相關內容。
Tool 區塊:
使用第 8 天寫好的小工具 resolvePath
來處理檔案路徑。
Data 區塊:
引用第 7 天 與 第 8 天處理好的 env
與 alias
內容,並透過 const envForBuildApp = env['process.env'] as EnvForBuildApp;
取用驗證過的部署用環境變數。
Main 部分:
設定 mode: production
並指定打包入口點為 entry: resolvePath('src/index')
。
在 output
內透過 path
設定最終輸出目的地,上方設定翻譯成白話文就是:透過 resolvePath()
解出環境變數 BUILD_DESTINATION
的絕對路徑,打包完畢的東西請送到這個資料夾。
而因為打算做 code splitting,所以指定輸出的 bundle 檔案名稱為 static/js/[name].[contenthash:8].js
。詳細說明可參考 webpack 5 output.filename 的內容。
設定 output.clean: true
代表每次打包前都先清空目的地資料夾。個人選擇這樣設定是因為:
- 避免任何檔案殘留影響的可能性
- 評估當下專案的靜態檔案沒有多到需要做檔案快取來加速打包流程,故選擇全刪
optimization
分成 splitChunks
與 minimizer
兩部分。分塊設定大部分參考自 webpack 5: optimization.splitChunks 內容。
設定 chunks: 'all'
代表所有的分塊都會被納入優化處理。因設定後實測打包速度還能維持 5 秒左右結束,故這裡選擇使用極限優化。
maxSize: 20000
(單位為 bytes 所以實際上代表 20kb)指示 webpack 盡量將大於這個尺寸的分塊繼續往下分解。
Using
maxSize
tells webpack to try to split chunks bigger thanmaxSize
bytes into smaller parts.
minChunks: 10
代表「至少要有 10 處以上的引用,才會被分塊」:
splitChunks.minChunks: The minimum times must a module be shared among chunks before splitting.
而 minimizer
中選擇使用既有的 EsbuildPlugin
來協助壓縮 css 輸出內容、不再另外安裝套件。完整介紹可參考官方說明。
module.rules
與開發設定不同的地方在於取消 sourceMap,並搭配 MiniCssExtractPlugin.loader
將 .css
內容獨立為一份檔案。
mini-css-extract-plugin: This plugin extracts CSS into separate files.
可看到 plugins
納入了 new MiniCssExtractPlugin({ ... })
設定。與最上方的 output
類似,這裡透過 mini-css-extract-plugin
指定 .css
檔案都要包進輸出資料夾中的 ./static/css
中。
輸出結果
執行(明天會說明細節)以上打包設定後,專案根目錄會出現一個 ./build
資料夾,結構基本如下:
./build
├── index.html
├── (輸出後的根目錄會包含一些被 index.html 引用的靜態檔案,會於後續鐵人賽中說明)
└── static
├── css
│ └── main-31743c5a.b5b18614.css
└── js
├── (省略了非常多的分塊檔案,不然內容會太長)
├── main-f16aa68b.5879c5ca.js
└── main-f6eac33a.2dac1962.js
這時已經可以透過 vs code 的 Live Server 或 npm 套件 http-server 來預覽打包成果了。
最後,謝謝你撐過今天的解說 👍