Nestjs 熱更新 + typeorm 配置



Nestjs 熱更新 + typeorm 配置

Nestjs 開發環境熱更新的方案

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

<code>警告
請注意,webpack不會自動將您的資產(例如graphql文件)複製到dist文件夾。同樣,webpack與glob靜態路徑(例如TypeOrmModule中的實體屬性)不兼容。
/<code>

1 使用 CLI

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

安裝首先安裝依賴包:

<code>$ npm i --save-dev webpack-node-externals
/<code>

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

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

此函數獲取包含默認webpack配置的原始對象,並返回一個修改後的對象,該對象帶有一個已應用的HotModuleReplacementPlugin插件。

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

<code>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();
/<code>

在 package.json文件中增加如下兩條腳本

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

執行如下命令

<code>$ npm run build
/<code>

Webpack 開始監聽文件,在新的命令行窗口執行

<code>$ npm run start 

/<code>

不使用 CLI

安裝

<code>$ npm i --save-dev webpack webpack-cli webpack-node-externals ts-loader
/<code>

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

<code>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',
},
};
/<code>

Hot-Module Replacement為了啟用HMR,打開應用程序入口文件(main.ts)並添加幾個與webpack相關的指令,如下所示:

<code>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();
/<code>

在 package.json 文件中加入以下腳本

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

執行命令

<code>$ npm run webpack
/<code>

新命令窗口下執行

<code>$ npm run start
/<code>

Typeorm 配置

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

如:

<code>import { Cat } from '../cat/cat.entity';

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

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

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

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

<code>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 {}
/<code>

這個方案的思路是:

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

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

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

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

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

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


-EOF-

參考

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


分享到:


相關文章: