JModule.define(moduleKey, { init(jModuleInstance) {}, // jModuleInstance 为JModule 实例 routes: [], // 路由 store: {}, // vuex imports: [], // 定义模块依赖 exports: {}, // 对外API }); 复制代码
这里做一点解释:
举个例子,开发一个研发流水线(pipeline)的模块:
应该会得到两个这样的模块配置:
// coding 模块配置 JModule.define('coding', { exports: { loadHooks() {}, }, }); // pipeline 模块配置 JModule.define('pipeline', { routes: [{ path: '/list', name: 'PipelineList', component: ListView, }, { ... }], store: { currentPipeline: {}, // vuex 里的模块概念 }, imports: ['coding'], // 它依赖coding提供的API, exports: { ListView, }, }); // pipeline 业务代码 JModule.require('coding.loadHooks').then((loadHooks) => { loadHooks('...'); }); 复制代码
把问题具体化,应该有三个问题需要回答
这里总共涉及六个字段:moduleKey、init、routes、store、imports、exports。
作用
这是模块的标识符,用于识别一个模块。
它不重要
如果从“让模块代码顺利运行”这一个目标来讲,它其实不需要,我把代码加载了,该注册的路由注册了,代码就可以正确工作了,业务逻辑根本不需要它。不过,让代码运行,只是工程中的一部分工作。
它很重要 从管理的角度讲,它真的必不可少。比如:
a. 权限管理: 特定的人群只能访问特定的模块,需要它识别模块身份
b. 资源加载管理: 比如由于某些原因,多次要求执行加载某个模块的资源,我们可以通过模块标识进行模块加载状态管理,避免重复从服务器加载资源
c. 调试: 可以跟踪指定模块的资源加载进度以及执行异常,另外,当同一个平台注入了同一个模块的两套资源配置时,比如既有生产环境配置又因开发需要注入了本地资源配置,可以选择本地优先。
作用
声明模块的路由信息和需要全局共享的数据
它不重要
一个模块有哪些页面,有哪些数据需要共享,是业务范畴的事情,业务是复杂多变的,它可能根本没有需要共享的数据,甚至根本不需要注册路由,比如我可能声明一个公共组件作为模块,或者它甚至没有视图,只是负责一些特定数据的加载,它其实可以不需要。所以这是可选字段。
它很重要
routes 很重要,对大多数场景而言,我们需要根据不同的地址显示不同的视图,路由就必不可少。而且业务范畴的事情,也应该在模块内管理,平台不会预测到一个模块有几个页面叫什么名字。store 的存在我倒是犹豫过,没有它并不会让业务开发不下去,如果需要共享数据,用 events 和 exports 事实上也可以达到相同的目的,但是从开发者习惯、易用性及历史项目改造难度的角度讲,store能避免一些额外的工作。
作用
模块间的依赖管理,声明模块对外提供服务的api, 以及依赖关系的外部模块的声明。可选
它不重要
没有这两个字段,事实上也确实有其它方式去满足功能开发的需求。没有imports,自己手动加载依赖的模块代码也可以工作;没有exports,往全局变量写点东西,其它模块也照样能访问到。如果只是为了实现功能,说它不重要,其实也没毛病。
它很重要
从代码可维护的角度讲,imports 声明依赖的模块,可以实现自动加载依赖的模块,不然可能会面临脑力记住依赖关系,手动加载依赖模块的尴尬场面。exports 除了对外暴露模块功能以外,可以告知开发者哪些是对外开放的功能,避免不兼容的修改导致其它模块不能正常工作。
作用
模块加载完之后自动执行的一个函数,可选
它不重要
绝大多数场景下,它确实没有存在必要。目前我也没有用到它了。
它很重要
这个字段存在的意义,主要是为了以后扩展功能,它可以拿到模块的实例信息,在早期的跨域解决方案中,模块内的http请求需要知道模块所在的域名,但在代码不允许使用域名硬编码的原则下,这个信息只有从模块实例才能获取到。后来为了研发方便调整了这部分方案,不过考虑到模块运行可能需要其它来源于外部配置的信息,所以保留了这个配置。
可能不一定够用,但我并没有遇到更复杂场景。从模块管理的角度讲,提供了moduleKey 作为身份识别;从模块自身功能完备性角度讲,一般业务场景中需要全局挂载使用的只有router和store了;从模块间的关系讲,定义了入口和出口,应该也齐活了。
这里我大概参考了一下angular的模块设计,angular是个自带模块设计的东西,所以借鉴了一下。或许有更好的方式。