前端架构

前端架构总览

软件架构,贯穿软件的生命周期,一个完整的软件架构设计应该自上而下处理好:

  • 系统间的关系,系统与其他系统是调用还是依赖关系

  • 系统内的关系,如前端和后端,是以怎样的方式进行通信

  • 应用内架构,包含应用相关的框架/组件

  • 规范和原则,用于指导具体的代码编写

最初,前端是没有架构的,因为功能简单的代码没有架构可言。前端开发的发展历史可以分为如下几个阶段:

  1. 古典时期,后端渲染出 HTML,前端用 CSS 进行简单的辅助

  2. 动效时期,前端开始编写 JS,进行动画效果的制作

  3. Ajax 通信时期,标志性事件是 2005 年 Google 在 Google Map 中首次使用异步通信的技术,开启了前端的新时代。一旦使用了异步通信,就意味着前端在运行时是需要动态渲染的。这时候,开发着需要做两件事:

    • 动态生成 HTML。后端返回 HTML,前端进行动态替换

    • 模板分离。后端 API 返回前端所需要的数据,前端通过模板引擎(Mustache 等)渲染 HTML

  4. 借鉴后端的 MVC 架构,前端诞生了一系列早期的 MVC 框架,如 Backbone/Knockout 等

  5. 在 Node.js 问世之后,前端的软件工程不断改善:

    • 构建工具:Gulp/Grunt 等

    • 包管理:NPM/Bower 等

    • 模块管理:Common.js/AMD 等

  6. 随着单页应用的流行,前后端分离架构成为标准实践,前端考虑的内容越来越多:

    • API 管理:Swagger API 管理工具以及 Mock Server 成为标准实践

    • 大前端:前端实现跨平台跨端移动应用框架,如 Ionic,React Native,Flutter 等

    • 组件化:前端开始由组件构成,而非页面

  7. 在 MVC 框架,React/Vue 等 MV*的框架外,出现了微前端的概念,它解决了如下问题:

    • 跨框架,同一个页面可以运行多个框架

    • 拆分应用,复杂的应用拆分成细小的应用

    • 系统迁移,遗留的系统可以嵌入新系统运行

针对前端的系统分层,自上而下有以下层次的设计:

  1. 系统级

    前后端分离架构设计,微前端架构设计

  2. 应用级

    组件库,脚手架,设计库

  3. 模块级

    组件化,模块化设计

  4. 代码级

    规范,原则,质量

项目技术架构实施

项目的技术周期分为三个阶段:

  1. 技术准备期

    这个阶段,技术第一,业务第二。主要需要完成 3 个任务: - 架构设计 - 概念验证(PoC):快速搭建与业务无关的示例代码,验证架构可行 - 迭代 0:搭建完整环境,更细粒度的技术选型,脚手架/代码库/CI/CD/权限

  2. 业务回补期

    业务开发过程正常,开始追回之前的业务累积,对应第一次业务的 DDL。这一阶段意味着技术上可能存在的调整有; - 测试策略调整,调低覆盖率,之后补回,加快开发速度 - 验证部署 - 提升团队能力:技术分享/WorkShop/结对编程/对外输出

  3. 成长优化期

    建立多次上线,业务开发流程相对固定,开始偿还技术债务:代码质量/测试覆盖率/依赖问题

工作流设计

工作流即我们开发过程中的流程和规范。具体的有如下几点:

  1. 代码结构 代码结构是我们项目文件夹组织结构以及必要的文档介绍。对于前端项目来说,我们进入项目,能够: - 通过 README 阅读相关资料,一份好的 README 应该包含支持运行的环境/必要的依赖准备/如何搭建/安装指南/线上示例或线上地址/相关文档链接/相关人员,讨论群 - package.json 了解项目依赖/组件库/构建脚本 - 浏览配置目录(或文件)信息 - 阅读具体代码

  2. 代码风格 前端项目中涉及代码风格的配置比较多,比较常用的有 Prettier/ESLint/CSSLint 以及编辑器的配置信息等。在多人协作的项目中,需要对这些规范进行强制性规范。这样的好处一来是能够统一项目代码风格,对新手阅读代码更加友好;二来是可以避免代码提交时来回产生不必要的 diff。

  3. 架构图 对于架构复杂的系统而言,架构图必不可少

  4. 代码提交规范

    • 版本管理工具(Git/SVN)统一

    • 提交信息需要规范

    • 为代码添加 pre-commit 检查:检查工作流是否存在不符合规范的行为

  5. 测试策略 对于 Web 应用的测试层级,自底向下有以下 4 层:

    • 单元测试

      选定单元测试框架,创建测试规则,指定测试覆盖率最小值

    • 组件测试

      功能测试,行为测试

    • 接口测试

      后端验证 API 是否与 Mock 数据统一,前端验证 Mock 数据或 API 是否满足前端需求

    • E2E 测试

      模仿用户行为,测试参数、参数类型、参数值、参数数量、返回值、抛出错误等,保证系统能够在任何情况下都稳定可靠完成工作

