React微前端实战教程(体验篇)
在前端领域,用户体验放到第一位。在复杂业务操作的前端项目中,SPA的体验要比多页面或iframe嵌套的效果好很多。我们不能为了代码解耦、浏览器中无冲突而放弃SPA。
在以前的项目中,我们已经使用npm
发包的方式将代码分割到多个仓库。在集成前端应用时,再使用npm
和Webpack
工具构建成一个SPA。但仍有一个明显的缺点:每次底层组件更新时,上层SPA都需要重新集成打包、部署。同时,底层组件个数不少又经常更新,上层SPA频繁地集成与部署,让人很反感。
参照后端微服务,微前端(或称为前端微服务)也应该有以下特点:独立开发、独立部署、灰度发布。选择Micro frontends + SPA
是最佳的DevOps与体验方案。经过几天的奋战,已经实现了一个基于React框架的微前端Demo,见: https://micro.qinzhiqiang.cn 。
1 项目设计
微前端主要拆分为三个部分:统一工程化构建脚本、浏览器中运行时框架、后端运维服务,其余的是可以无数多的微前端APP。本项目中,我们只考虑统一的React+Router+Redux
前端技术栈,也可以确保开发人员在本地开发拥有独立SPA的开发体验、无感知微前端的存在。
如果运行时框架要同时支持React
+Vue
+Angular
,至少需要统一托管浏览器History的变化、微前端APP的启动,意味着与各个前端框架强绑定的Router都无法使用,需要我们实现统一的Router,工程量比较大。
Demo中需要以下8个项目:
- react-micro-frontend-scripts,工程化核心。Webpack与NodeJS脚本,控制微前端项目的元信息输出、JS符号导入/导出。
- react-micro-frontend-polyfill,浏览器Polyfill。引入react-app-polyfill不同的内容,构建支持IE9、IE11和其它浏览器不同的Polyfill入口文件。
- react-micro-frontend-framework,运行时核心。运行在浏览器中的微前端框架,负责微前端App的资源加载、组件显示,同时提供公共库JS功能服务。切换到不同的主路由时,可以展示不同的微前端APP。
- react-micro-frontend-app-home,主页微前端APP。经部署了多个版本,用户可以看到有差异的不同主页。
- react-micro-frontend-app-example,与Redux、Saga、Epic相关的微前端APP样例。
- react-micro-frontend-app-example-sub,嵌套在example中展示的另一个微前端APP样例。已部署多个版本,其中一个版本有打字效果。
- react-micro-frontend-app-example-echarts,微前端依赖样例,依赖从CDN加载的Echarts。定义Echarts为微前端
3rd-echarts
,框架加载Echarts完成之后才加载该APP。 - react-micro-frontend-server-go,服务与运维核心,GoLang编写。处理微前端部署的元信息,提供SPA默认请求服务、不同用户提供不同的微前端版本服务;控制微前端版本上线、下线。也能提供微前端静态资源服务,方便本地开发微前端。
2 效果体验
用户开始浏览 https://micro.qinzhiqiang.cn 时,后端server-go
动态生成一个HTML页面,包含以下内容:微前端Metadata、加载Framework的CSS/JS。部署多个framework版本时,可给用户返回不同的framework版本。
<link href="/rmf-framework/v1.0.0/framework.08a0d251.chunk.css" rel="stylesheet">
<script>var rmfMetadataJSONP = {/*...*/} </script>
<script>function(){/*inline runtime-framework.xxxx.js javascript*/}</script>
<script src="/rmf-framework/v1.0.0/vendor-polyfill.a034dfcb.chunk.js"></script>
<script src="/rmf-framework/v1.0.0/vendor-redux.0367ad19.chunk.js"></script>
<script src="/rmf-framework/v1.0.0/framework.bcc5ef2e.chunk.js"></script>
2.1 微前端Metadata
Metadata中包含:各个微前端APP的CSS/JS资源路径与渲染位置、默认路由。可以F12查看全局变量rmfMetadataJSONP
。独立的请求URL为: https://micro.qinzhiqiang.cn/api/metadata/info 。对于不同的用户,可以返回不同的Metadata。在Demo中,我们随机返回不同的app-home
版本,F5刷新页面就可以体验。
{
"apps": [
{
"id": "3rd-echarts",
"dependencies": [],
"entries": [
"https://cdn.bootcdn.net/ajax/libs/echarts/4.8.0/echarts.min.js"
],
"renders": []
},
{
"id": "app-example",
"dependencies": [],
"entries": [
"/rmf-app-example/v1.1.0/app-example.1c738c30.css",
"/rmf-app-example/v1.1.0/app-example.c5d5e9b7.js"
],
"renders": [
{
"renderId": "root",
"routePath": "/app-example",
"componentKey": "default"
}
]
},
{
"id": "app-example-echarts",
"dependencies": [
"3rd-echarts"
],
"entries": [
"/rmf-app-example-echarts/792735ad/app-example-echarts.3c31ae87.css",
"/rmf-app-example-echarts/792735ad/app-example-echarts.67238f12.js"
],
"renders": [
{
"renderId": "app-example-sub",
"routePath": "/app-example/echarts",
"componentKey": "default"
}
]
}
],
"extra": {
"defaultRoute": "/home"
}
}
2.2 不同版本的主页
老版本主页:
新版本主页,React Logo像卫星一样远近距离围绕旋转:
2.3 Example嵌套Sub
微前端嵌套,Example App
中嵌套Example App Sub
。
Example App Sub
本身也是一个独立的微前端,可以按需加载到任意地方。同时,其内部中使用Webpack的import()
动态加载自身内容。
打字效果版本:
2.4 前端资源加载次序
从输入URL到显示主页:
- 用户浏览某个URL时,
server-go
返回SPA入口HTML文件。 - 解析入口HTML文件时,加载
framework
的CSS与JS文件。 - 执行
framework
JS内容时,渲染root
节点,其中的根路由使用<Switch>
渲染render == 'root'
的微前端。未匹配路由时,<Redirect>
到用户的默认路由/home
. - 使用
AsyncApp
组件渲染app-home
,动态加载其相关的CSS与JS文件。待app-home
的CSS/JS加载完成时,渲染app-home
动态注册的React组件HomeApp
。
跳转到/app-example/sub
路由:
- 框架使用
AsyncApp
组件渲染app-example
,动态加载app-example
的CSS/JS之后,渲染app-example
动态注册的React组件ExampleApp
。 ExampleApp
中使用AsyncApp
组件渲染app-example-sub
,动态加载app-example-sub
的CSS/JS之后,渲染app-example-sub
动态注册的React组件ExampleAppSub
。ExampleAppSub
中使用import()
动态加载其它内容:1.da13f959.chunk.css、1.1eedc88a.chunk.js。
跳转到/app-example/echarts
路由:
- 微前端之间有依赖关系,框架先加载依赖的
3rd-echarts
,再加载app-example-echarts
。 - 对
app-example-echarts
使用preload
技术,并行请求3rd-echarts
与app-example-echarts
的CSS/JS,提升用户体验。