iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 27
0
Software Development

今晚我想來點 Express 佐 MVC 分層架構系列 第 27

[今晚我想來點 Express 佐 MVC 分層架構] DAY 27 - 用 Webpack 打包 Express

Webpack 是什麼?

https://ithelp.ithome.com.tw/upload/images/20201012/20119338bEHdErt8M8.png
圖片來源

Webpack 是一個打包工具,經常用於前端領域,能夠將各個依賴的檔案進行 bundle, 更提供了預處理的功能,使 sassES6 等被翻譯成瀏覽器看得懂的樣子。

為什麼要用 Webpack?

事實上後端與前端不同,大部分情況下 後端是沒有打包的必要,也就是說使用 Webpack 打包後端並不是必要條件,但我會特地寫一篇關於 Webpack 打包 Express 是有原因的,請大家先試著建置 production 版本並跑起來看看吧!

要記得先完善 production.env 的內容呦!

npm start

咦?怎麼會出錯?
https://ithelp.ithome.com.tw/upload/images/20200916/20119338Kk3fNpqqxI.png

這個 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 結合

在開發過程中一定會有熱重載的需求,也就會想起我們的好朋友 nodemon,但要如何跟 Webpack 整合呢?就要安裝 nodemon-webpack-plugin

npm install nodemon-webpack-plugin --save-dev

並在 webpack.config.jsplugins 下使用:

plugins: [
  new webpack.DefinePlugin(processEnv),
  new NodemonPlugin()
]

修改 tsconfig.json

由於有些操作權已經交給 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"]  //不納入編譯範圍
}

修改 package.json

我們要來調整 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 打包後端雖然不是必要,但也是種選擇,我認為能夠穩定、方便開發與容易維護才是最重要的,要不要使用這個方法全看需求而定,各位有什麼看法呢?歡迎在下面討論!


上一篇
[今晚我想來點 Express 佐 MVC 分層架構] DAY 26 - Validator 與 Pipe
下一篇
[今晚我想來點 Express 佐 MVC 分層架構] DAY 28 - node.js 與線程 (上)
系列文
今晚我想來點 Express 佐 MVC 分層架構30

尚未有邦友留言

立即登入留言