构建流设计

构建系统能够帮助开发者从系统的源代码构建出来可部署的软件。前端的构建系统需要处理如下问题:

  1. 依赖管理工具

    不同的模块化管理对应着不同的依赖管理工具; - AMD:Bower - CommonJS:NPM/Yarn

  2. 包源管理

    根据开发/组织的实际情况选择: - 公有/私有的包管理(NPM)源 - 源码版本管理器(Git)

  3. 代码打包

    在打包过程中,前端系统大部分的打包过程都交给相应的框架 CLI 进行打包

  4. 构建流设计

    设计构建流需要根据前端使用的框架/开发经验/框架脚手架功能进行设计,需要考虑的有如下几点:

    • 根据构建的复杂度选用不同的构建工具:Webpack(模块化打包)/Gulp/Grunt(复杂的项目工作流构建)或者直接使用 NPM(脚本简单的应用构建)

    • 步骤拆解:对于复杂的构建步骤(build),对其进行拆解,拆分出多个子命令

    • 自动化任务

  5. CD

    前端代码部署有 3 种方式:

    • 持续部署,构建完成即部署,常见于测试环境

    • 自动化部署,人为介入完成部署

    • 手动部署

多页应用设计

单页应用是指只有一个 Web 页面的应用,它能够动态改写当前页面的内容来达到和用户的交互。多页应用则指存在多个页面,当用户交互时,会请求新的页面进行展示。

首先,为什么会选择多页应用。单页应用是 Web 发展中的新产物,它在某些方面上做的并不好:

  • 单页应用的学习成本高

  • SEO 不友好

  • 架构复杂,构建成本大

在多页应用的架构设计上,我们分为简单多页应用的设计和复杂多页应用的设计。

简单多页应用设计

由于单页应用的架构相对复杂,构建并执行一个单页应用的步骤很多,成本很大。所以,在实现一些轻量级应用时,使用多页应用可以快速并有效地进行开发。这类应用通常有:

  • 门户,咨询,博客网站

  • 弱交互应用,比如 Github 这种网站,不需要太多的交互

  • 资源受限的设备应用,在嵌入式系统中,可能没有足够的 Web 资源使用单页应用

在简单多页应用的设计上,主要考虑以下几点:

  1. 可能你并不需要框架

对于多数简单多页应用的设计上,你可能并不需要框架或者库来进行参与。以 JQuery 为例,原生的 JS 与 JQuery 相比并没有负责很多,JQuery 简化了 HTML 和 JS 的操作,但使用原生 JS 的写法也能够满足需要,只不过原生 JS 的写法会更长一些。可以通过封装常用函数,甚至简单封装到一个新的命名空间下进行快捷使用,就像 JQuery 中的$()一样

  1. 选择 UI 库

选择一个 UI 库进行开发,很多团队会选择自己进行封装 UI 库,或者直接从外部引入优秀的 UI 库,比如 Bootstrap

  1. 选择框架

常用的比如 JQuery/Zepto

负责多页应用设计

实际情况下,应用并不是总是那么理想。使用多页应用过于简单,使用单页应用过于复杂。多页应用和单页应用之间存在着一些交集,而这些交集也影响着我们如果进行单页还是多页的选择。

