Module Monkey是什么

对于CabloyJS内置的模块,以及第三方提供的模块,如果我们想对这些模块提供的某些页面、功能、逻辑进行改造,一般有两种方法:

  1. Fork分支:If the module provides the source code, we can fork a branch and modify it on this branch
  2. Module Monkey:使用CabloyJS提供的Module Monkey机制,使我们可以像猴子🐒一样,很轻易的对模块的某些组件进行替换

下面,我们使用模块test-partymonkey-monkey来Monkey另一个模块test-party

创建Module Monkey

为了演示的需要,当创建一个新项目时,会自动在项目中添加模块test-partymonkey-monkey。如果要新建一个Module Monkey,可以如下所示:

$ npm init cabloy src/module/test-partymonkey-monkey --type=module

模块后缀:-monkey

  • 模块必须添加后缀-monkey,从而告知系统这个模块是一个Monkey

前端Monkey

通过Monkey机制,可以替换前端的页面路由Vuex StoreConfig组件,等等

注入monkey

在模块前端注入一个monkey对象

src/module/test-partymonkey-monkey/front/src/main.js

  return cb({
    ...
    monkey: require('./monkey.js').default(Vue),
  });

定义monkey

src/module/test-partymonkey-monkey/front/src/monkey.js

import monkeyerPage from './pages/monkeyer.vue';
import monkeyerComponent from './components/monkeyerComponent.vue';

// eslint-disable-next-line
export default function(Vue) {

  function monkeyRoute(moduleSelf, module, routePath, routeComponent) {
    const route = module.options.routes.find(item => item.path === routePath);
    if (route) {
      route.module = moduleSelf;
      route.component = routeComponent;
    }
  }

  function monkeyStore(moduleSelf, module) {
    const store = module.options.store;
    // monkey getters: message2
    const _message2 = store.getters.message2;
    store.getters.message2 = function(state) {
      const res = _message2(state);
      console.log('monkey-store message2:', res);
      return res;
    };
    // monkey mutations: setMessage
    const _setMessage = store.mutations.setMessage;
    store.mutations.setMessage = function(state, message) {
      _setMessage(state, message);
      console.log('monkey-store setMessage:', state.message);
    };
  }

  function monkeyConfig(moduleSelf, module) {
    const config = module.options.config;
    config.monkeyed = true;
  }

  function monkeyComponent(moduleSelf, module, componentName, component) {
    component.module = moduleSelf;
    module.options.components[componentName] = component;
  }

  return {
    moduleLoaded({ module }) {
      if (module.name !== 'test-party') return;
      const moduleSelf = Vue.prototype.$meta.module.get('test-partymonkey');
      // route
      monkeyRoute(moduleSelf, module, 'kitchen-sink/monkey/monkeyee', monkeyerPage);
      // store
      monkeyStore(moduleSelf, module);
      // config
      monkeyConfig(moduleSelf, module);
      // component
      monkeyComponent(moduleSelf, module, 'monkeyeeComponent', monkeyerComponent);
    },
  };

}

后端Monkey

通过Monkey机制,可以替换后端的API路由Config,等等

注入monkey

在模块后端注入一个monkey对象

src/module/test-partymonkey-monkey/backend/src/main.js

  ...
  // monkey
  const monkey = require('./monkey.js')(app);

  return {
    ...
    monkey,
  };

定义monkey

src/module/test-partymonkey-monkey/backend/src/monkey.js

const controllerMonkeyer = require('./controller/monkeyer.js');

module.exports = app => {
  // eslint-disable-next-line
  const moduleInfo = app.meta.mockUtil.parseInfoFromPackage(__dirname);

  function monkeyRoute(module, routePath, routeController) {
    const route = module.main.routes.find(item => item.path === routePath);
    if (route) {
      route.controller = routeController;
    }
  }

  function monkeyConfig(module, config) {
    config.monkeyed = true;
  }

  const monkey = {
    moduleLoaded({ module }) {
      if (module.info.relativeName !== 'test-party') return;
      // route
      monkeyRoute(module, 'test/monkey/monkeyee/test', controllerMonkeyer);
    },
    configLoaded({ module, config }) {
      if (module.info.relativeName !== 'test-party') return;
      // config
      monkeyConfig(module, config);
    },
  };
  return monkey;
};