AOP
(面向切面编程)
- 所谓面向切面编程,就是对
OOP
(面向对象编程)的一种补充,我们都知道,在面向对象编程的思想中,我们某一个类却笑功能直接在对应的类中添加即可,但是如果其他类中同样缺少这些功能呢?拿到我们又要复制粘贴到另一个类中去吗? 这里就引出了OOP
,在一个项目中类似错误处理,权限校验这些功能一般是集中处理的,在不影响其他业务模块(类)功能的情况下,这里的切面我们可以认为是同等与每一个类(业务功能模块)中的位置,我们在拓展对应的功能时,无需在给模块中修改,直接插入即可,一句话就是: AOP
能在不破坏封装功能的前提下,额外新增其他功能。而Nestjs
中内置提供了许多AOP
功能模块,类似于中间件,管道,错误处理,守卫拦截器等..
IOC
(控制反转)以及DI
(依赖注入)
IOC
是一种思想,一种设计模式,用于降低代码之间的耦合度,基本思想是借助第三方
来实现具有依赖关系的对象之间的解耦; DI
是IOC
的一种具体实现,它允许字类外创建依赖对象,并通过不同的方式将这些对象提供给类
下面进行代码演示:
class IPhone { playGame(name: string) { console.log(`${name} play game `); } }
class Student { constructor(private name: string) {} play() { const iphone = new IPhone(); iphone.playGame(this.name); } }
const student = new Student('lam'); student.play();
interface Phone { playGame: (name: string) => void; }
class DIStudent { constructor(private name: string, private phone: Phone) { this.phone = phone; this.name = name; } play() { this.phone.playGame(this.name); } }
class Android implements Phone { playGame(name: string) { console.log(`${name} use android play game `); } } const student1 = new DIStudent('lam1', new Android()); student1.play();
const student2 = new DIStudent('lam2', new IPhone()); student2.play();
|
Nestjs
中的生命周期
模块化
- 我们都知道,前端领域有组件化这一概念,而
Nestjs
中的模块化与组件化也非常的相似,在Nestjs
中,所有的功能均由模块来组织(组装)的,在一个Nestjs
项目中,需要使用ts中的装饰器
来描述模块(@Module
),且模块中有4大属性,分别为imports
,providers
,controllers
以及exports
imports
以及exports
可以认为是ES6
中的import
以及export
用于对应模块的导入以及导出
controllers
用于处理请求
providers
是用于处理业务逻辑以及联通数据库的(services
)
官方推荐的项目环境配置方法
- 安装
npm i --save @nestjs/config
|
- 在根目录下中新建
.env
以及enum
文件夹,以及在enum
文件夹下新建config.ts
文件,使.env
文件与config.ts
文件建立映射关系
├── src │ ├── enum │ │ └── config.ts │ └── index.ts └── .env
|
# 全局配置环境(一般用于全局配置) # 局部配置的化推荐在对应的模块文件夹中新建枚举类型
DB = 'Mysql' DB_HOST = '127.0.0.1'
|
export enum ConfigEnum { DB = 'DB', DB_HOST = 'DB_HOST', }
|
- 获取环境变量的两种方法
import { ConfigModule } from '@nestjs/config';
@Module({ imports: [
ConfigModule.forRoot({ isGlobal: true, }), BoysModule, ], controllers: [], providers: [], })
import { ConfigService } from '@nestjs/config/dist'; import { ConfigEnum } from '../enum/config'; @Controller('boys') export class BoysController { constructor( private ConfigService: ConfigService, ) {
}
@Get() getBoys(): any { console.log('config', this.ConfigService.get('DB')); console.log('config', this.ConfigService.get(ConfigEnum.DB_HOST)); } }
|
import { ConfigModule } from '@nestjs/config'; import { ConfigEnum } from '../enum/config';
@Module({ imports: [ConfigModule.forRoot()], controllers: [BoysController], providers: [], })
import { ConfigService } from '@nestjs/config/dist'; @Controller('boys') export class BoysController { constructor( private ConfigService: ConfigService, ) {
}
@Get() getBoys(): any { console.log('config', this.ConfigService.get('DB')); console.log('config', this.ConfigService.get(ConfigEnum.DB_HOST)); } }
|
下面再来讲讲nestjs/config
的进阶使用
- 根据生产环境来自动获取环境数据(
envFilePath
)
DB = 'Mysql-dev' DB_HOST = '127.0.0.1-dev'
DB = 'Mysql-prod' DB_HOST = '127.0.0.1-prod'
|
import { ConfigModule } from '@nestjs/config';
const envFilePath = `.env.${process.env.NODE_ENV || `development`}`;
@Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath, }), BoysModule, ], controllers: [], providers: [], })
|
"scripts": { "start:dev": "cross-env NODE_ENV=development nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "cross-env NODE_ENV=production node dist/main", },
|
import { ConfigService } from '@nestjs/config/dist'; import { ConfigEnum } from '../enum/config'; @Controller('boys') export class BoysController { constructor( private ConfigService: ConfigService, ) {
}
@Get() getBoys(): any { console.log('config', this.ConfigService.get('DB')); console.log('config', this.ConfigService.get(ConfigEnum.DB_HOST)); } }
|
结果展示
- 使用
.env
来配置公共变量,在上面提到的生产环境以及开发环境中使用的.env.development/production
等,那么当这个项目中有非常多的公共环境变量是时,我们就需要在两个.env(开发环境,生产环境)
文件中重复定义,这样就会显得非常的冗余,此时我们就可以使用到共享配置文件(.env
)
这里需要下载第三方库dotenv
import { ConfigModule } from '@nestjs/config'; import * as dotenv from 'dotenv';
const envFilePath = `.env.${process.env.NODE_ENV || `development`}`;
@Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath, load: [() => dotenv.config({ path: '.env' })], }), BoysModule, ], controllers: [], providers: [], })
|
DB = 'Mysql' DB_HOST = '127.0.0.1'
DB_USER = 'lam'
DB = 'Mysql-dev' DB_HOST = '127.0.0.1-dev'
|
import { ConfigService } from '@nestjs/config/dist'; import { ConfigEnum } from '../enum/config';
@Controller('boys') export class BoysController { constructor( private ConfigService: ConfigService, ) {
}
@Get() getBoys(): any { console.log( 'config', this.ConfigService.get(ConfigEnum.DB), this.ConfigService.get(ConfigEnum.DB_USER), ); return { message: '跨域请求成功!', }; } }
|
export enum ConfigEnum { DB = 'DB', DB_HOST = 'DB_HOST', DB_USER = 'DB_USER', }
|
结果展示:
环境变量的参数校验(后面进行数据库的灵活调用)
安装
在app.module.ts
中引入
import * as Joi from 'joi';
const envFilePath = `.env.${process.env.NODE_ENV || `development`}`;
@Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath, load: [() => dotenv.config({ path: '.env' })],
validationSchema: Joi.object({ NODE_ENV: Joi.string() .valid('development', 'production') .default('development'), DB_PORT: Joi.number().default(3306), DB_HOST: Joi.string().ip(), }), }), BoysModule, ], controllers: [], providers: [], }) export class AppModule {}
|
在官方文档中还提供自定义的校验规则,需要自己去查看….