首頁>技術>

Nestjs 開發環境熱更新的方案

Nestjs 的熱更新是基於 Webpack HMR(Hot-Module Replacement) 方案

警告請注意,webpack不會自動將您的資產(例如graphql檔案)複製到dist資料夾。同樣,webpack與glob靜態路徑(例如TypeOrmModule中的實體屬性)不相容。

1 使用 CLI

如果您正在使用Nest CLI,配置過程非常簡單。CLI包裝webpack,它允許使用HotModuleReplacementPlugin。

安裝首先安裝依賴包:

$ npm i --save-dev webpack-node-externals

配置在根目錄下建立 webpack.config.js,內容如下:

const webpack = require('webpack');const nodeExternals = require('webpack-node-externals');module.exports = function(options) {  return {    ...options,    entry: ['webpack/hot/poll?100', './src/main.ts'],    watch: true,    externals: [      nodeExternals({        whitelist: ['webpack/hot/poll?100'],      }),    ],    plugins: [...options.plugins, new webpack.HotModuleReplacementPlugin()],  };}

此函式獲取包含預設webpack配置的原始物件,並返回一個修改後的物件,該物件帶有一個已應用的HotModuleReplacementPlugin外掛。

Hot-Module Repacement為了啟用HMR,開啟應用程式入口檔案(main.ts)並新增幾個與webpack相關的指令,如下所示:

declare const module: any;async function bootstrap() {  const app = await NestFactory.create(AppModule);  await app.listen(3000);  if (module.hot) {    module.hot.accept();    module.hot.dispose(() => app.close());  }}bootstrap();

在 package.json檔案中增加如下兩條指令碼

"build": "nest build --watch --webpack""start": "node dist/main",

執行如下命令

$ npm run build

Webpack 開始監聽檔案,在新的命令列視窗執行

$ npm run start
不使用 CLI

安裝

$ npm i --save-dev webpack webpack-cli webpack-node-externals ts-loader

配置建立 webpack.config.js 內容如下:

const webpack = require('webpack');const path = require('path');const nodeExternals = require('webpack-node-externals');module.exports = {  entry: ['webpack/hot/poll?100', './src/main.ts'],  watch: true,  target: 'node',  externals: [    nodeExternals({      whitelist: ['webpack/hot/poll?100'],    }),  ],  module: {    rules: [      {        test: /.tsx?$/,        use: 'ts-loader',        exclude: /node_modules/,      },    ],  },  mode: 'development',  resolve: {    extensions: ['.tsx', '.ts', '.js'],  },  plugins: [new webpack.HotModuleReplacementPlugin()],  output: {    path: path.join(__dirname, 'dist'),    filename: 'server.js',  },};

Hot-Module Replacement為了啟用HMR,開啟應用程式入口檔案(main.ts)並新增幾個與webpack相關的指令,如下所示:

declare const module: any;async function bootstrap() {  const app = await NestFactory.create(AppModule);  await app.listen(3000);  if (module.hot) {    module.hot.accept();    module.hot.dispose(() => app.close());  }}bootstrap();

在 package.json 檔案中加入以下指令碼

"webpack": "webpack --config webpack.config.js""start": "node dist/server",

執行命令

$ npm run webpack

新命令視窗下執行

$ npm run start
Typeorm 配置

由於webpack與glob靜態路徑不相容,所以要想讓 typeorm 同樣支援熱更新,正常需要靜態引入 entity 而不是利用萬用字元方式。

如:

import { Cat } from '../cat/cat.entity';@Module({    imports: [        // provides: typeorm/Connection, typeorm/EntityManager        TypeOrmModule.forRoot({            entities: [                Cat,            ],        }),    ],})export class DatabaseModule { }

但是這樣如果有很多 entity 會非常不便,幸運的是,webpack有一特性 require.context。允許收集所需檔案的上下文。那麼我們可以這樣:

// entity.context.ts (in root src folder)export const entityContext =  require.context('.', true, /\\.entity\\.ts$/);
// database.module.tsimport { entityContext } from '../entity.context';@Module({    imports: [        TypeOrmModule.forRoot({            entities: [                ...entityContext.keys().map(id => {                    const entityModule = entityContext(id);                    // We must get entity from module (commonjs)                    // Get first exported value from module (which should be entity class)                    const [entity] = Object.values(entityModule);                    return entity;                })            ],        }),    ],})export class DatabaseModule { }

但這個方案,對 production 不友好,所以還可以使用下面的方案:

import { Module } from '@nestjs/common';import { TypeOrmModule } from '@nestjs/typeorm';import { getMetadataArgsStorage } from 'typeorm';// The modules are loaded from here, hence importing the entitiesimport { AModule } from './a/a.module';import { BModule } from './b/b.module';@Module({  imports: [    AModule,     BModule,     TypeOrmModule.forRoot({       ...,      entities: getMetadataArgsStorage().tables.map(tbl => tbl.target),      migrations: ...,    }),  ]})export class AppModule {}

這個方案的思路是:

由於webpack將所有程式碼打包成一個單獨的包檔案,為了讓HMR工作,這個包檔案作為伺服器載入並執行指定實體:* dirname + '/*/。ts'將導致typeorm需要那些檔案(而實際的實體已經在webpack包中載入了)。

當typeorm試圖獲取repository時,它會將傳入的實體與從js/ts檔案中載入的實體配置進行比較(例如,從webpack包中載入使用者的getRepository)。

儘管它們有相同的名稱,但它們是從不同的模組載入的兩個不同的類(函式),因此typeorm將無法找到正確的類。

我的解決方案是基於所有模組都已載入的事實,因此應該已經通過匯入載入了所有實體。對於結構良好的專案,NestJS尤其如此。具體來說,對於每個模組,要麼模組本身,要麼控制器將匯入實體到某個地方。

通過利用@Entity裝飾器的內部機制,我們將能夠獲得實體類的列表。

不確定這個是最佳方案,但的確執行良好。

-EOF-

參考

https://github.com/nestjs/nest/issues/755

  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Spring Cloud(八):使用Spring Cloud Bus來實現配置動態更新