了解Node.js Nestjs框架的模塊機制,聊聊實現原理( 三 )


3 Nestjs的模塊(@Module)Nestjs實現了控制反轉 , 約定配置模塊(@module)的 imports、exports、providers 管理提供者也就是類的依賴注入 。
providers 可以理解是在當前模塊注冊和實例化類 , 下面的 A 和 B 就在當前模塊被實例化 , 如果B在構造函數中引用 A , 就是引用的當前 ModuleD 的 A 實例 。
import { Module } from '@nestjs/common';import { ModuleX } from './moduleX';import { A } from './A';import { B } from './B';@Module({ imports: [ModuleX], providers: [A,B], exports: [A]})export class ModuleD {}// Bclass B{ constructor(a:A){ this.a = a; }}exports 就是把當前模塊中的 providers 中實例化的類 , 作為可被外部模塊共享的類 。 比如現在 ModuleF 的 C 類實例化的時候 , 想直接注入 ModuleD 的 A 類實例 。 就在 ModuleD 中設置導出(exports)A , 在 ModuleF 中通過 imports 導入 ModuleD 。
按照下面的寫法 , 控制反轉程序會自動掃描依賴 , 首先看自己模塊的 providers 中 , 有沒有提供者 A , 如果沒有就去尋找導入的 ModuleD 中是否有 A 實例 , 發現存在 , 就取得 ModuleD 的 A 實例注入到 C 實例之中 。
import { Module } from '@nestjs/common';import { ModuleD} from './moduleD';import { C } from './C';@Module({ imports: [ModuleD], providers: [C],})export class ModuleF {}// Cclass C { constructor(a:A){ this.a = a; }}因此想要讓外部模塊使用當前模塊的類實例 , 必須先在當前模塊的providers里定義實例化類 , 再定義導出這個類 , 否則就會報錯 。
//正確@Module({ providers: [A], exports: [A]})//錯誤@Module({ providers: [], exports: [A]})

后期補充模塊查找實例的過程回看了一下 , 確實有點不清晰 。 核心點就是providers里的類會被實例化 , 實例化后就是提供者 , 模塊里只有providers里的類會被實例化 , 而導出和導入只是一個組織關系配置 。 模塊會優先使用自己的提供者 , 如果沒有 , 再去找導入的模塊是否有對應提供者
這里還是提一嘴ts的知識點
export class C { constructor(private a: A) { }}由于 TypeScript 支持 constructor 參數(private、protected、public、readonly)隱式自動定義為 class 屬性 (Parameter Property) , 因此無需使用 this.a = a 。 Nest 中都是這樣的寫法 。
4 Nest 元編程元編程的概念在 Nest 框架中得到了體現 , 它其中的控制反轉、裝飾器 , 就是元編程的實現 。 大概可以理解為 , 元編程本質還是編程 , 只是中間多了一些抽象的程序 , 這個抽象程序能夠識別元數據(如@Module中的對象數據) , 其實就是一種擴展能力 , 能夠將其他程序作為數據來處理 。 我們在編寫這樣的抽象程序 , 就是在元編程了 。
4.1 元數據
Nest 文檔中也常提到了元數據 , 元數據這個概念第一次看到的話 , 也會比較費解 , 需要隨著接觸時間增長習慣成理解 , 可以不用太過糾結 。
元數據的定義是:描述數據的數據 , 主要是描述數據屬性的信息 , 也可以理解為描述程序的數據 。
Nest 中 @Module 配置的exports、providers、imports、controllers都是元數據 , 因為它是用來描述程序關系的數據 , 這個數據信息不是展示給終端用戶的實際數據 , 而是給框架程序讀取識別的 。
4.2 Nest 裝飾器
如果看看 Nest 中的裝飾器源碼 , 會發現 , 幾乎每一個裝飾器本身只是通過 reflect-metadata 定義了一個元數據 。
@Injectable裝飾器
export function Injectable(options?: InjectableOptions): ClassDecorator { return (target: object) => { Reflect.defineMetadata(INJECTABLE_WATERMARK, true, target); Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, options, target); };}這里存在反射的概念 , 反射也比較好理解 , 拿 @Module 裝飾器舉例 , 定義元數據 providers , 只是往providers數組里傳入了類 , 在程序實際運行時providers里的類 , 會被框架程序自動實例化變為提供者 , 不需要開發者顯示的去執行實例化和依賴注入 。 類只有在模塊中實例化了之后才變成了提供者 。 providers中的類被反射了成了提供者 , 控制反轉就是利用的反射技術 。
換個例子的話 , 就是數據庫中的 ORM(對象關系映射) , 使用 ORM 只需要定義表字段 , ORM 庫會自動把對象數據轉換為 SQL 語句 。
const data = https://www.52zixue.com/zhanzhang/webqd/js/04/09/69299/TableModel.build();data.time = 1;data.browser = 'chrome'; data.save();// SQL: INSERT INTO tableName (time,browser) [{"time":1,"browser":"chrome"}]

推薦閱讀