Remix 中没有中间件,面对重复的代码应该如何是好?

一、问题

Remix 目前还没有中间件,开发路由时,有些公用的代码需要重复的编写。例如每一个路由 action/loader 都需要判断是否授权。

重复是我们不愿意看到的。

那么有没有办法解决重复的问题?

二、别的框架是如何解决重复问题的?

  • express: 中间件

大部分框架都是使用中间件来解决重复的代码问题。但是明显现在 Remix 还没有中间件支持。

  • 装饰器函数

一个装饰器在写好了,可以在类中各种装饰。但是装饰器也是有限制了的。只能在 class 中使用。

到此我们就引出了我们的正题:

在 Remix 中使用 class static method + 装饰器,解决重复代码大量重复的问题。

三、问题代码

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 处理,如果我们登录,每次进入需要授权的页面都需要先进行授权和认证,显示的调用与正常业务无关的内容。

四、为什么会思考到静态函数?

const date = Date.now()

Date 是 JS 内置的一个函数,now 方法就是一个静态方式,说明其实我们隐式写 JS、TS 的时候一直在与静态方法打交道。然后再 TS 装饰器中不能直接修饰函数,而可以修饰静态方式。这样我们就可以使用装饰器,在对静态函数进行修饰了。

从本质上思考 Date 就是一个内置对象,不需要导入直接用,now 方法也不需要实例化,直接可以使用。思考,与实例没有关系,这种复用方式是不是能用在我们自己的业务中呢?

五、ts 中的 class + 静态函数

function console() {
    console.log("loader call")
}
export class LoginHandler {
    @console
    static loader() {}
     @console
    static action() {}
}

console 的函数用于装饰 loader 在 loader 函数调用之前调用。在我们 Remix 路由中,我们就可以直接导入class类型和静态方法。

import {LoginHandler} from '~/LoginHandler'

export const action: ActionFunction = LoginHandler.action
export const loader: ActionFunction = LoginHandler.loader

当然如果有更加项目可能逻辑比这个复杂,下面我们系统的 TS 的装饰器内容。

六、装饰器学习资源

七、在 TS stage 2 下中使用装饰器需要完成以下步骤

注意:虽然装饰器已经到了第三个阶段,但是以下还是

ts.config 配置: 基础配置

{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
  }
}

如果要支持反射

npm i reflect-metadata --save
{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
  }
}

八、装饰器分类

  • 类装饰器:应用于类构造函数,可以用来修改类的行为或元数据。
  • 方法装饰器:应用于类的方法,可以修改方法的行为或元数据。
  • 访问符装饰器: 应用于类的放问题,可以修改该访问器的行为和元数据。
  • 属性装饰器:应用于类的属性,可以修改属性的行为或元数据。
  • 参数装饰器:应用于类的构造函数或方法的参数,可以修改参数的行为或元数据。

九、普通装饰器和装饰器工厂

就是一个带有 target 参数的函数:

function console(target) {
    // do some things
}

target 就是我们装饰的目标(类,函数,属性,函数参数)

装饰器工厂,其实就是一个典型的 JavaScript 闭包函数:

function color(value: string) {
  return function (target) {
    // do something with 'target' and 'value'...
  };
}

很简单:函数和闭包函数,以及它们的参数需要熟悉。

十、方法装饰器

function MethodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("Method Decorator called on: ", propertyKey);
}
  1. target: 装饰的目标类的原型对象。
  2. propertyKey: 被装饰的方法的名称。
  3. descriptor: 被装饰方法的属性描述符。

场景需求,我们将装饰的目标方法重写

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

其实就是简单存储原有方法,然后干一些需要自定义事情,然后再调用原来的方式,返回结果的过程。

十一、多装饰器执行顺序

  • 对于类的装饰器,执行顺序是从类的上方向下执行。
  • 而对于类的方法、属性或参数的装饰器,执行顺序是从左到右执行。

十二、有一个定义良好的顺序的装饰器应该这样做:

// 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");
    }
}
  1. 类装饰器:放在最上面,因为它们应用于整个类,影响类的构造函数及其行为。
  2. 方法装饰器:在类装饰器之后,因为它们应用于类的方法,可以修改方法的行为或元数据。
  3. 属性装饰器:在方法装饰器之后,因为它们应用于类的属性,可以修改属性的行为或元数据。
  4. 参数装饰器:放置在最后,因为它们应用于类的构造函数或方法的参数,可以修改参数的行为或元数据。

十三、内置装饰器

内置装饰器:TypeScript提供了一些内置的装饰器,例如

  • @deprecated: 表示废弃
  • @sealed: 表示冻结
  • @readonly: 表示只读
  • @experimental:试验性质
  • @abstract:表示抽象方法

十四、为什么需要 reflect-metadata

reflect-metadata 提供了一种在 JavaScript 运行时获取 TypeScript 类的装饰器和类型信息的方式,使得开发者能够更灵活地处理类的元数据和类型信息,从而实现一些高级的编程模式和功能。

十五、使用装饰器库的框架和库类

  • class-transformer
  • typeorm
  • Angular
  • NestJS

十六、小结

装饰器能够方便的解决业务逻辑中重复的内容,装饰器使用特别方便。但是装饰器在 class 中才能使用,结合 Date.now 的思考。我们可以组织基于 class + static method + 装饰器,形成代码简单逻辑复用。

原文链接:https://juejin.cn/post/7354656192352813106 作者:编程杂货铺

(0)
上一篇 2024年4月7日 下午5:04
下一篇 2024年4月7日 下午5:15

相关推荐

发表回复

登录后才能评论