这里我们主要对交集中的三个方面进行解释:

  1. 模板引擎

    对于一些相对固定的业务模块,比如弹窗,我们可以采用模板来动态生成。最简单的方式是拼接 HTML 字符串。对于一些简单的处理,确实是可以使用字符串拼接的方式,但是如果要生成比较复杂的 HTML,需要使用模板引擎。模板引擎分为两类,一类是基于字符串的模板引擎,一类是基于 JS 的模板引擎。

    • 基于字符串模板引擎

    基于字符串的模块引擎是通过字符串替换的方式来渲染 HTML,再将 HTML 插入到 DOM 中。代表框架有 Mustache/HandlerBars.js/Jeklly/Hexo/Assemble。 这种处理方式的原理是全局正则匹配关键词,例如`

    `,再从传入的参数中找到 name 的值然后进行替换。这种引擎在更新 DOM 时会更新所有 DOM 节点,然后浏览器再重新渲染所有节点,这对于有大量节点或者大量操作的情况来说是不合适的。

    • 基于 JS 的模板引擎

    基于 JS 的模板引擎需要先将模板转义成 JS,然后在执行时动态修改 DOM,主要步骤如下:

    1. 将模板编译成某种 DSL(HypeScript/JavaScript)

    2. 使用时,调用 JS 来更新 DOM 节点

    3. 当发生变化时,通过 DOM diff 算法来更新 DOM

    它与基于模板的引擎最大的区别就是第三点,是否进行全局替换还是对变化的 DOM 进行局部替换。

  2. 双向绑定

    双向绑定即双向数据绑定,视图(View)的变化能改变数据模型(Model)的数据,同时,数据模型的变化也行实时反映在视图的变化上。

    对于双向绑定,有以下几种实现方式:

    • 手动绑定,当一方数据进行改变时,去手动修改数据或者改变 UI

    • 脏检查,当发生事件时(HTTP/DOM),只去检查事件对应的数据和 DOM,然后对发生变化的数据/DOM 进行修改

    • 数据劫持,通过 hack(Object.defineProperty)的方式对数据的 setter 和 getter 进行劫持,当数据变化时,通知相关的订阅者,以触发相应的事件回调

  3. 前端路由

    传统的多页面应用,路由是由后端控制的,当发生变化时,也由后端进行处理。而单页面应用的路由则完全由前端控制,用于连接分散到各处的控制逻辑。

    前端路由主要有两种实现方式:

    • 基于 History

      HTML5 的 History API 可以实现无刷新更改地址栏链接,它提供的方法可以满足单页应用所有的需求(back/forward/go/pushState/replaceState),通过 JS 可以操作这些 API。

    • 基于 Hash

      基于 Hash 的路由是通过 Url#后面的内容进行修改。DOM API 的 location.hash 可以获取当前页面的 hash,当页面的 hash 改变时,我们可以通过监听 window 的hashChange事件,来找到对应绑定的模块。Hash 路由有以下特点:

      • hash 改变不会导致页面重新加载

      • hash 有浏览器控制,不会发送到服务器

      • hash 的变化会记录在浏览器历史里,因此可以进行前进和后退

单页应用设计

在业务不断发展的过程中,前端应用也变得不断复杂,前端的各个页面也趋向组成一个独立的应用,而不是使用依赖后端渲染的多页应用。在开发一个单页应用的过程中,有几个点是我们密切关注的:

  1. 前端应用的 MV*框架

  2. 前端三大框架如何选择

  3. 开发好单页应用应该使用哪些技术

  4. 单页应用的后台渲染

前端应用的 MV*框架

架构是在不断的实践中总结出来的,在实践的过程中,工程师不断对软件进行抽象化。其中,MVC 框架是这个过程中整理出来的一个好的框架。

MVC 框架在前端的体现是

  • Model 获取/存放所有对象数据

  • View 呈现信息给用户

  • Controller Model 与 View 之间的纽带

我们可以使用原生 JS 实现 MVC 的代码结构,但是在各处实行 MVC 框架操作上会变得非常繁琐,而且代码也难以维护。因此,我们需要前端框架来帮我们解决:

  • UI 与 Model 的同步问题

  • 如何将代码变得易于维护

前端三大框架如何选择

