阅读 3724

前端模块化、分布式架构设计与实现(一)

设计思想与目标

概要

主要介绍 Vue 框架下、在工程化的前端项目中如何实现前端模块化、分布式部署方案。

这也是一份在我所在部门内已经使用近一年的一套前端架构及工程化方案。

PS:

这是一部连续剧...

这是里的模块指业务功能模块,不是 js 范畴的模块。为便于区分,后文将 js 范畴的模块称为 jsModule.

这是一段现实场景的回忆录,所以,1. 可能有细节遗漏;2. 它不是一个目标的最简版实现,可能会有点复杂

架构图

难点在哪里

如果是在一个项目内,完成业务与非业务逻辑、业务组件之间的解耦,增加代码引用规范的校验,基本可以满足业务功能的模块化。但是如果期望模块能够独立部署,问题就复杂起来了。

独立部署意味着:

  1. 代码需要分别打包
  2. 代码托管在不同的服务器,对应不同的域名
  3. 不同业务模块可能访问不同的后端服务

这会带来一些问题:

  1. 对现有研发流程会有影响

曾经的一个 npm run dev 会打包所有代码,webpack或者其他打包工具会解决代码拆分和加载的问题。现在需要:分别打包平台、模块的代码,分别启动平台资源服务、模块资源服务,然后自己加载这个模块的代码并执行

  1. Router 及 Vuex 管理工作方式的变化

模块是后加载的,异步的,模块需要各自负责自己的路由、状态管理,对平台而言这是不可预测并先行定义的,所以 Router 和 Vuex 需要改为动态注入的方式,不过很幸运的是,它们都支持这个特性

  1. 分别打包的代码,如果打包工具配置不当运行时会导致代码引用错误

工程内使用的打包工具是 webpack, 它会注册一个全局的变量(默认是 webpackJsonp), 并以数组的方式管理 jsModule。如果加载另外一段同样的方式打包的代码,两者管理的 jsModule 会变得混乱,因为 jsModule 的引用是以数组索引的方式实现的。

  1. 模块的资源管理

模块打包后的结果,可能产生 js、css以及其它静态资源文件,如果期望保留 chunkId 拼接文件名的方式,或者考虑打包配置变更、后期资源变化的情况,模块的最终资源列表将是不可人工枚举的。所以我需要打包工具打包完成后输出一个资源列表,不过 webpack 的插件很优秀,不必担心。

  1. 模块资源的加载

考虑一个模块间依赖的场景:如果A依赖B,如何知道B已经加载完了?再考虑一个场景,模块 C 和 D,同时请求加载同样的资源,如何管理?所以我需要知道模块是什么,是否已经完全加载,还需要一个独立资源加载器避免重复资源请求。

  1. 开发环境下的模块冲突

考虑这样的场景:某系统A(平台)已经集成了模块B(B的资源指向了测试或生产环境地址),开发时如何将模块B的资源指向开发环境?(理想情况下可能期望提供一个纯净的平台,不过现实总会有惊喜)

  1. 多环境下的模块功能测试

一个功能的上线需要经过:开发环境 > 测试环境 > 预发环境 > 生产环境,平台和模块都各自有对应的以上这些环境。以上不同的流程,除域名不同以外,代码本身及其运行环境并没有任何差别,那怎样保证测试环境的平台加载测试环境的模块资源?生产环境加载生产的?

  1. 跨域问题

这也是处理起来最麻烦的一个问题,模块的请求事实上有两部分:模块资源(js/css等) 和 数据资源(ajax请求)。通常不同的模块都对应不同的后端服务(b.com)。当模块集成到平台上运行的时候,实际上发出请求的对象不是模块本身而是平台(a.com)。当然我们可以选择同一走平台代理,或者要求模块服务器信任平台来源的请求。其次,从开发上讲,原则上禁止代码中的任何形式的域名硬编码,模块中没有域名的记录,那应该怎样让平台知道模块的数据资源请求的域名呢?

  1. 模块隔离

举个例子,平台选择用 axios 提供 http 服务,如果模块A引用axios并提供了并不小心做了全局配置,比如加个 baseUrl,其它的模块怕是会全军覆没了。。。我不希望每个模块引入自己http服务,这很混乱,也会带来很多功能重复的代码,我也不希望采用 iframe,这会带资源和性能的浪费,也会带来布局上的一些坑。我做了部分隔离,以确保不会出现无意识的AOE

  1. 其它问题

实际上也还一些其它细节问题,比如npm run dev 后自动打开浏览器的功能,我需要分别启动平台的服务和模块的服务,这是两个进程,我需要确定平台的代码编译工作已经完成,所以进程间通信成了必需品。再比如,抽离了全局的编译命令,hotreload 也让我踩了点坑, 等等。。。

我的目标

  1. 不改变研发习惯

无论是模块还是平台的开发,保留原来的味道npm run dev, 该有热加载会有,该有自动打开浏览器也会有。当然,配置文件变了~

  1. 模块开发时不用关心细节,比如跨域的代码怎么写

这就有点扯淡了,事实上当时在发展过程中让前端的兄弟陪我踩了不少的坑,这部分处理也是经历一次大的修改的,当然,现在工作得好不错。

  1. 在以上要求下实现前端的模块化、分布式部署

具体事项

  1. 编译命令改造,支持平台、模块代码打包,这也是解决上面的问题必要的一步
  2. 编译命令集成模板功能(平台和模块的模板)
  3. 模块管理工具(客户端运行)
  4. 增加 eslint 规则,禁止不规范的跨模块代码引用
  5. 分离 http 服务、mock 服务
  6. 文档

接下来。。。

  1. 模块接口设计
  2. 模块管理器的实现
  3. 模块依赖关系的定义与实现
  4. 模块、平台通信功能实现
  5. 路由管理
  6. 状态管理
  7. 跨域请求处理
  8. 编译工具的改造
  9. 开发模式架构
  10. 开发与测试

以上输出顺序或结构可能会有调整

FLAG: 用自律善待未来的自己

文章分类
前端
文章标签