前言

微前端跟Serverless,当前前端热门话题。

@木子朗,小米 前端工程师,目前专注于前端架构、前端工程化,热衷于打造高质量、高性能、用户体验一流的产品。个人网站:https://www.lishuaishuai.com

正文从这开始~~

大型组织的组织结构、软件架构在不断地发生变化。移动优先(Mobile First)、App中台(One App)、中台战略等,各种口号在不断的提出、修改和演进。同时,业务也在不断地发展,导致应用不断膨胀,进一步映射到软件架构上。

现有Web应用(SPA)不能很好的拓展和部署,随着时间的推移,各个项目变得越来越臃肿,web应用变得越来越难以维护。

微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。各个前端应用还可以独立运行、独立开发、独立部署。

Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. — Micro Frontends

实施微前端的六种方式

《前端架构从入门到微前端》一书中,将微前端的实施分为六种:

路由分发

路由分发式微前端,即通过路由将不同的业务分发到不同的、独立前端应用上。其通常可以通过 HTTP 服务器的反向代理来实现,又或者是应用框架自带的路由来解决。如图:

前端微服务化

前端微服务化,是微服务架构在前端的实施,每个前端应用都是完全独立(技术栈、开发、部署、构建独立)、自主运行的,最后通过模块化的方式组合出完成的应用。

采用这种方式意味着,一个页面上可以同时存在两个以上的前端应用在运行。如图:

目前主流的框架有 Single-SPA、qiankun、Mooa,后两者都是基于 Single-SPA 的封装。

微应用

微应用化是指在开发时应用都是以单一、微小应用的形式存在的,而在运行时,则是通过构建系统合并这些应用,并组合成一个新的应用。

微应用化大都是以软件工程的方式来完成前端应用的聚合,因此又可以称之为组合式集成。

微应用化只能使用唯一的一种前端框架。

如图:

微件化

微件化(Widget)是一段可以直接嵌入应用上运行的代码,它由开发人员预先编译好,在加载时不需要再做任何修改或编译。微前端下的微件化是指,每个业务团第编写自己的业务代码,并将编译好的代码部署到指定的服务器上,运行时只需要加载指定的代码即可。

如图:

iframe

iFrame 作为一个非常古老的,人人都觉得普通的技术,却一直很管用。

HTML 内联框架元素 <iframe> 表示嵌套的正在浏览的上下文,能有效地将另一个 HTML 页面嵌入到当前页面中。

iframe 可以创建一个全新的独立的宿主环境,这意味着我们的前端应用之间可以相互独立运行。采用 iframe 有几个重要的前提:

  • 网站不需要 SEO 支持

  • 拥有相应的应用管理机制。

在很多业务场景下,难免会遇到一些难以解决的问题,那么可以引入 iframe 来解决。

Web Components

Web Components 是一套不同的技术,允许开发者创建可重用的定制元素(它们的功能封装在代码之外)并且在您 Web 应用中使用它们。

在真正的项目上使用 Web Components技术,离现在还有一些距离,结合 Web Components 来构建前端应用,是一种面向未来演进的架构。或者说在未来可以采用这种方式来构建应用。

如图:

在真实的业务场景中,往往是上面提到六种方式中的几种的结合使用,或者是某种方式的变种。下面看我遇到的真实场景。

真实的业务场景

现有三个内部系统,下面称之为 old-a、old-b和C,其中,old-a和old-b是老旧的前后端未分离项目,C为前后端分离的SPA应用(React + HIUI),三个系统的架构图大致如下:

可以看到,old-a 运行在一台服务器1上,old-b运行在服务器2上,C系统的前端资源在服务器2上,并且C没有自己的域名。

三个系统均在后端同学维护和开发,他们的需求如下:

  • 统一的域名

  • 统一的界面和交互

  • 系统需要方便拓展

  • 不希望开发阶段每个系统有独立的代码仓库

  • CI 构建

怎么改造?

关键点

考虑开发同学的需求和开发成本、维护成本、未来的可拓展性,系统改造关键点如下:

  • 申请统一的域名(暂且称之为crm.mi.com)

  • 将 old-a 和 old-b 两个老旧的系统样式调整,像系统C靠拢

  • 三个系统使用统一的菜单和权限

  • 三个系统使用统一的 SSO

  • C 系统和正在开发的 X 个系统使用 CI 解决打包和手动 copy 的问题

微前端几种方式对比

总体的改造方案使用微前端的思想进行。对上面提到的六种方式进行对比(点击查看大图):

实施

对于上面几种方式,在具体的实施使用了路由分发、iFrame、应用微服务化、微应用化的融合方式。或者说是某种方案的变种,因为改造之后同时具备了这几种方案的特点。

对于C系统和正在开发的x个系统使用 single-spa 做改造,对于老旧的系统 old-a 和 old-b 使用 iframe 接入。

改造后如下图:

此时,两个老系统分别部署在各自的服务器,C 和未来的多个应用部署在同一台服务器。然后,在 Nginx 层 为老系统分配了两个路由(暂且称之为 old-a 和old-b),分别将请求打到各自的服务器,根路由打到 C 和 xx 应用的服务器。

