在TS中如何在子进程中动态实例化一个类

背景:

假设当前有一个主进程A,一个子进程B和一个DemoTask

我们期望的是,在主进程A中通过IPC向子进程B发送一条指令,然后在子进程B中实例化DemoTask,并执行DemoTask中的的逻辑。

在TS中,并没有类似Java反射风格的调用机制,因此需要寻找一种适合TS的动态实例化的方法。

本文主要介绍在子进程B中如何动态实例化DemoTask,不涉及IPC通信的相关内容。

要求:

主进程A中不能import DemoTask,即不能在主进程中加载任何DemoTask的资源。

应该在子进程B中加载DemoTask的相关资源,并执行逻辑。

实现

思想

在子进程中做一个<类名 - 类> 的映射,然后根据"名称 ",找到具体的" ",最后直接"new"即可

举例1:极简实现

ts 复制代码
// DemoTask.ts
// 定义Task
export class DemoTask {
    // 略
}


//////////////////////////////////////////////////////////////////////////////


// TaskRepo.ts
import { DemoTask } from 'DemoTask'

// 关键:注册具体的Task实现类,用于在子进程中实例化具体的Task
// 在子进程中调用
export const TASK_REPO = {}

export function registerTask() {
    TASK_REPO["DemoTask"] = DemoTask;
}


//////////////////////////////////////////////////////////////////////////////


// Demo.ts
import { TASK_REPO } from 'TaskRepo'

// 关键:实例化具体的Task
// 在子进程中调用
export function newTask() {
    let taskInstance = (new TASK_REPO["DemoTask"])();
    
    // 可以调用Task中的具体方法,略...

    return taskInstance
}

关键点:

  1. 在子进程B起来的时候,需要先执行 registerTask(),然后才能执行newTask() 进行实例化
  2. 需要手动维护 TASK_REPO 这个对象:当需要定义新的Task时,需要手动在此处补充Task属性

举例2:结合注解装饰器,实现自动注册

TS 复制代码
// DemoDecorator.ts 
/**
 * 关键:注册具体的Task实现类,用于在子进程中实例化具体的Task
 * 在子进程中调用
 */
export const TASK_REPO = {}

/**
 * 关键:定义注解
 * 该 注解(装饰器)可以向 TASK_REPO 中自动注册Task实现类
 * 在子进程中调用
 */
export function ProcessTask() {
  return (target): void => {
    TASK_REPO[target.name] = target;
  };
}


/////////////////////////////////////////////////////////////////////////////////////


// DemoTask.ts
// 定义Task:使用装饰器,实现自动注册Task
@ProcessTask()
export class DemoTask {
    // 略
}


/////////////////////////////////////////////////////////////////////////////////////


// Demo.ts
// 关键:实例化具体的Task
// 在子进程中调用
export function newTask() {
    let taskInstance = (new TASK_REPO["DemoTask"])();
    
    // 可以调用Task中的具体方法,略...

    return taskInstance
}

关于这个例子需要进一步讨论:

  • Q:在子进程中,类装饰器为什么没有触发执行?
    A:当加载DemoTask类时,会自动触发类装饰器;若没有触发类装饰器,说明DemoTask没有被加载到该进程。

  • Q:那该怎么办?
    A:在进程起来的时候执行一段"携带该Task的import代码 ",如,打印该Task的类名

    TS 复制代码
    import {DemoTask} from DemoTask.ts
    
    export const TASK_LOADER = [
      DemoTask
    ]
    
    // 在 子进程B 起来时,先调用执行这一段代码
    export function printDetail() {
      let array = PAF_TASK_LOADER  
      let taskNames = ""
      for (let task of array) {
        taskNames += (task.name) + " ## "
      }
      Logger.info(TAG, "succeed loading TASK. detail: length=" + array.length + " taskNames=" + taskNames)
    }
  • Q:"携带该Task的import代码"需要手写?
    A:可以开发"编译插件",在编译阶段自动生成上述代码

  • Q:那在"举例1"的基础上直接开发"编译插件",实现自动注册,岂不是更简单
    A:是的。但是引入装饰器,既可以在未来扩展DemoTask的功能,同时也不会使"编译插件"开发得过于复杂

相关推荐
Deepsleep.7 分钟前
前端性能优化面试回答技巧
前端·面试·性能优化
不想上班只想要钱2 小时前
vue3使用<el-date-picker分别设置开始时间和结束时间时,设置开始时间晚于当前时间,开始时间早于结束时间,结束时间晚于开始时间
前端·javascript
Li_Ning212 小时前
为什么 Vite 速度比 Webpack 快?
前端·webpack·node.js
2501_915373882 小时前
Electron 入门指南
前端·javascript·electron
同志327133 小时前
用HTML+CSS做了一个网易云音乐客户端首页
前端·css
小猪欧巴哟3 小时前
pnpm install 安装项目依赖遇到 illegal operation on a directory, symlink 问题
前端·vue.js
独角仙梦境3 小时前
🚀🚀🚀学习这个思路,你也能手撸自己的专属vip脚手架🚀🚀🚀
前端
CJWbiu3 小时前
Github Action + docker 实现自动化部署
前端·自动化运维
吃瓜群众i3 小时前
兼容IE8浏览器的8个实用知识点
前端·javascript