iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >Node.js中Nestjs框架的模块机制是什么
  • 269
分享到

Node.js中Nestjs框架的模块机制是什么

2024-04-02 19:04:59 269人浏览 泡泡鱼
摘要

这篇文章主要介绍“node.js中Nestjs框架的模块机制是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“node.js中Nestjs框架的模块机制是什么”文

这篇文章主要介绍“node.js中Nestjs框架的模块机制是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“node.js中Nestjs框架的模块机制是什么”文章能帮助大家解决问题。

Node.js中Nestjs框架的模块机制是什么

Nest 提供了模块机制,通过在模块装饰器中定义提供者、导入、导出和提供者构造函数便完成了依赖注入,通过模块树组织整个应用程序的开发。按照框架本身的约定直接撸一个应用程序,是完全没有问题的。可是,于我而言对于框架宣称的依赖注入、控制反转、模块、提供者、元数据、相关装饰器等等,觉得缺乏一个更清晰系统的认识。

  • 为什么需要控制反转?

  • 什么是依赖注入?

  • 装饰器做了啥?

  • 模块 (@Module) 中的提供者(providers),导入(imports)、导出(exports)是什么实现原理?

好像能够理解,能够意会,但是让我自己从头说清楚,我说不清楚。于是进行了一番探索,便有了这篇文章。从现在起,我们从新出发,进入正文。

1 两个阶段

1.1 Express、Koa

一个语言和其技术社区的发展过程,一定是从底层功能逐渐往上丰富发展的,就像是树根慢慢生长为树枝再长满树叶的过程。在较早,nodejs 出现了 Express 和 Koa 这样的基本 WEB 服务框架。能够提供一个非常基础的服务能力。基于这样的框架,大量的中间件插件开始在社区诞生,为框架提供更加丰富的服务。我们需要自己去组织应用依赖,搭建应用脚手架,灵活又繁琐,也具有一定工作量。

发展到后面,一些生产更高效、规则更统一的框架便诞生了,开启了一个更新的阶段。

1.2 EggJs、Nestjs

为了更加适应快速生产应用,统一规范,开箱即用,便发展出了 EggJs、NestJs、Midway等框架。此类框架,通过实现底层生命周期,将一个应用的实现抽象为一个通用可扩展的过程,我们只需要按照框架提供的配置方式,便可以更简单的实现应用程序。框架实现了程序的过程控制,而我们只需要在合适位置组装我们的零件就行,这看起来更像是流水线工作,每个流程被分割的很清楚,也省去了很多实现成本。

1.3 小结

上面的两个阶段只是一个铺垫,我们可以大致了解到,框架的升级是提高了生产效率,而要实现框架的升级,就会引入一些设计思路和模式,Nest 中就出现了控制反转、依赖注入、元编程的概念,下面我们来聊聊。

2 控制反转和依赖注入

2.1 依赖注入

一个应用程序实际就是非常多的抽象类,通过互相调用实现应用的所有功能。随着应用代码和功能复杂度的增加,项目一定会越来越难以维护,因为类越来越多,相互之间的关系越来越复杂。

举个例子,假如我们使用 Koa 开发我们的应用,Koa 本身主要实现了一套基础的 Web 服务能力,我们在实现应用的过程中,会定义很多类,这些类的实例化方式、相互依赖关系,都会由我们在代码逻辑自由组织和控制。每个类的实例化都是由我们手动 new,并且我们可以控制某个类是只实例化一次然后被共享,还是每次都实例化。下面的 B 类依赖 A,每次实例化 B 的时候,A 都会被实例化一次,所以对于每个实例 B 来说,A 是不被共享的实例。

class A{}
// B
class B{
    contructor(){
        this.a = new A();
    }
}

下面的 C 是获取的外部实例,所以多个 C 实例是共享的 app.a 这个实例。

class A{}
// C
const app = {};
app.a = new A();
class C{
    contructor(){
        this.a = app.a;
    }
}

