Nestjs 熱更新包含 typeorm 的配置方案

Nestjs 開發(fā)環(huán)境熱更新的方案

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

警告
請(qǐng)注意,webpack不會(huì)自動(dòng)將您的資產(chǎn)(例如graphql文件)復(fù)制到dist文件夾。同樣,webpack與glob靜態(tài)路徑(例如TypeOrmModule中的實(shí)體屬性)不兼容。

1 使用 CLI

如果您正在使用Nest CLI,配置過(guò)程非常簡(jiǎn)單。CLI包裝webpack,它允許使用HotModuleReplacementPlugin。

安裝
首先安裝依賴包:

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

配置
在根目錄下創(chuàng)建 webpack.config.js,內(nèi)容如下:

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()],
  };
}

此函數(shù)獲取包含默認(rèn)webpack配置的原始對(duì)象,并返回一個(gè)修改后的對(duì)象,該對(duì)象帶有一個(gè)已應(yīng)用的HotModuleReplacementPlugin插件。

Hot-Module Repacement
為了啟用HMR,打開應(yīng)用程序入口文件(main.ts)并添加幾個(gè)與webpack相關(guān)的指令,如下所示:

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",

執(zhí)行如下命令

$ npm run build

Webpack 開始監(jiān)聽文件,再新的命令行窗口執(zhí)行

$ npm run start

不使用 CLI

安裝

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

配置
創(chuàng)建 webpack.config.js 內(nèi)容如下:

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,打開應(yīng)用程序入口文件(main.ts)并添加幾個(gè)與webpack相關(guān)的指令,如下所示:

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",

執(zhí)行命令

$ npm run webpack

新命令窗口下執(zhí)行

$ npm run start

Typeorm 配置

由于webpack與glob靜態(tài)路徑不兼容,所以要想讓 typeorm 同樣支持熱更新,正常需要靜態(tài)引入 entity 而不是利用通配符方式。

如:

import { Cat } from '../cat/cat.entity';

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

但是這樣如果有很多 entity 會(huì)非常不便,幸運(yùn)的是,webpack有一特性 require.context。允許收集所需文件的上下文。那么我們可以這樣:

// entity.context.ts (in root src folder)
export const entityContext =  require.context('.', true, /\.entity\.ts$/);
// database.module.ts
import { 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 { }

但這個(gè)方案,對(duì) production 不友好,所以還可以使用下面的方案:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { getMetadataArgsStorage } from 'typeorm';

// The modules are loaded from here, hence importing the entities
import { 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 {}

這個(gè)方案的思路是:

由于webpack將所有代碼打包成一個(gè)單獨(dú)的包文件,為了讓HMR工作,這個(gè)包文件作為服務(wù)器加載并運(yùn)行指定實(shí)體:* dirname + '/*/。ts'將導(dǎo)致typeorm需要那些文件(而實(shí)際的實(shí)體已經(jīng)在webpack包中加載了)。

當(dāng)typeorm試圖獲取repository時(shí),它會(huì)將傳入的實(shí)體與從js/ts文件中加載的實(shí)體配置進(jìn)行比較(例如,從webpack包中加載用戶的getRepository)。

盡管它們有相同的名稱,但它們是從不同的模塊加載的兩個(gè)不同的類(函數(shù)),因此typeorm將無(wú)法找到正確的類。

我的解決方案是基于所有模塊都已加載的事實(shí),因此應(yīng)該已經(jīng)通過(guò)導(dǎo)入加載了所有實(shí)體。對(duì)于結(jié)構(gòu)良好的項(xiàng)目,NestJS尤其如此。具體來(lái)說(shuō),對(duì)于每個(gè)模塊,要么模塊本身,要么控制器將導(dǎo)入實(shí)體到某個(gè)地方。

通過(guò)利用@Entity裝飾器的內(nèi)部機(jī)制,我們將能夠獲得實(shí)體類的列表。

不確定這個(gè)是最佳方案,但的確運(yùn)行良好。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容