前端目前流行的三大框架是 React/Vue/Angular,各个框架之间既有类似的设计,也有各自不同的优势。在实际应用中,不同的团队选择的框架也是不同的,在选择框架时有几点我们需要考虑:

  • 框架能否满足大部分的应用需求

  • 团队对框架的掌握情况如何

  • 框架是否有丰富的组件库

  • 框架的社区环境如何,遇到问题能否找到解答

  • 框架的替换成本/现有项目的迁移成本

  • 框架的维护成本/难度

React

React 是一个为数据提供渲染成 HTML 的 JS 框架。从定义来看,React 是一个 View 层框架,提供了数据渲染的机制。

React 是第一个使用 Virtual DOM 的前端流行框架。除了 Virtual DOM,React 的一个重要思想是组件化,即 UI 每个组件都应该是独立封装的。React 引进了 JSX 语法,可以在 JS 中编写模板。

React 是一个 View 层的框架,它是为了优化 DOM 操作而诞生。但是对于前端应用来说,还需要一系列 Controller 以及 Model 的管理(路由/单向数据流库等)。依赖于 React 优秀的社区环境,这些库都可以在 React 社区中找到比较主流的解决办法。

选择 React 还有一个特点是 React 的设计思想不止局限在前端领域中,React Native/React VR/Electron 可以在不同的平台上运行 React 应用,因此 React 的处理逻辑可以得以复用。

Angular

Angular 是一个大而全的框架,它提供了一个前端应用完整的要素。Angular 提供了开发应用所需的脚手架,测试,运行,打包等部分。除此之外,Angular 默认还提供了动画/HTTP/表单/路由/单元测试/E2E 测试以及静态分析等内容。

Angular 除了提供开发前端应用的所有要素外,还提供了开发规范。这些严格的规范更加适合大公司的运作,尤其金融/保险类的公司。

Angular 虽然没有官方的 Web 领域之外的平台方案,但是社区拥有一些框架,例如 NativeScript/Ionic 等框架,它们都可以在某种程度上与 Web 平台逻辑共用。

Vue

Vue 借鉴了 Angular 和 React 的一些思想,在其基础上开发了一套更加易于上手的框架。学习 Vue 需要学习 Vue 的 Template 语法。

Vue 针对 React 和 Angular 的一个优势是 Vue 针对传统的多页应用直接引入 vue.min.js 就可以直接使用,在代码库上直接发布就可以,不需要打包。针对从多页应用迁移前端框架的项目来说提供了渐进式的迁移方案。

Vue 还拥有使用类似语法的 Weex 框架,熟悉了 Vue 可以快速上手 Weex 这样类似 RN 的跨端框架。但是受限与 Weex 的发展,Weex 在移动应用上的发展并不理想。

开发好单页应用应该使用哪些技术

选好前端框架后,之后的步骤有:

  1. 寻找合适的脚手架,脚手架的任务主要有:通用的业务相关模块/页面模板/业务模板/CI,CD 脚本/常用的依赖

  2. 寻找合适的 UI 框架(是否跨框架,是否易于替换)

  3. 明确浏览器的支持程度,明确测试边界

  4. 明确响应式的需求,明确支持的设备

单页应用的后台渲染

对单页应用使用服务端渲染主要有两个好处:

  • SEO

  • 更快的内容到达

单页应用的服务端渲染主要有三种类型:

  • 非 JS 的同构渲染

    同构渲染,指前后端共享模板文件,非 JS 是指与目前流行的 Node.js 同构渲染的区别。这时,需要寻找后端支持的模板类型,然后找到一个合适的模板引擎。在用户或者爬虫访问 URL 时,会由后端获取数据对应的模板文件,然后渲染出 HTML 给用户或者爬虫。使用这种技术相当于将一个单页应用变成了多页应用。

    当用户或者爬虫直接访问 URL 时,由后端渲染 HTML。当用户从当前页面跳转到其他页面时,由前端渲染,不经过后端。当用户刷新时,重新由后端返回 HTML。

  • 基于 JS 的同构渲染

    对于前端开发人员来说,使用 Nodejs 来进行同构渲染更容易上手。由于是同一门语言,对于很多 API 和语法来说都不用进行修改,只需要将前端页面的状态传递过来即可。使用同构渲染还有一个好处是主流框架都提供了renderToString(React/Vuew)或renderModuleFactory(Angular)的支持。它们的主要逻辑相似,都是通过在这个方法中传入引用的组件,就可以直接输出 HTML,再借助后端框架进行 HTML 的输出。

    使用同构渲染也会带来一些问题:

    1. 后端有内存溢出的危险

    2. 前端组件和模块需要兼容浏览器和 Node 环境

    3. 提供状态的获取和同步机制

