大型应用前端解决方案
架构设计方案
微服务架构是国内外互联网企业普遍采用的一种架构风格,通过分解巨大的单体式应用为多个服务的方式,来解决复杂性问题。在功能不变的情况下,一个大规模业务系统,被分解为多个细粒度的服务,每个服务关注某一特定的业务领域,并拥有一个用 API 定义的清晰功能边界。微服务可被独立部署,彼此之间松散耦合,使得单个服务很容易开发、部署和维护。
另一方面,以 NodeJS 为基础的前端技术也得到了迅速普及,出现了以 React、Vue、Angular 为代表的 MVVM 框架。以往需要在后端通过模板引擎动态生成的页面,被预先编译和打包好的静态 Javascript 文件取代,而数据交互逻辑通过 Ajax 调用后台 JSON 接口实现,以达到前后端完全分离的目的。
随着越来越多的系统同时应用前端工程化技术和微服务架构,一个系统分成很多个微服务,每个微服务又分成前端和后端两个部分的情况越来越普遍。这就带来了一个问题:多个微服务的功能界面怎么融合到一起,向最终用户提供一个或多个结构完整的用户系统?本方案通过对前端技术的整合利用,开发一个微服务专用 Portal 以解决这个问题。
该架构主要实现以下三个技术目标:
界面组织: 通过建立一个独立于所有微服务的 Portal 应用,为各个微服务的前端(以下称子界面)提供一个菜单注入和页面区域注入的运行环境。各个子界面打包好的 Javascript 文件在此环境下运行,不同子界面的 Javascript 逻辑和 CSS 样式相互隔离,但又可以通过跨服务数据交互机制交换数据。
远程加载: 通过一系列规范和约定,Portal 可以远程获取当前用户有权访问的菜单和页面部件,并根据需求远程加载对应子界面的 Javascript 文件。Portal 还应提供机制,使得这些 Javascript 文件中的代码可以加载远程 CSS 样式和资源文件,并可以跨域访问微服务后端的 Restful 接口。Portal 应该是一个静态应用,所有数据都通过接口远程获取,本身不持久化数据。
技术兼容: 支持各个子界面选择不同 MVVM 框架,支持 React、Angular、Vue.js、Svelte、Preact 等前端框架。各个子界面通过这些 MVVM 框架的命名空间管理和 JS/CSS 模块化机制开发自己的 UI 界面,并基于 Webpack 打包成单独的JS文件。Portal 通过生命周期函数动态加载这些 JS 文件,并将不同子界面的视图绑定到不同的 DOM 节点,从而实现在同一个界面中不同前端框架的共存。
该架构主要技术原理
前置条件
- 前端框架只要没有全局变量冲突,都可以在同一个页面上引入并共存,流行的前端框架如 React、Angular、Vue 相互之间并没有全局变量冲突
- 前端框架通常都提供的 CSS 命名空间方案,如 Vue 组件在编译时会自动给样式和 DOM 元素加上命名空间来避免样式冲突
- 大部分界面上需要用到的图片、图标资源,可以以 Base64 的方式直接保存在 js 文件中
前端模块化
- 为了简化前端开发的复杂度,前端社区涌现出了许多实践方法。如 Javascript 模块化可以将复杂的程序的实现逻辑,拆分到细小的文件里;如使用 JSX 等扩展语言在 Javascript 里描述 DOM 结构;如使用 SCSS 等扩展语言来简化样式定义
- 利用 Webpack 对 Javascript 模块进行分析、编译、打包。它分析 Javascript 和 CSS 模块,以及其它的一些浏览器不能直接运行的扩展语言(如 JSX、Scss、TypeScript 等),并将其打包为合适的格式以供浏览器使用
- Webpack 可以将各个子界面,打包成一个或者多个独立的 JS 文件,每个 JS 文件表示一个页面区域(称之为页面部件),相关的 HTML、CSS 都被打包到了这个 JS 文件里。页面部件可以有高度复杂的 DOM 结构,可以带有多个对话框,可以触发复杂的交互逻辑。页面部件一般会基于某个流行的 js 框架(如 React、Angular、Vue.js)进行开发
如何实现
基于上述 前置条件 和 前端模块化 技术,微服务 Portal 通过以下做法实现 界面组织 、 远程加载 和 技术兼容 三大目标:
- Portal 提供了一个入口页,入口页向 SSO 检查当前用户状态,用户未登录时重定向到 SSO 登录页;
- Portal 入口页检查权限成功后,重定向到集成界面,集成界面提供一个主菜单、二级菜单的导航体系;
- 每个二级菜单还有一个对应的工作区域,这个工作区域允许子界面注册页面部件,Portal 提供子界面的注册规范;
- Portal 的集成界面能够远程加载页面部件对应的 JS 文件,执行相应的生命周期函数;
- Portal 的集成界面负责将不同的页面部件加载到不同的 DOM 节点,以避免命名空间冲突;
- Portal 协调非 MVVM 框架的兼容性,主要是原生 JS 或其他 JSP、ASP、PHP 框架;
- Portal 统一调度各子界面的初始化、渲染、销毁事件,在特定的事件(一般是地址栏 hash 值变化时)发生时,实现界面切换;
- Portal 提供跨服务数据交互机制;
- Portal 提供全局样式,使各个子界面可以保持大体一致的风格。