下面的 D 是通过构造函数参数传入,可以每次传入一个非共享实例,也可以传入共享的 app.a 这个实例(D 和 F 共享 app.a),并且由于现在是参数的方式传入,我也可以传入一个 X 类实例。

class A{}
class X{}
// D
const app = {};
app.a = new A();
class D{
    contructor(a){
        this.a = a;
    }
}
class F{
    contructor(a){
        this.a = a;
    }
}
new D(app.a)
new F(app.a)
new D(new X())

这种方式就是依赖注入,把 B 所依赖的 A,通过传值的方式注入到 B 中。通过构造函数注入(传值)只是一种实现方式,也可以通过实现 set 方法调用传入,或者是其他任何方式,只要能把外部的一个依赖,传入到内部就行。其实就这么简单。

class A{}
// D
class D{
    setDep(a){
        this.a = a;
    }
}
const d = new D()
d.setDep(new A())

2.2 All in 依赖注入?

随着迭代进行,出现了 B 根据不同的前置条件依赖会发生变化。比如,前置条件一 this.a 需要传入 A 的实例,前置条件二this.a需要传入 X 的实例。这个时候,我们就会开始做实际的抽象了。我们就会改造成上面 D 这样依赖注入的方式。

初期,我们在实现应用的时候,在满足当时需求的情况下,就会实现出 B 和 C 类的写法,这本身也没有什么问题,项目迭代了几年之后,都不一定会动这部分代码。我们要是去考虑后期扩展什么的,是会影响开发效率的,而且不一定派的上用场。所以大部分时候,我们都是遇到需要抽象的场景,再对部分代码做抽象改造。

// 改造前
class B{
    contructor(){
        this.a = new A();
    }
}
new B()

// 改造后
class D{
    contructor(a){
        this.a = a;
    }
}
new D(new A())
new D(new X())

按照目前的开发模式,CBD三种类都会存在,B 和 C有一定的几率发展成为 D,每次升级 D 的抽象过程,我们会需要重构代码,这是一种实现成本。

这里举这个例子是想说明,在一个没有任何约束或者规定的开发模式下。我们是可以自由的写代码来达到各种类与类之间依赖控制。在一个完全开放的环境里,是非常自由的,这是一个刀耕火种的原始时代。由于没有一个固定的代码开发模式,没有一个最高行动纲领,随着不同开发人员的介入或者说同一个开发者不同时间段写代码的差别,代码在增长的过程中,依赖关系会变得非常不清晰,该共享的实例可能被多次实例化,浪费内存。从代码中,很难看清楚一个完整的依赖关系结构,代码可能会变得非常难以维护。

那我们每定义一个类,都按照依赖注入的方式来写,都写成 D 这样的,那 C 和 B 的抽象过程就被提前了,这样后期扩展也比较方便,减少了改造成本。所以把这叫All in 依赖注入,也就是我们所有依赖都通过依赖注入的方式实现。

可这样前期的实现成本又变高了,很难在团队协作中达到统一并且坚持下去,最终可能会落地失败,这也可以被定义为是一种过度设计,因为额外的实现成本,不一定能带来收益。

2.3 控制反转

既然已经约定好了统一使用依赖注入的方式,那是否可以通过框架的底层封装,实现一个底层控制器,约定一个依赖配置规则,控制器根据我们定义的依赖配置来控制实例化过程和依赖共享,帮助我们实现类管理。这样的设计模式就叫控制反转

控制反转可能第一次听说的时候会很难理解,控制指的什么?反转了啥?

猜测是由于开发者一开始就用此类框架,并没有体验过上个“Express、Koa时代”,缺乏旧社会毒打。加上这反转的用词,在程序中显得非常的抽象,难以望文生义。

前文我们说的实现 Koa 应用,所有的类完全由我们自由控制的,所以可以看作是一个常规的程序控制方式,那就叫它:控制正转。而我们使用 Nest,它底层实现一套控制器,我们只需要在实际开发过程中,按照约定写配置代码,框架程序就会帮我们管理类的依赖注入,所以就把它叫作:控制反转。