使用React 框架的 C 和 xx 应用基于 single-spa 改造后,那么老系统 iframe 如何接入?

在配置菜单时,老系统路由会被带上标识,统一交给其中一个应用以 iframe 的方式处理。

如图:

改造后微前端架构图:

一些问题

子应用注册方式

官方示例:

// single-spa-config.js
import{ registerApplication, start } from'single-spa'registerApplication("applicationName", loadingFunction, activityFunction)
start()function loadingFunction() {
returnimport("src/app1/main.js")
}
function activityFunction(location) {
return location.pathname.indexOf("/app1/") === 0
}

当增加一个应用的时候,就需要对 single-spa-config.js 文件进行修改。

通过可配置的方式实现子应用注册:

// single-spa-config.js
import* as singleSpa from'single-spa'
import config from'./manifest.json'registerApp(config)singleSpa.start()function registerApp(conf) {conf.forEach(application => {singleSpa.registerApplication(application.name,
() => import(`./${application.name}.app/index.js`),pathPrefix(application.activeRule, application.strict),
)
})
}function pathPrefix(prefix, strict) {
returnfunction(location) {
if(strict) {
return location.pathname === prefix
}
return location.pathname.startsWith(`${prefix}`)
}
}
// manifest.json
[
{
"name": "layout",
"activeRule": "/"
},
{
"name": "welcome",
"activeRule": "/",
"strict": true
},
{
"name": "iframe",
"activeRule": "/link"
},
{
"name": "app1",
"activeRule": "/app1"
},
{
"name": "app2",
"activeRule": "/app2"
}
]
共享cookie

将域名统一的一大好处是 iframe 域名和主应用域名同源。没有了跨域 可以在 layout 统一 SSO 登录,通过 cookie 共享让其他模块拿到登录信息。

应用之间数据共享及通信

由于此次改造,应用之间不涉及数据共享,所以没有顶级 store 的概念。模块之间的简单通信 可以通过 postMessage 或基于浏览器原生事件做通信。

// 应用 A
window.dispatchEvent(
newCustomEvent('iframe:change', { detail: { path: '/a/b/c'} })
)// 应用 B
window.addEventListener('iframe:change', (event) => {console.log(event.detail.path)
})
css隔离

样式的隔离有很多种处理方式,如:BEM、CSS Module、css前缀、动态加载/卸载样式表、Web Components自带隔离机制等。

此次采用添加 css 前缀来隔离样式,比如 postcss 插件:postcss-plugin-namespace。但是这个插件并不满足需求,我们的应用分布在 src/下,并以 name.app 的方式命名,需要给不同的应用添加不同的前缀。因此使用自己定制的插件:

postcss.plugin('postcss-plugin-namespace', function() {
returnfunction(css) {css.walkRules(rule => {
if(rule.parent && rule.parent.type === 'atrule'&& rule.parent.name !== 'media') return
const filePath = rule.source && rule.source.input.file
const appName = /src\/(\S*?)\//.exec(filePath)[1] || ''
constnamespace= appName.split('.')[0] || ''rule.selectors = rule.selectors.map(s => `#${namespace} ${s === 'body' ? '' : s}`)
})
}
})
js沙箱

有一个可严重可不严重的问题,如何确保子应用之间的全局变量不会互相干扰,实现js的隔离。普遍的做法是给全局变量添加前缀,这种方式类似 css 的 BEM,通过约定的方式来避免冲突。这种方式简单,但不是很靠谱。

qiankun 内部的实现方式是通过 Proxy 来实现的沙箱模式,即在应用的 bootstrap 及 mount 两个生命周期开始之前分别给全局状态打下快照,然后当应用切出/卸载时,将状态回滚至 bootstrap 开始之前的阶段,确保应用对全局状态的污染全部清零。有兴趣的同学可以看源码。

优化体验 PWA

  • 创建桌面图标,快速访问

  • Service Worker 缓存,对大文件和不经常更改的文件缓存,优化加载。

还可以做什么?

上面的改造已经基本满足了业务需求,针对此业务还有更进一步的做法,达到更好的体验:

  • 可以使用 lerna 统一管理所有项目,方便维护,或者让每个应用拥有独立的仓库,做到独立开发。

  • 可以使用 SystemJS 实现应用的动态加载、独立部署。

总结

上面提到,此次的实践方式是微前端实现方式中几种的结合,或者是某种方式的变种。也许在理论上并不是最优的,但是在具体的问题中要是最优解。架构设计必须要与当前要解决的问题相匹配,“没有最优的架构,只有最合适的架构”。

微前端不是一个框架或者工具,而是一套架构体系。

这套体系除了微前端的基础设施外还需要具备微前端配置中心(版本管理、发布策略、动态构建、中心化管理)、微前端观察工具(应用状态可见、可控)等。

整个体系的搭建将是一个庞大的工程,目前大部分团队是在使用微前端的模式和思想来解决现有系统中的痛点。

推荐阅读

  • 前端微服务化解决方案

  • 可能是你见过最完善的微前端解决方案

  • 微前端的那些事儿

  • Micro Frontends

  • https://single-spa.js.org/

关于本文 作者:@木子郎

