背景说明

为了实现对扩展开放,对修改关闭的开闭原则,CabloyJS从不同的维度思考可以扩展的方式。对于不同的功能,CabloyJS针对功能的特点提供了相应的扩展机制。除此之外,还提供了Module Monkey机制,可以深度替换所有模块的前端和后端组件,实现高度的定制开发(二次开发)。Module Monkey机制一般不轻易使用,是最后的大招

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由套件test-party提供,只需安装套件test-party即可

  1. 1$ npm run cli :store:sync test-party

下面以模块test-mymonkey-monkey为例,来演示如何新建一个Module Monkey:

  1. 1$ npm run cli :create:module test-mymonkey-monkey -- --template=module

模块后缀:-monkey

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

前端Monkey

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

注入monkey

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

src/suite-vendor/test-party/modules/test-partymonkey-monkey/front/src/main.js

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

定义monkey

src/suite-vendor/test-party/modules/test-partymonkey-monkey/front/src/monkey.js

  1. 1import monkeyerPage from './pages/monkeyer.vue';
  2. 2import monkeyerComponent from './components/monkeyerComponent.vue';
  3. 3
  4. 4// eslint-disable-next-line
  5. 5export default function(Vue) {
  6. 6
  7. 7 function monkeyRoute(moduleSelf, module, routePath, routeComponent) {
  8. 8 const route = module.options.routes.find(item => item.path === routePath);
  9. 9 if (route) {
  10. 10 route.module = moduleSelf;
  11. 11 route.component = routeComponent;
  12. 12 }
  13. 13 }
  14. 14
  15. 15 function monkeyStore(moduleSelf, module) {
  16. 16 const store = module.options.store;
  17. 17 // monkey getters: message2
  18. 18 const _message2 = store.getters.message2;
  19. 19 store.getters.message2 = function(state) {
  20. 20 const res = _message2(state);
  21. 21 console.log('monkey-store message2:', res);
  22. 22 return res;
  23. 23 };
  24. 24 // monkey mutations: setMessage
  25. 25 const _setMessage = store.mutations.setMessage;
  26. 26 store.mutations.setMessage = function(state, message) {
  27. 27 _setMessage(state, message);
  28. 28 console.log('monkey-store setMessage:', state.message);
  29. 29 };
  30. 30 }
  31. 31
  32. 32 function monkeyConfig(moduleSelf, module) {
  33. 33 const config = module.options.config;
  34. 34 config.monkeyed = true;
  35. 35 }
  36. 36
  37. 37 function monkeyComponent(moduleSelf, module, componentName, component) {
  38. 38 component.module = moduleSelf;
  39. 39 module.options.components[componentName] = component;
  40. 40 }
  41. 41
  42. 42 return {
  43. 43 moduleLoaded({ module }) {
  44. 44 if (module.name !== 'test-party') return;
  45. 45 const moduleSelf = Vue.prototype.$meta.module.get('test-partymonkey');
  46. 46 // route
  47. 47 monkeyRoute(moduleSelf, module, 'kitchen-sink/monkey/monkeyee', monkeyerPage);
  48. 48 // store
  49. 49 monkeyStore(moduleSelf, module);
  50. 50 // config
  51. 51 monkeyConfig(moduleSelf, module);
  52. 52 // component
  53. 53 monkeyComponent(moduleSelf, module, 'monkeyeeComponent', monkeyerComponent);
  54. 54 },
  55. 55 };
  56. 56
  57. 57}

后端Monkey

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

注入monkey

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

src/suite-vendor/test-party/modules/test-partymonkey-monkey/backend/src/main.js

  1. 1 ...
  2. 2 // monkey
  3. 3 const monkey = require('./monkey.js')(app);
  4. 4
  5. 5 return {
  6. 6 ...
  7. 7 monkey,
  8. 8 };

定义monkey

src/suite-vendor/test-party/modules/test-partymonkey-monkey/backend/src/monkey.js

  1. 1module.exports = app => {
  2. 2 // eslint-disable-next-line
  3. 3 const moduleInfo = app.meta.mockUtil.parseInfoFromPackage(__dirname);
  4. 4
  5. 5 function monkeyRoute(module, routePath, routeController) {
  6. 6 const route = module.main.routes.find(item => item.path === routePath);
  7. 7 if (route) {
  8. 8 route.controller = routeController;
  9. 9 }
  10. 10 }
  11. 11
  12. 12 function monkeyConfig(module, config) {
  13. 13 config.monkeyed = true;
  14. 14 }
  15. 15
  16. 16 const monkey = {
  17. 17 moduleLoaded({ module }) {
  18. 18 if (module.info.relativeName !== 'test-party') return;
  19. 19 // route
  20. 20 monkeyRoute(module, 'test/monkey/monkeyee/test', {
  21. 21 module: moduleInfo.relativeName,
  22. 22 name: 'monkeyer',
  23. 23 });
  24. 24 },
  25. 25 configLoaded({ module, config }) {
  26. 26 if (module.info.relativeName !== 'test-party') return;
  27. 27 // config
  28. 28 monkeyConfig(module, config);
  29. 29 },
  30. 30 };
  31. 31 return monkey;
  32. 32};