组件化设计

从前端的视角来看,大到一整个页面,小到一个小 UI 部件,都可以称作为组件。基于组件的架构设计侧重于将广泛使用的功能模块独立出来,将独立的功能分离出来,降低系统的耦合度。从小组件到整个页面的顺序来看,组件化设计主要包含:风格设计/UI 组件库/设计系统

风格设计

  1. 原则

在风格设计上,主要遵循以下原则:

  • 亲密性:相关的组件组织到一起

  • 对齐:每一项都应该与页面的其他相关内容建立某些视觉联系

  • 重复:重复的元素表现应该一致

  • 对比强烈:对比产生强调

  • 色彩

在 Web 应用中,需要一系列的色彩来建立用户友好的页面,这些色彩通常会做如下分类:

  • 主题色

  • 功能色:展示数据和状态

  • 中性色:页面的过渡和常规显示

  • 文字

在早期的 Windows 系统中,是没有奇数字体大小的,多数的字体都是偶数的,这个习惯一直延续至今。通常,设计人员会在大于 14px 的偶数字体大小中进行选择。

对于字体来说,不同系统之间最常使用的字体也不同,macOS 最常使用PingFang SC,Windows 最常使用Microsoft YaHei,而 Linux 最常使用WenQuanYi Micro Hei。因此,全局 CSS 可以这么设置:

body {
  font-family: -apple-system, BlinkMaxSystemFont, Helvetica Neue, PingFang SC,
    Microsoft YaHei, Source Han Sans SC, Noto Hans CJK SC, WenQuanYi Micro Hei,
    sans-serif;
}
  1. 布局

目前,最常用的前端布局主要有栅格布局和 Flex 布局。

组件库

组件库有一系列优点:

  • 提升开发效率,无需重复编写代码

  • 一致化,避免了代码重写带来的样式/功能的不统一

  • 提高可维护性

根据复杂程度和业务相关性可以分为三类组件:

  1. 基础 UI 组件

通常由项目的 UI 组件库提供,提供页面最小粒度的组件,例如 button/select。

  1. 复合组件

复合组件通常由基础 UI 组件构成,它们互相之间进行某些连接,有相互的联动效果,可以在基础组件的基础上封装出复合组件。

  1. 业务组件

业务组件是实现业务功能时抽出的组件,它们通常设计到更复杂的业务场景,很多时候不止本项目可用,也可提供给其他系统和项目进行使用。通常,在业务组件下,又可以分为应用相关组件和领域特定组件。

应用相关组件是与应用逻辑绑定在一起的组件,通常不会在其他系统中进行使用,如:前端密码校验规则/对后端错误规则的响应/对后端响应的处理/登录后的处理逻辑。

领域特定组件是可以用于特定行业的组件,例如腾讯广点通的广告选择表单。

设计系统

设计系统是一组相互关联的设计模式与共同实践的结合。设计系统包含了完整的设计标准/文档/原则和工具包。设计系统是一个开发人员和设计人员写协作的产物。

构建设计系统时,需要用到模块的分层方法,并且能与组件库进行结合。从微观到宏观有以下五个不同阶段:

  • 原子,基本的 HTML 标签

  • 分子,由基本标签组成的简单组织,例如导航栏

  • 有机体,原子/分子组织而成的相对复杂的 UI 组织,例如 header 和 footer

  • 模板,构建整体布局,将组件与上下文结合起来,例如博客的一个页面是由 header+footer+博客内容组建而成

  • 页面,真实的数据展示出来的最终产品,可用于测试系统弹性

前后端分离架构设计

在现如今的前端开发中,单页面应用是主流的开发趋势。使用单页面应用则意味着前后端分离,前后端分离意味着一系列开发模式的改变:前后端的代码库分离,前后端独部署,而其中最大的痛点是 API 管理。