原文:https://www.lishuaishuai.com/architecture/1344.html

【微前端】591- 微前端在小米 CRM 系统的实践相关推荐

  1. 微前端在小米 CRM 系统的实践

    作者 | 木子朗 https://www.lishuaishuai.com/architecture/1344.html 大型组织的组织结构.软件架构在不断地发生变化.移动优先(Mobile Firs ...

  2. qiankun 微前端_微前端方案 qiankun(实践及总结)

    ❝ 作者:沉末_ 链接:https://juejin.im/post/5ed73b73e51d4578724e3fa4 ❞ 什么是微前端? 我们先来看两个实际的场景: 1. 复用别的的项目页面 通常, ...

  3. 乾坤 微前端_微前端架构初探以及我的前端技术盘点

    前言 最近几年微前端一直是前端界的热门议题, 它类似于微服务架构, 主要面向于浏览器端,能将一个复杂而庞大的单体应用拆分为多个功能模块清晰且独立的子应用,且共同服于务同一个主应用.各个子应用可以独立运 ...

  4. Bifrost微前端框架及其在美团闪购中的实践

    Bifrost(英 ['bi:frɔst])原意彩虹桥,北欧神话中是连通天地的一条通道.而在漫威电影<雷神>中,Bifrost是神域--阿斯加德(Asgard)的出入口,神域的人通过它自由 ...

  5. 网易微专业之《前端开发工程师》学习笔记(1)

    何为网易微专业? 微专业是由网易云课堂联合各领域知名专家,以就业为导向,精心打造的职业培训方案. 按要求完成学习,考试通过可获得专业认定证书,令你求职或加薪多一份独特优势. 一个微专业包含了多门必修课 ...

  6. postmessage 游戏窗口内无效_前端的微前端在交通项目内的应用实践

    项目背景 业务的快速发展,越来越多的接入渠道(百度.快应用等等),人员增加,开发成本与管理成本都上升,效率反而越来越低,团队的人员重复造轮子,毫无挑战,当然市面上也有多端解决方案,但是不太适用目前的业 ...

  7. 前端怎么使用jsessionid_前端搞微前端 | 侑夕 - 如何落地微前端一体化运营工作台...

    下期预告 前端早早聊大会目标成为用得上.听得懂.抄得走的技术大会,计划 2020 年办 >= 15 期,由前端早早聊与掘金联合举办,前端早早聊大会行程动态.录播视频/PPT/讲稿资料下载请关注 ...

  8. 前端项目微金所1 - bootstrap模板,Compatible(兼容),Viewport(视口),条件注释,第三方依赖,MediaQuery媒体查询...

    前端项目微金所笔记1 基础的bootstrap模板 <!DOCTYPE html> <html lang="en"><head><meta ...

  9. ruoyi-UI (若依)微服务版 vue前端使用及分析(2021-4-13更新)

    ruoyi-UI (若依) 微服务版 vue前端版本使用及分析(2021-4-13更新) 文章目录 ruoyi-UI (若依) 微服务版 vue前端版本使用及分析(2021-4-13更新) 1. 目录 ...

最新文章

  1. scipy.optimize.fsolve:用Python求解方程的解
  2. Map 四种获取 key 和 value 值的方法,以及对 map 中的元素排序
  3. PHP文件操作的经典案例
  4. JavaScript逻辑运算符“”和“||”短路原则的应用
  5. 项目中查询数据和模糊查询
  6. 【转】Objective-C 属性特性(assign , retain , copy , readonly , readwrite , atomic , nonatomic)...
  7. 近些年很火的Java,就业前景到底怎么样?
  8. 17.和优化相关的hint
  9. android webview打印,javascript - 如何在Android Webview中使网站上的打印按钮工作? - 堆栈内存溢出...
  10. pcb文件转成原理图_电子设计基础教学-PCB板制作之 AD(Altium Designer)的工程建立过程...
  11. 记一次有趣的诱导安装
  12. ckplayer播放器 直播
  13. 通过UDP解析域名 DNS解析
  14. OpenStreetMap初探(一)——了解OpenStreetMap
  15. H5案例分享:html5移动开发细微之美
  16. css div里引用em字体会变斜体_DIV+CSS怎么样改字体样式
  17. 29.Android展示PDF文件
  18. java音乐播放器视频_java 实现音乐播放器的简单实例
  19. 滴滴云AI超分辨率实践 | AI大师码产品折扣
  20. 谁动了你的cookie

热门文章

  1. 2020年代,中国AI创业公司将走向何方?
  2. 单片机c语言程序编写步骤,用c语言编写单片机流水灯程序详解
  3. Nat. Commun.| 机器学习对可突变的治疗性抗体的亲和力和特异性进行共同优化
  4. java+ElementUI前后端分离旅游项目第三天 预约管理
  5. 宜信研发邮件POP3/SMTP测试
  6. solar2 android,solarwalk2
  7. docker 删除映像_创建自己的Docker映像(技术提示#57)
  8. 思岚科技—SLAMTEC将自家研发技术应用到更多行业中
  9. node.js请求接口
  10. 计算机的四个发展史教案,计算机发展史教案