一、问题
Remix 目前还没有中间件,开发路由时,有些公用的代码需要重复的编写。例如每一个路由 action/loader 都需要判断是否授权。
重复是我们不愿意看到的。
那么有没有办法解决重复的问题?
二、别的框架是如何解决重复问题的?
- express: 中间件
大部分框架都是使用中间件来解决重复的代码问题。但是明显现在 Remix 还没有中间件支持。
- 装饰器函数
一个装饰器在写好了,可以在类中各种装饰。但是装饰器也是有限制了的。只能在 class 中使用。
到此我们就引出了我们的正题:
在 Remix 中使用 class static method + 装饰器,解决重复代码大量重复的问题。
三、问题代码
ts
export const action: ActionFunction = async ({
request,
params,
}: ActionFunctionArgs) => {
const session = await getSession(request.headers.get("Cookie"));
const lang = params?.lang || defaultLang;
const dataDto = await request.json();
let validateDataDto: TLogin;
// 登录流程...
};
这是一个简单的 action 处理,如果我们登录,每次进入需要授权的页面都需要先进行授权和认证,显示的调用与正常业务无关的内容。
四、为什么会思考到静态函数?
ts
const date = Date.now()
Date 是 JS 内置的一个函数,now 方法就是一个静态方式,说明其实我们隐式写 JS、TS 的时候一直在与静态方法打交道。然后再 TS 装饰器中不能直接修饰函数,而可以修饰静态方式。这样我们就可以使用装饰器,在对静态函数进行修饰了。
五、ts 中的 class + 静态函数
ts
function console() {
console.log("loader call")
}
export class LoginHandler {
@console
static loader() {}
@console
static action() {}
}
console 的函数用于装饰 loader 在 loader 函数调用之前调用。在我们 Remix 路由中,我们就可以直接导入class类型和静态方法。
ts
import {LoginHandler} from '~/LoginHandler'
export const action: ActionFunction = LoginHandler.action
export const loader: ActionFunction = LoginHandler.loader
当然如果有更加项目可能逻辑比这个复杂,下面我们系统的 TS 的装饰器内容。
六、装饰器学习资源
- TypeScript 手册 decorators
- proposal-decorators 装饰器草案进度阶段 Github 仓库。
七、在 TS stage 2 下中使用装饰器需要完成以下步骤
注意:虽然装饰器已经到了第三个阶段,但是以下还是
ts.config 配置: 基础配置
ts
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
}
}
如果要支持反射
sh
npm i reflect-metadata --save
ts
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
}
}
八、装饰器分类
- 类装饰器:应用于类构造函数,可以用来修改类的行为或元数据。
- 方法装饰器:应用于类的方法,可以修改方法的行为或元数据。
- 访问符装饰器: 应用于类的放问题,可以修改该访问器的行为和元数据。
- 属性装饰器:应用于类的属性,可以修改属性的行为或元数据。
- 参数装饰器:应用于类的构造函数或方法的参数,可以修改参数的行为或元数据。
九、普通装饰器和装饰器工厂
就是一个带有 target 参数的函数:
ts
function console(target) {
// do some things
}
target 就是我们装饰的目标(类,函数,属性,函数参数)
装饰器工厂,其实就是一个典型的 JavaScript 闭包函数:
ts
function color(value: string) {
return function (target) {
// do something with 'target' and 'value'...
};
}
很简单:函数和闭包函数,以及它们的参数需要熟悉。
十、方法装饰器
ts
function MethodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("Method Decorator called on: ", propertyKey);
}
- target: 装饰的目标类的原型对象。
- propertyKey: 被装饰的方法的名称。
- descriptor: 被装饰方法的属性描述符。
场景需求,我们将装饰的目标方法重写
ts
function MethodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("Method Decorator called on: ", propertyKey);
const originalMethod = descriptor.value; // 保存原有方法
// 重写原始方法
descriptor.value = function(...args: any[]) {
console.log("Method execution starts");
const result = originalMethod.apply(this, args); // 调用原始方法
console.log("Method execution ends");
return result;
};
return descriptor;
}
其实就是简单存储原有方法,然后干一些需要自定义事情,然后再调用原来的方式,返回结果的过程。
十一、多装饰器执行顺序
- 对于类的装饰器,执行顺序是从类的
上方向下
执行。 - 而对于类的方法、属性或参数的装饰器,执行顺序是
从左到右
执行。
十二、有一个定义良好的顺序的装饰器应该这样做:
ts
// 1. 类装饰器
function ClassDecorator(target: any) {
console.log("Class Decorator");
}
// 2. 方法装饰器
function MethodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("Method Decorator on: ", propertyKey);
}
// 3. 属性装饰器
function PropertyDecorator(target: any, propertyKey: string) {
console.log("Property Decorator on: ", propertyKey);
}
// 4. 参数装饰器
function ParameterDecorator(target: any, propertyKey: string, parameterIndex: number) {
console.log("Parameter Decorator on: ", propertyKey, " with index: ", parameterIndex);
}
@ClassDecorator
class ExampleClass {
@PropertyDecorator
exampleProperty: string;
@MethodDecorator
exampleMethod(@ParameterDecorator param1: string, @ParameterDecorator param2: number) {
console.log("Example method called");
}
}
- 类装饰器:放在最上面,因为它们应用于整个类,影响类的构造函数及其行为。
- 方法装饰器:在类装饰器之后,因为它们应用于类的方法,可以修改方法的行为或元数据。
- 属性装饰器:在方法装饰器之后,因为它们应用于类的属性,可以修改属性的行为或元数据。
- 参数装饰器:放置在最后,因为它们应用于类的构造函数或方法的参数,可以修改参数的行为或元数据。
十三、内置装饰器
内置装饰器:TypeScript提供了一些内置的装饰器,例如
- @deprecated: 表示废弃
- @sealed: 表示冻结
- @readonly: 表示只读
- @experimental:试验性质
- @abstract:表示抽象方法
- ...
十四、为什么需要 reflect-metadata
reflect-metadata
提供了一种在 JavaScript 运行时获取 TypeScript 类的装饰器和类型信息的方式,使得开发者能够更灵活地处理类的元数据和类型信息,从而实现一些高级的编程模式和功能。
十五、使用装饰器库的框架和库类
class-transformer
typeorm
Angular
NestJS