前后端分离 API 设计

  1. RESTful API

    RESTful API 几乎是前后端分离 API 设计上的标准实践,在实际开发中,前端使用的数据格式大多是 JSON,此外 RESTful API 还支持 XML 等格式。后端是 API 的实现者,前端需要考虑接口的合理性以及保证接口的规范性。

    RESTful API 基本的规范有:

    • 使用标准的 HTTP 动词(GET/POST/DELETE/PUT/PATCH),动词应该与行为一致

    • 正确使用状态码(20X/40X/50X)

    • 资源路径,确保 API 的 URL 正确代表资源

    • 参数处理

  2. API 与安全

    • Token 管理

    • 表单校验

    • 权限管理

  3. API 变更

    API 的变更是不可避免的,对 API 进行修改时,往往意味着字段的修改。我们可以通过以下方式降低修改 API 导致的 bug:

    • 统一 API 接口服务。将同一领域,同一类型的 API 集中在一个服务中,它所做的事情就是统一接口请求,简化相关参数。当 API 进行修改时,我们可以只修改此文件的内容,尽可能减少使用此服务的文件的修改。

    • 定义 API 数据模型。使用 TS 进行开发时,可以将返回的数据模型定义好一个接口,这样当数据模型修改时,其他地方的类型判断会抛出错误,利于对代码进行检查。

API 文档管理

  1. 传统方式

在没有专业的 API 管理工具之前,API 文档都是手写的,写在 word 或者 markdown 中。口头约定 API 是最不靠谱的一种形式。这些 API 文档都是离线的,需要在内容中标注好版本。

在线协作文档 API 管理可以保证目前的 API 是最新的版本,也能满足协作的需求。在有了 Git 之后,可以使用版本化的 API 文档,不但能够使用最新的 API 版本,也能够同时进行离线的修改和可追溯回退的功能。

还有一种方式是代码即文档,在编写相关功能时,需要同时写好注释等文档,然后使用 Javadoc 或者 JSdoc 等工具自动生成 API 文档。

  1. 互联网模式

对于互联网企业来说,传统的文档化管理相对落后,并且需要耗费时间来进行维护,维护 API 文档变成了一个负担。因为,代码化 API 文档更加适合,此处的代码是指可运行的代码,在后端服务不可用时,可以提供给前端进行使用的 API。

  • HTTP 服务即 API 文档,给定 API 文档,前端使用 Mock Server 进行模拟接口

  • 代码生成可交互的 API 文档,可视化在线工具如 Swagger

Mock Server 的三种类型

  1. 普通 Mock Server:HTTP 服务器

普通 Mock Server 看上去就像一个 HTTP 文件服务器,在相关的文件中定义好需要返回的数据,然后启动服务器,就可以将指定路径的接口数据进行返回。

如果使用的 Mock 功能比较简单,可以采用这种方式,但是如果 API 需要进行授权这类操作时,需要其他类型的 Mock Server 进行支持。

  1. DSL 形式的 Mock Server

与普通 Mock Server 相比,DSL 形式的 Mock Server 最大的特点是以配置代替代码,将原本需要实现的代码变成一行行的配置。例如:

[
  {
    "request": {
      "method": "POST",
      "url": "/test-url",
      "Authoritation": "Bear 23xs1231"
    },
    "response": {
      "status": 200,
      "text": "{\"success\": true}"
    }
  }
]
  1. 编程式 Mock Server

编程式 Mock Server 能提供最大化的定制功能,可以方便进行扩展,但是并不利于维护,也相对来说违背了 Mock Server 的初衷。当使用编程式 Mock Server 时,可以通过函数来定制,在函数中处理特定的逻辑。此外,编程式 Mock Server 可以提供灵活的数据创造功能,比如 Fake.js,可以根据数据类型生成大量模拟数据。

BFF

BFF,即 Backends For Frontends,指在服务器设计 API 时会考虑客户端的使用情况,会根据不同的设备返回不同客户端所需要的结果,而不是创建通用的 API。

使用 BFF 的目的可能有:

  • 应对多端应用

  • 聚合后端微服务

  • 代理第三方 API

  • 遗留系统的微服务改造

