十一、模块的重新导出

上节模块导入的时候,我们留下了一些问题,今天我们来处理一部分,完成模块的重新导出功能

前置知识

模块可以重新导出他们导入的模块,从而使其可用于导入该模块的其他模块。也就是说exports不仅能导出providers,也可以导出模块

比如有模块A, B, C,模块B导入了A并将A重新导出,模块C导入了B,那么模块C可以通过使用AB中导出的providers

1
2
3
4
5
6
7
8
9
10
11
12
13
// 伪代码示例
let moduleA = { // 可以使用1 2 3
providers: [1, 2, 3],
exports: [1, 2]
}
let moduleB = { // 可以使用 1 2 4 5
imports: [moduleA],
providers: [4, 5],
exports: [moduleA, 4]
}
let moduleC = { // 可以使用 1 2 4
imports: [moduleB]
}

源码实现

由于我们会导出模块,所以我们就需要一个方法来判断是不是模块,为了实现这个,我们对@Module装饰器进行一下调整,添加一个新的元数据做标识

1
2
3
4
5
6
7
8
9
10
11
12
13
// @nestjs/common/module.decorator.ts
import 'reflect-metadata'



// Module装饰器工厂函数
export function Module(metadata: ModuleMetadata): ClassDecorator {
return (target: Function) => {
// 给模块添加一个元数据进行标识,表明它是一个类
~ Reflect.defineMetadata('isModule', true, target)
// ...
}
}

然后在nest-application中做相应的解析,我们调整了initProviders方法并添加了两个新的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// @nestjs/core/nest-application.ts

initProviders() {
// 拿到导入的模块
const imports = Reflect.getMetadata('imports', this.module) ?? []
// 遍历所有导入的模块
for (const importModule of imports) {
this.registerProvidersFromModule(importModule)
}
// 当前模块的providers也需要保存
const providers = Reflect.getMetadata('providers', this.module) ?? []
for (const provider of providers) {
this.addProvider(provider)
}
}
// 判断是否是一个模块
isModule(token) {
return token && token instanceof Function && !!Reflect.getMetadata('isModule', token)
}
private registerProvidersFromModule(module) {
// 拿到导入模块的providers
const importedProviders = Reflect.getMetadata('providers', module) ?? []
// 拿到导入模块的exports
const exports = Reflect.getMetadata('exports', module) ?? []
// 根据providers和exports进行过滤,把需要的provider保存起来
for (const exportToken of exports) {
// exports中可能是provider,也可能是一个module
if (this.isModule(exportToken)) {
// 这里需要递归,因为重新导出的模块可能也导出了其他模块
this.registerProvidersFromModule(exportToken)
} else {
const provider = importedProviders.find(pro => pro === exportToken || pro.provide === exportToken)
if (provider) {
this.addProvider(provider)
}
}

}
}

前面的五个问题,我们已经基本实现了1,2,5三个

作者

胡兆磊

发布于

2025-05-11

更新于

2025-05-06

许可协议