• actionherojs 的插件机制


    actionherojs 的插件机制是比较强大的,基于插件我们可以直接实现npm包的安装与卸载,同时利用提供的reload api 实现
    模块功能的生效(grouparoo 就利用了这些特性)

    创建一个插件

    • 代码结构
      可以使用actionherojs 的cli 创建
     
    ├── README.md
    ├── package.json
    ├── public
    └── dalong
    └── index.html
    ├── src
    ├── actions
    ├── createChatRoom.ts
    ├── mydemo.ts
    └── status.ts
    ├── bin
    ├── initializers
    └── ah-dalong-ui.ts
    ├── server.ts
    ├── servers
    └── tasks
    ├── tsconfig.json
    └── yarn.lock
    • 代码说明
      plugin 实际上就是一个普通的actionherojs项目,只是有些功能我们不需要
      以上代码很简单,就是一个简单的ui 组件, 注意actionhero 为peer的
      package.json
     
    {
      "author": "dalongrong",
      "name": "ah-dalong-plugin",
      "description": "my actionhero project",
      "version": "0.1.0",
      "engines": {
        "node": ">=8.0.0"
      },
      "dependencies": {
        "ioredis": "latest",
        "ioredis-mock": "latest",
        "winston": "latest",
        "ws": "latest"
      },
      "peerDependencies": {
        "actionhero": "28.1.7"
      },
      "devDependencies": {
        "@types/jest": "latest",
        "@types/node": "latest",
        "actionhero": "28.1.7",
        "jest": "latest",
        "prettier": "latest",
        "ts-jest": "latest",
        "ts-node-dev": "latest",
        "type-fest": "latest",
        "typescript": "latest"
      },
      "scripts": {
        "postinstall": "npm run build",
        "dev": "ts-node-dev --no-deps --transpile-only ./src/server",
        "debug": "tsc && ts-node-dev --transpile-only --no-deps --inspect -- ./src/server ",
        "start": "node ./dist/server.js",
        "actionhero": "actionhero",
        "test": "jest",
        "pretest": "npm run build && npm run lint",
        "build": "tsc --sourceMap false --declaration",
        "lint": "prettier --check src/*/** __tests__/*/**",
        "pretty": "prettier --write src/*/** __tests__/*/**"
      },
      "jest": {
        "testEnvironment": "node",
        "transform": {
          "^.+\\\\.ts?$": "ts-jest"
        }
      }
    }

    initializers/ah-dalong-ui.ts 主要实现了简单的路由注册

    import { Initializer, route} from "actionhero";
     
    export class AHResqueUIInitializer extends Initializer {
      constructor() {
        super();
        this.name = "ah-dalong-ui";
        this.loadPriority = 99999999;
      }
     
      async initialize() {
        /* ----- Route Injection ----- */
        route.registerRoute(
          "get",
          "/:apiVersion/dalong",
          "dalongrong"
        );
      }
    }

    actions/mydemo.ts

    import {Action} from "actionhero"
     
    module.exports = class MyDemoAction extends Action {
        async run() {
            return {name:"dalongrong"};
        }
        constructor(){
            super()
            this.name="dalongrong"
            this.description="demoapp",
            this.version=1
        }
    }
    • 集成使用
      yarn link 模式使用
      actionhero 项目的config/plugins.ts
     
    import { PluginConfig } from "actionhero";
     
    const namespace = "plugins";
     
    declare module "actionhero" {
      export interface ActionheroConfigInterface {
        [namespace]: ReturnType<typeof DEFAULT[typeof namespace]>;
      }
    }
     
    export const DEFAULT: { [namespace]: () => PluginConfig } = {
      [namespace]: () => {
        return {
          'dalong': { path: __dirname + '/../../node_modules/ah-dalong-plugin' }
        }
      }
    };
    • 访问效果

    • 参考日志

    • 参考机制
      actionherojs 实际上 src/initializers/actions.ts 通过此加载的
     
    for (const p of config.general.paths.action) {
          let files = glob.sync(path.join(p, "**", "**/*(*.js|*.ts)"));
          files = utils.ensureNoTsHeaderFiles(files);
          for (const j in files) {
            await api.actions.loadFile(files[j]);
          }
        }
        // 包含plugin的模式
        for (const plugin of Object.values(config.plugins)) {
          if (plugin.actions !== false) {
            const pluginPath: string = path.normalize(plugin.path);
     
            // old style at the root of the project
            let files = glob.sync(path.join(pluginPath, "actions", "**", "*.js"));
     
            files = files.concat(
              glob.sync(path.join(pluginPath, "dist", "actions", "**", "*.js"))
            );
     
            utils
              .ensureNoTsHeaderFiles(files)
              .forEach((f) => api.actions.loadFile(f)); // 基于import 动态加载插件里边的代码定义
          }
        }

    对于静态内容,实际上是基于staticFile 的Initializer 解决的,使用了默认的public 路径
    参考

     
      async initialize() {
        api.staticFile = {
          searchLocations: [],
          get: this.get,
          searchPath: this.searchPath,
          sendFile: this.sendFile,
          fileLogger: this.fileLogger,
          sendFileNotFound: this.sendFileNotFound,
          checkExistence: this.checkExistence,
          logRequest: this.logRequest,
        };
     
        // load in the explicit public paths first
        if (config.general.paths) {
          config.general.paths.public.forEach(function (p: string) {
            api.staticFile.searchLocations.push(path.normalize(p));
          });
        }
     
        // source the public directories from plugins
        for (const plugin of Object.values(config.plugins as PluginConfig)) {
          if (plugin.public !== false) {
            const pluginPublicPath: string = path.normalize(
              path.join(plugin.path, "public")
            );
           // 将public 放到searchLocations 中,当需要访问静态页面的时候通过searchLocations查找,然后sendFile
            if (
              fs.existsSync(pluginPublicPath) &&
              api.staticFile.searchLocations.indexOf(pluginPublicPath) < 0
            ) {
              api.staticFile.searchLocations.push(pluginPublicPath);
            }
          }
        }
     
        log(
          "static files will be served from these directories",
          "debug",
          api.staticFile.searchLocations
        );
      }

    一些问题

    • 覆盖问题 an existing xxxx with the same name xxxx will be overridden by the file

    如下


    actionherojs 使用了插件优先的模式,本地程序的action 会被plugin 的覆盖,所以名称不重复很重要

    说明

    actionherojs 的插件机制,并不难,主要还是利用了import 动态加载的机制(ts 模式),对于commonjs 的模式为

     
    let  collection = await Promise.resolve().then(() => require(fullFilePath));

    建议大家阅读下actionherojs 的源码,源码并不多,但是设计还是比较巧妙的,后边会对与reload 介绍下grouparoo,很多地方就使用到了这个而且很巧妙
    很多地方使用了类似java classpath 查找的模式,实现了js 模块的动态加载

    参考资料

    https://www.actionherojs.com/tutorials/plugins
    https://github.com/actionhero/ah-resque-ui

  • 相关阅读:
    RWCString 定义 memeroy leak
    打开eclipse报错
    Eclipse 增加php插件
    Shell 字符串的截取
    【转载】Shell判断字符串包含关系的几种方法
    Shell $? $* $@ 等含义
    Shell 获取指定行的内容
    概念性进程
    网络编程
    模块详解
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/15863442.html
Copyright © 2020-2023  润新知