与传统的 Node.js + Web 框架实现 BFF 相比,更流行的方式是使用 GraphQL。GraphQL 是一种 API 的查询语言,同时它也是能满足开发者数据查询的运行时。

GraphQL 相比常规 BFF 的优势:

  • 按需获取

  • 代码即文档

  • 易于使用的 API 调试文档

  • 强类型 API 检查

  • 易于版本化的 API 管理

GraphQL 相比常规 BFF 的劣势:

  • HTTP 请求无法被缓存

  • 错误码处理不友好

  • 学习成本

  • 实现复杂

微前端架构设计

随着业务的不断增长,我们将前后端进行分离对应用进行解耦,如果应用设计的范围很广,需要多个团队进行维护时,前端应用又会被拆解。在各个团队下,对技术栈的喜好不同,对于应用变大,我们应该继续向下拆分,由此引入微前端的架构。

微前端是一种类似微服务的架构,它将单一的前端应用转化为多个小型前端应用的聚合,对于旧系统迁移和聚合前端应用上有很大的优点。每个前端应用可以独立开发,独立部署,同时也可以进行并行开发:npm/git TargetGit/submodule

在划分微前端模块时,需要考虑以下几点:

  • 应用自治,相互之间不存在依赖关系

  • 单一职责

  • 技术栈无关

微前端的技术拆分方式

  1. 路由分发

通过 HTTP 服务器反向代理,将相应的路由请求到对应的应用上。但是这样做是以页面作为最小单位,切换应用时往往需要刷新页面,重载资源。

  1. 前端微服务化

在不同的框架之上设计通信和加载机制,使之能够在同一个页面之中加载对应的应用。因为不管使用哪个框架,都离不开基础的 DOM。所以只需要实现在页面中创建合适的 DOM,并且在用户操作时,能够加载和卸载应用。

  1. 微应用

通过软件工程的方式,在部署构建环境中,把多个应用组合成同一个应用。对于一个大型应用来说,往往是以业务作为主目录,在主目录下存放各自的组件,并且有一个可以相互间分享的目录。我们要做的,就是将各个业务目录作为一个单独的项目,当构建时,再复制所有的模块到一个项目中,再进行构建。

  1. 微件化

开发一个新的构建系统,将部分独立的功能构建为一个单独的 chunk,使用时远程加载。微件是一段可以直接嵌在页面里运行的代码,它由开发人员预先编译好,在加载时不再需要任何修改和编译。对于微前端来说,微件化是指每个业务团队编写业务代码,并将代码部署在制定的服务器上,在运行时只需要加载各自的业务模块即可,更新时只需要更新相应的模块。

单页应用如果想要支持微件化,需要:

  1. 持有一个完整的框架运行时和编译环境,保证微件可以正常使用,并且能够调用系统 API

  2. 应用由提前编译变成运行时编译,会造成性能的影响

  3. 提前规划依赖,如果一个微件想使用新的依赖,需要从上游编译引入

  4. 前端容器化

使用 iframe 作为容器来容纳其他前端应用

  1. 应用组件化

基于Web Components技术,构建跨框架的前端应用

微前端架构设计

  • 组件与模式库:应用之间共用

  • 应用通信机制

  • 数据共享机制

  • 专用的构建系统

微前端架构模式

  • 基座模式

一个主应用来管理其他应用,设计难度小,通用度低

  • 自组织模式

应用之间平等,不存在相互管理,设计难度大,通用度高

微前端设计理念

  • 中心化:应用注册表

微前端时去中心化的,但是它不能完全不使用中心化。对于一个微服务来说,它需要一个服务注册中心,服务注册方要注册通告服务,服务调用方能够发现目标服务。应用配置表可以是一个 JSON 文件,或者是一个后端服务。

  • 标识化应用

需要一个特定的 ID 区分不同的应用(系统内不重复即可),可由系统后台生成。

  • 生命周期

微前端应用作为一个客户端有自己的生命周期,最基本的生命周期有:加载应用,运行应用,卸载应用。(微前端框架 Single-SPA 中有 5 种生命周期:load(加载)/bootstrap(请求静态资源)/mount(安装/挂载)/unload(删除应用)/unmount(卸载))

  • 高内聚,低耦合

最后更新于