[web]TS 通过装饰器(Decorators\注解)实现模块插件化-代码解耦小思路

453 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

#题记:通过该文章你能了解什么?

  1. 了解到TS的装饰器(Decorators)是什么?以及它的简单机制。
  2. 如何通过装饰器(Decorators)实现一个模块的解耦

一、TypeScript的装饰器(Decorators)

熟悉Java的同学对注解肯定不陌生。毕竟sprint全家桶注解的应用是相当多。在JAVA中注解分为编译时注解、运行时注解。TS的装饰器可以理解成一种编译时注解,也就是在编译的过程中执行的,也就是在import加入了装饰器的文件时。

装饰器可以加在 类名、方法、参数上。 通过装饰器我们可以为(类、方法、参数)执行一些特别的定制。比如:为类加一个方法、加一个参数、比如为参数加一个 代理(proxy) ,诸如此类...装饰你想要的数据,监听它、修改它、替换它...

关于装饰器基本实用大家可以看官网之类的了解下:

(这里是一个TS中文网) typescript.bootcss.com/decorators.…

二、使用装饰器实现模块解耦小思路

下面以导入配置生成不同的节点这个功能为示例,我们需要的是: 首先导入配置有一个默认的生成流程,但是在该流程上可能会根据不同的配置进行一些不同的处理操作。而我们希望这些不同的配置操作能和导入相关代码完全解耦,作为一个插件使用。

1、开启tsconfig.json中的设置,启用experimentalDecorators编译器选项.

"compilerOptions": {
    ...
    
    "experimentalDecorators": true,
    
    ...
}

2、未加入装饰器前

未加入 装饰器前,我们有一个工厂,里面进行了节点的创建工作。假设该代码在一个依赖库中,其他的协同开发人员引入了该工厂依赖,我们将使用装饰器为开发人员提供一些可供其自定义创建的功能。

export class NodeFactory {

    async createNode(){
        // 下面是创建的方法...
        readConfig()
        //...
        createByConfig()
        //...
    }
}

3、定义装饰器,提供插件方法

//构建队列数组
const NodeFactoryCreatorHooks: typeof ICreateHook[] = [];
export {
  NodeFactoryCreatorHooks,
};
/**
 * 构建装饰器:外部通过在class上@nodeCreatorHook使用
 * 没到import插件的时候,将插件放入到数组里面
 */
export function nodeCreatorHook(target: typeof ICreateHook) {
   NodeFactoryCreatorHooks.push(target);
}

/**
 * 依赖注入监听
 */
export abstract class ICreateHook {
  //排序:值越大越后面执行
  public sort: number = 10;
  
  //创建前拦截
  abstract onCreateBefore(params): any;
  //创建后拦截
  abstract onCreateAfter(params): any;
}

4、工厂中使用声明了装饰器的插件

export class NodeFactory {

  //获取装饰器插件数组
  public creatorHooks: ICreateBlockHook[] = [];
  private static _instance: NodeFactory;
  
  private constructor() {
    //加载hook对象
    NodeFactoryCreatorHooks.forEach((hook:any) => {
      this.creatorHooks.push(new hook());
      //排序
      this.creatorHooks.sort((a, b) => (b.sort > a.sort ? -1 : 0));
    });
  }


  public static get instance(): NodeFactory {
    if (!this._instance) {
      this._instance = new NodeFactory();
    }
    return this._instance;
  }


  async createNode(){
         
       
        // 下面是创建的方法...
        const config =readConfig()
        //创建前调用插件的相关监听钩子
        this.creatorHooks.forEach((task) => {
          task.onCreateBefore(config);
        });
        //...
        createByConfig(config)
        //...
        //创建后调用插件的相关监听钩子
        this.creatorHooks.forEach((task) => {
          task.onCreateAfter(config);
        });
    }
}

以上,依赖提供插件的方式就已经实现完成了。

5、定义一个插件


//类名上使用装饰器,继承插件基础类
@nodeCreatorHook
export class LogCreatorHook extends ICreateHook {
     public sort: number = 1;
     //创建前拦截
     public onCreateBefore(config): any{
         console.log("马上要创建节点了,配置是:",config)
     }
     //创建后拦截
     public onCreateAfter(config): any{
         console.log("节点已经创建了,配置是:",config)
     }
}

6、使用插件

在需要的项目中import一下即可为项目开启该插件,注意import顺序应在ICreateHook的依赖之后import

//相关Hook插件导入
import '@/hooks/cretor-hook';

7、备注:

使用过程中发现类似 umi之类的框架import的顺序是和编写顺序不一致,想必和改动了webpack有关系,可以在找一个应用启动的时机(或者运行时)import

瑞思拜~ see you ~

加油加油~ 2/300