Webpack 是一個打包工具,經常用於前端領域,能夠將各個依賴的檔案進行 bundle, 更提供了預處理的功能,使 sass
、ES6
等被翻譯成瀏覽器看得懂的樣子。
事實上後端與前端不同,大部分情況下 後端是沒有打包的必要,也就是說使用 Webpack 打包後端並不是必要條件,但我會特地寫一篇關於 Webpack 打包 Express 是有原因的,請大家先試著建置 production 版本並跑起來看看吧!
要記得先完善 production.env 的內容呦!
npm start
咦?怎麼會出錯?
這個 secret
有點眼熟,沒錯,就是 JWT 要使用的值,這部分我們是將它寫成環境變數去讀取,表示環境變數的讀取有問題,可以發現編譯後的檔案不包含 environments
下的所有檔案,原因是 TypeScript Compiler 本身只編譯 TS ,那要怎麼把環境變數檔搬過去呢?最土法煉鋼的方法就是寫腳本複製一份到 dist
資料夾下,但還有另一條路可以走,就是使用 Webpack。
Webpack 對環境變數的處理 不是 將環境變數檔的內容放入環境變數來 讀取,而是將使用到環境變數檔內容的程式碼片段 抽換 成這些環境變數的值,什麼意思呢?以 JWT_SIGN
這個環境變數為例,以下是 JWT_SIGN
的範例內容:
JWT_SIGN=zxcdsaqwerfv
在沒有經過預處理的情況下是透過 process.env.JWT_SIGN
去讀取,並從記憶體中取得 zxcdsaqwerfv 這串字;經過預處理後,是會把程式碼中出現 process.env.JWT_SIGN
的部分直接變成 zxcdsaqwerfv 這串字,這樣的好處是 不用一直搬移檔案 ,也 不需要花時間與空間去讀取環境變數。
先透過 npm 安裝 Webpack 與 Webpack CLI:
npm install webpack webpack-cli --save-dev
安裝完畢後,還需要再安裝 ts-loader,原因是要讓 Webpack 知道該以什麼樣的方式去讀取檔案並編譯:
npm install ts-loader --save-dev
如果不希望把 node_modules
這個黑洞 一起打包的話,強烈建議安裝 webpack-node-externals 這個套件:
npm install webpack-node-externals --save-dev
一切準備就緒!在專案目錄下新增 webpack.config.js
,裡面的內容即為 Webpack 的配置:
const path = require('path');
const webpack = require('webpack');
const dotenv = require('dotenv');
const nodeExternals = require('webpack-node-externals');
module.exports = (env, argv) => {
// 取得環境變數檔的內容,並依照 mode 來切換模式
const params = dotenv.config({ path: path.resolve(__dirname, `./src/environments/${argv.mode}.env`) }).parsed;
// 將環境變數的設置寫入 Object 中,以方便後續調用
const processEnv = {};
Object.keys(params).forEach(key => processEnv[`process.env.${key}`] = JSON.stringify(params[key]));
return {
entry: './src/index.ts', // 載入點
target: 'node', // 使用 node
externals: [
nodeExternals() // 排除 node_modules
],
module: {
rules: [
{
test: /\.ts$/, // .ts 的檔案用 ts-loader 讀取
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.ts', '.js'] // 補足 import 檔案的結尾
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.DefinePlugin(processEnv) // 設置環境變數
]
}
};
在開發過程中一定會有熱重載的需求,也就會想起我們的好朋友 nodemon,但要如何跟 Webpack 整合呢?就要安裝 nodemon-webpack-plugin:
npm install nodemon-webpack-plugin --save-dev
並在 webpack.config.js
的 plugins
下使用:
plugins: [
new webpack.DefinePlugin(processEnv),
new NodemonPlugin()
]
由於有些操作權已經交給 Webpack,這邊就可以把部分 TypeScript 的配置關閉:
{
"compilerOptions": {
"incremental": true, // 啟用增量編譯
"target": "ES2017", // 編譯成指定的 JavaScript 版本
"module": "commonjs", // 指定編譯成何種模組
"declaration": false, // 產生 '.d.ts' 檔
"sourceMap": false, // 產生 '.map' 檔
"rootDir": "./src", // 載入點的位置
"removeComments": true, // 移除註解
"strict": true, // 採用嚴格模式
"baseUrl": "./src", // 指定匯入檔案的基準路徑
"esModuleInterop": true, // 兼容模組
"experimentalDecorators": true, // 啟用裝飾器
"emitDecoratorMetadata": true // 提供裝飾器 metadata
},
"include": ["src/**/*.ts"], // 納入編譯範圍
"exclude": ["node_modules", "dist"] //不納入編譯範圍
}
我們要來調整 npm script 的配置,如下:
"scripts": {
"start": "NODE_ENV=production node ./dist/main.bundle.js",
"start:dev": "webpack --mode=development --watch",
"start:prod": "webpack --mode=production --watch",
"build": "webpack --mode=production"
}
接著只要視情況使用就可以囉!
用 Webpack 打包後端雖然不是必要,但也是種選擇,我認為能夠穩定、方便開發與容易維護才是最重要的,要不要使用這個方法全看需求而定,各位有什麼看法呢?歡迎在下面討論!