主要介绍 Vue 框架下、在工程化的前端项目中如何实现前端模块化、分布式部署方案。
这也是一份在我所在部门内已经使用近一年的一套前端架构及工程化方案。
PS:
这是一部连续剧...
这是里的模块指业务功能模块,不是 js 范畴的模块。为便于区分,后文将 js 范畴的模块称为 jsModule.
这是一段现实场景的回忆录,所以,1. 可能有细节遗漏;2. 它不是一个目标的最简版实现,可能会有点复杂
如果是在一个项目内,完成业务与非业务逻辑、业务组件之间的解耦,增加代码引用规范的校验,基本可以满足业务功能的模块化。但是如果期望模块能够独立部署,问题就复杂起来了。
独立部署意味着:
这会带来一些问题:
曾经的一个 npm run dev
会打包所有代码,webpack或者其他打包工具会解决代码拆分和加载的问题。现在需要:分别打包平台、模块的代码,分别启动平台资源服务、模块资源服务,然后自己加载这个模块的代码并执行
模块是后加载的,异步的,模块需要各自负责自己的路由、状态管理,对平台而言这是不可预测并先行定义的,所以 Router 和 Vuex 需要改为动态注入的方式,不过很幸运的是,它们都支持这个特性
工程内使用的打包工具是 webpack, 它会注册一个全局的变量(默认是 webpackJsonp), 并以数组的方式管理 jsModule。如果加载另外一段同样的方式打包的代码,两者管理的 jsModule 会变得混乱,因为 jsModule 的引用是以数组索引的方式实现的。
模块打包后的结果,可能产生 js、css以及其它静态资源文件,如果期望保留 chunkId 拼接文件名的方式,或者考虑打包配置变更、后期资源变化的情况,模块的最终资源列表将是不可人工枚举的。所以我需要打包工具打包完成后输出一个资源列表,不过 webpack 的插件很优秀,不必担心。
考虑一个模块间依赖的场景:如果A依赖B,如何知道B已经加载完了?再考虑一个场景,模块 C 和 D,同时请求加载同样的资源,如何管理?所以我需要知道模块是什么,是否已经完全加载,还需要一个独立资源加载器避免重复资源请求。
考虑这样的场景:某系统A(平台)已经集成了模块B(B的资源指向了测试或生产环境地址),开发时如何将模块B的资源指向开发环境?(理想情况下可能期望提供一个纯净的平台,不过现实总会有惊喜)
一个功能的上线需要经过:开发环境 > 测试环境 > 预发环境 > 生产环境,平台和模块都各自有对应的以上这些环境。以上不同的流程,除域名不同以外,代码本身及其运行环境并没有任何差别,那怎样保证测试环境的平台加载测试环境的模块资源?生产环境加载生产的?
这也是处理起来最麻烦的一个问题,模块的请求事实上有两部分:模块资源(js/css等) 和 数据资源(ajax请求)。通常不同的模块都对应不同的后端服务(b.com)。当模块集成到平台上运行的时候,实际上发出请求的对象不是模块本身而是平台(a.com)。当然我们可以选择同一走平台代理,或者要求模块服务器信任平台来源的请求。其次,从开发上讲,原则上禁止代码中的任何形式的域名硬编码,模块中没有域名的记录,那应该怎样让平台知道模块的数据资源请求的域名呢?
举个例子,平台选择用 axios 提供 http 服务,如果模块A引用axios并提供了并不小心做了全局配置,比如加个 baseUrl,其它的模块怕是会全军覆没了。。。我不希望每个模块引入自己http服务,这很混乱,也会带来很多功能重复的代码,我也不希望采用 iframe,这会带资源和性能的浪费,也会带来布局上的一些坑。我做了部分隔离,以确保不会出现无意识的AOE
实际上也还一些其它细节问题,比如 npm run dev
后自动打开浏览器的功能,我需要分别启动平台的服务和模块的服务,这是两个进程,我需要确定平台的代码编译工作已经完成,所以进程间通信成了必需品。再比如,抽离了全局的编译命令,hotreload 也让我踩了点坑, 等等。。。