本质就是把程序的实现过程交给框架程序去统一管理,控制权从开发者,交给了框架程序。

控制正转:开发者纯手动控制程序

Node.js中Nestjs框架的模块机制是什么

控制反转:框架程序控制

Node.js中Nestjs框架的模块机制是什么

举个现实的例子,一个人本来是自己开车去上班的,他的目的就是到达公司。它自己开车,自己控制路线。而如果交出开车的控制权,就是去赶公交,他只需要选择一个对应的班车就可以到达公司了。单从控制来说,人就是被解放出来了,只需要记住坐那趟公交就行了,犯错的几率也小了,人也轻松了不少。公交系统就是控制器,公交线路就是约定配置。

通过如上的实际对比,我想应该有点能理解控制反转了。

2.4 小结

从 Koa 到 Nest,从前端JqueryVue React。其实都是一步步通过框架封装,去解决上个时代低效率的问题。

上面的 Koa 应用开发,通过非常原始的方式去控制依赖和实例化,就类似于前端中的 JQuery 操作 dom ,这种很原始的方式就把它叫控制正转,而 Vue React 就好似 Nest 提供了一层程序控制器,他们可以都叫控制反转。这也是个人理解,如果有问题期望大神指出。

下面再来说说 Nest 中的模块 @Module,依赖注入、控制反转需要它作为媒介。

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

// B
class 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 {}

// C
class 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 = TableModel.build();

data.time = 1;
data.browser = 'chrome';
    
data.save();
// SQL: INSERT INTO tableName (time,browser) [{"time":1,"browser":"chrome"}]

ORM 库就是利用了反射技术,让使用者只需要关注字段数据本身,对象被 ORM 库反射成为了 SQL 执行语句,开发者只需要关注数据字段,而不需要去写 SQL 了。

4.3 reflect-metadata

reflect-metadata 是一个反射库,Nest 用它来管理元数据。reflect-metadata 使用 WeakMap,创建一个全局单实例,通过 set 和 get 方法设置和获取被装饰对象(类、方法等)的元数据。

// 随便看看即可
var _WeakMap = !usePolyfill && typeof WeakMap === "function" ? WeakMap : CreateWeakMapPolyfill();   

var Metadata = new _WeakMap();
function defineMetadata(){
    OrdinaryDefineOwnMetadata(){
        GetOrCreateMetadataMap(){
          var targetMetadata = Metadata.get(O);
            if (IsUndefined(targetMetadata)) {
                if (!Create)
                    return undefined;
                targetMetadata = new _Map();
                Metadata.set(O, targetMetadata);
            }
            var metadataMap = targetMetadata.get(P);
            if (IsUndefined(metadataMap)) {
                if (!Create)
                    return undefined;
                metadataMap = new _Map();
                targetMetadata.set(P, metadataMap);
            }
            return metadataMap;
        }
    }
}

reflect-metadata 把被装饰者的元数据存在了全局单例对象中,进行统一管理。reflect-metadata 并不是实现具体的反射,而是提供了一个辅助反射实现的工具库。

关于“Node.js中Nestjs框架的模块机制是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网node.js频道,小编每天都会为大家更新不同的知识点。

--结束END--

本文标题: Node.js中Nestjs框架的模块机制是什么

本文链接: https://www.lsjlt.com/news/97248.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Node.js中Nestjs框架的模块机制是什么
    这篇文章主要介绍“Node.js中Nestjs框架的模块机制是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node.js中Nestjs框架的模块机制是什么”文...
    99+
    2024-04-02
  • Node.js中模块加载机制的原理是什么
    这期内容当中小编将会给大家带来有关Node.js中模块加载机制的原理是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。简单例子老规矩,讲原理前我们先来一个简单的例子,从...
    99+
    2024-04-02
  • Node.js内置模块的运行机制是什么
    今天小编给大家分享一下Node.js内置模块的运行机制是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。内置模块一些栗子F...
    99+
    2023-07-04
  • Node.js的模块化机制和Buffer对象是什么
    本篇内容主要讲解“Node.js的模块化机制和Buffer对象是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Node.js的模块化机制和Buffer对象是什么”吧!一、Node.js的模块...
    99+
    2023-06-29
  • tp5框架中的hook机制是什么
    这篇文章主要介绍tp5框架中的hook机制是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1. 官方解释行为(Behavior)是ThinkPHP扩展机制中比较关键的一项扩展,行为既可以独立调用,也可以绑定到某个...
    99+
    2023-06-15
  • Node的模块机制是什么
    这篇文章主要介绍“Node的模块机制是什么”,在日常操作中,相信很多人在Node的模块机制是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Node的模块机制是什么”的疑惑...
    99+
    2024-04-02
  • Node.js模块查找,引用及缓存机制是什么
    这篇文章主要介绍“Node.js模块查找,引用及缓存机制是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node.js模块查找,引用及缓存机制是什么”文章能帮助大家解决问题。1. Node.js...
    99+
    2023-07-05
  • Node.js中的内置模块是什么
    本篇内容主要讲解“Node.js中的内置模块是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Node.js中的内置模块是什么”吧!Node.js 架构图内置...
    99+
    2024-04-02
  • Node.js中的模块化概念是什么
    这篇文章主要介绍“Node.js中的模块化概念是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node.js中的模块化概念是什么”文章能帮助大家解决问题。一、模...
    99+
    2024-04-02
  • Node.js中的模块化方法是什么
    今天给大家介绍一下Node.js中的模块化方法是什么。文章的内容小编觉得不错,现在给大家分享一下,觉得有需要的朋友可以了解一下,希望对大家有所帮助,下面跟着小编的思路一起来阅读吧。一、模块化的基本概念1、什么是模块化模块化是指解决一个复杂问...
    99+
    2023-06-26
  • Node.js的5大框架是什么
    这篇文章主要介绍“Node.js的5大框架是什么”,在日常操作中,相信很多人在Node.js的5大框架是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Node.js的5大...
    99+
    2024-04-02
  • linux中引入模块机制的好处是什么
    本文小编为大家详细介绍“linux中引入模块机制的好处是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“linux中引入模块机制的好处是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学...
    99+
    2023-04-21
    linux
  • Node.js中的http模块是什么及怎么使用
    本篇内容介绍了“Node.js中的http模块是什么及怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、什么是http在百度百科的解...
    99+
    2023-07-04
  • Node.js模块的加载逻辑是什么
    这篇文章主要介绍Node.js模块的加载逻辑是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、按照组织方式划分模块文件模块:是我们上一章节说过的,就是一个独立的.js文件。目录模块:是我们可以将多个独立的.js...
    99+
    2023-06-26
  • Winform开发框架中的通用附件管理模块是什么
    这篇文章将为大家详细讲解有关Winform开发框架中的通用附件管理模块是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。在做Winform项目的时候,一直有一个梦想,就是希望把所有的组件模...
    99+
    2023-06-17
  • Node.js中Require机制的原理是什么
    这篇文章给大家介绍Node.js中Require机制的原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。什么是 CommonJS每一个文件就是一个模块,拥有自己独立的作用域,变量...
    99+
    2024-04-02
  • Node.js中http模块有什么用
    这篇文章给大家分享的是有关Node.js中http模块有什么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。http模块网络作为信息传输,接收,共享的虚拟平台,通过它将各个点,面...
    99+
    2024-04-02
  • Node.js中URL 模块有什么用
    这篇文章给大家介绍Node.js中URL 模块有什么用,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。URL 模块提供了一些解析 URL 的工具var url&nbs...
    99+
    2024-04-02
  • Node.js中path模块有什么用
    这篇文章主要为大家展示了“Node.js中path模块有什么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Node.js中path模块有什么用”这篇文章吧。p...
    99+
    2024-04-02
  • Node.js的模块化机制和Buffer对象详解
    目录一、Node.js的模块化机制1、什么是CommonJS模块规范2、CommonJS模块规范化的内容3、每个导出的模块都有一个moudle对象,该对象包含的属性有:4、使用req...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作