微前端(Micro-Frontends)是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。
微前端并不是前端领域的新概念。早期希望前端工程能够像后台的微服务一样,项目分开自治,核心的诉求是:
1、兼容不同技术栈
2、将项目看作页面、组件,能够复用到不同的系统中
早期比较成熟的 single-spa,从早期 React 的现代框架组件生命周期中获得灵感,将生命周期应用于这个应用程序,即将整个页面作为组件。
后来蚂蚁金融团队孵化了基于 single-spa 的 qiankun 架构,将微前端进一步的深耕,目标直指巨石应用业务难题,旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用 ( Frontend Monolith) 后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。
本人在深入实践微前端之后,深感 qiankun 受制于前端架构的定位,无法使用 Nodejs 等能力快速解决快速发布,构建,管理的困境,因此在此基础上做了一定程度的 APAAS 探索,将本文的项目作为 APAAS 应用快速集成到其他业务系统。
首先基于作者自己的思考,给大家梳理下 qiankun 微前端的渲染流程,方便不了解微前端的同学有个大体的认识。
假如你不了解微前端的话,对于这个新事物的学习和探索,一般是按照 5Ws 的学习规律来学习:
本文也按照类似的方式一个具体的案例去梳理 qiankun 的核心概念。
本文有一个业务系统 A,希望集成业务系统 B 的欢迎卡片配置功能页面。
那么 who 包含 qiankun 客户端、主应用 A、微应用 B 三部分,三者协作完成了微前端的设计。从产品使用的角度来看,第一步则是注册路由,确定微前端启动的时机和启动渲染的内容(when 和 where)。
import { registerMicroApps, start } from'qiankun';
registerMicroApps([
{
name: 'smart-contact-cms-index', // app name registered
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/yourActiveRule',
},
]);
start();
上面的实例十分简单,通过 name 来作为微应用 B 的唯一标识, entry 作为微应用 B 的资源加载地址。当路由映射到 '/yourActiveRule' 时,则请求微应用 B 的页面资源地址,解析其中的 JS 和 CSS 资源,将微应用 B 的页面渲染到 id 为 #yourContainer 的 DOM 节点。
const packageName = require('./package.json').name;
module.exports = {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
};
上面是一段微应用 webpack 的配置,library 就是注册路由时的唯一标识 name 了。这里也十分好理解为啥 qiankun 要在构建资源时给 js 增加标识。
qiankun 客户端 entry 配置的 HTML 页面资源的地址,拉取到页面资源之后劫持 HTML 解析,自行构建 Http 请求去加载 JS 文件。很明显页面中肯定不止一段 JS 代码,就需要标识来标识那一段的入口 JS 代码了。
下面截取了 index.js 头部代码
!function(e,t){
"object"==typeofexports&&"object"==typeofmodule?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeofexports?exports["smart-contact-cms-index"]=t():e["smart-contact-cms-index"]=t()
}
仅仅修改微应用 B 的 webpack 是远远不够的,常规前端页面工程会在 index.js 文件中直接调用类似 ReactDOM.render API,直接去渲染到特定节点。那 qiankun 根本无法干预渲染过程,因此需要将渲染的实际交给 qiankun 客户端来控制,qiankun 受前端组件化的影响,也希望将页面做成一个暴露生命周期的组件,导出相关的生命周期钩子。
// 单独 App 运行
if (!isMicroApp) {
// 非微前端渲染
renderApp({});
}
// 导出 qiankun 生命周期
export const bootstrap = async () => {
console.log('[smart-contact-cms] bootstrap');
};
export const mount = async (props: any) => {
console.log('[smart-contact-cms] mount', props);
normalizeObject(props.mainAppState);
// 作为微应用运行
renderApp(props);
};
export const unmount = async (props: any) => {
const { container } = props;
ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
};
细心的同学会发现有个 isMicroApp 的变量可以控制启动微前端渲染还是独立 APP 渲染,这个实际是 qiankun 客户端启动后,会在 window 上挂在 __POWERED_BY_QIANKUN__ 变量标识运行时环境。
这里还有个小细节就是微应用的 JS CSS 文件请求是属于 Ftech/XHR 类型,说明 js 文件的请求是 qiankun 客户端自行构造的。
至于为何要这么做?我的理解是为了实现微前端的沙箱功能。
qiankun 客户端干预 script 标签和 link 标签的加载过程的难度,和上面的方式相比明显是更简单的,但是这种方式很明显是缺乏安全性,特别是在面临第三方 SDK 加载的时候,例如微信 web SDK 的引入,就无法通过其安全校验,具体可以详细看下 FAQ 1 的案例介绍。
一句话总结上述过程:
在 qiankun 的框架下,一个页面集成到另外一个页面系统中,最关键的核心点就是将微应用封装成具有生命周期的页面组件,使得 qiankun 可以调用 React 或者 Vue 的 render 能力,将页面渲染到对应的 DOM 节点。
本文由于篇幅限制,只介绍 Client 端、Server 端的接口协议代理、微应用改造,其他部分更多是偏向于自动工程化和项目管理的方面,之后有时间给大家详细介绍下。
Client 端是经过业务封装后的 qiankun SDK,内部集成了经过 qiankun 改造的各自子应用系统,对外暴露以下接口:
appInfo 参数
{
"app_id": "xxx",
"prefetch": true,
"signUrl": "remote-url"
}
这个接口主要是为了初始化部分信息,包括微应用的鉴权配置、是否启动预加载、微应用的标识。其中鉴权配置可能会让大家感到疑惑。
在介绍 qiankun 的渲染过程中提到
这里还有个小细节就是微应用的 JS CSS 文件请求是属于 Ftech/XHR 类型,说明 js 文件的请求是 qiankun 客户端自行构造的。
这里必然要涉及前端的跨域问题,尤其是当主应用和微应用的域名不一致时,qiankun 客户端如何能够在跨域的限制之下获取到微应用的页面资源?本文的解决方案是主应用提供一个鉴权秘钥下发的接口 signUrl,这个接口由微应用提供也可以,将秘钥信息下发到 cookie 中,通过配置 qiankin 自定义 fetch 方法,带上这些鉴权信息。
本文只是其中一个解决方案,官方也提供了一些通用方案以供参考《 微应用静态资源一定要支持跨域吗 》。
动态加载微应用,其 FloadConfig 参数如下:
interface FLoadConfig {
container: string;
pageId: string;
props?: FMicroProps; // 传入对应微应用所需要的参数
}
interface FMicroProps {
loginUrl?: string, // 登录的重定向地址
baseApiUrl?: string,
dispatch?: (value: { type: string; data: any }) =>void,
pageInfo?: FPageConfig,
useNativeRoute?: number;
extra?: any,
}
loginUrl 是鉴权失败跳转的登录地址;baseApiUrl 是后台请求的地址;useNativeRoute 决定微前端采用何种路由方式加载。
本文在这个阶段主要做两方面的突破:
baseApiUrl 这里默认提供了基于腾讯云的鉴权下发能力,各个业务系统只需要按照规范去对接腾讯云 API 即可,比较远离前端,在这里不做过多解读。
在实际的业务场景中,主应用和微应用互相无法感知到对方,因此其路由有可能会互相冲突,这里通过 useNativeRoute 参数来控制微应用的路由模式。
1、useNativeRoute = 0 采用配置端数据返回的路由路径 2、useNativeRoute = 1 采用当前页面 hash 前缀 + 配置端数据返回的路由路径作为新路由 3、useNativeRoute = 3 采用当前的页面路由不做任何改动, 默认 0
底层的实现逻辑则是在 renderApp 传入主应用的路由前缀,改造相对比较简单,但是很实用。
exportconst mount = async (props: any) => {
console.log('[smart-contact-cms] mount', props);
// 删除不能序列化的内容
normalizeObject(props.mainAppState);
// 作为微应用运行
renderApp(props);
};
返回 MicroApp 实例:
详情参考参考 qiankun 官方文档 。
监听主子应用的事件和数据,callback 函数 ({type: string; data: any}) => { 处理程序 },目前通用返回加载完成事件 type: load,其他自定义事件由主子应用自行定义。
返回值:返回取消订阅的句柄。
其他接口在这里不做赘述了。
实现页面低成本接入是微前端的重要愿景之一,也是吸引大家持续探索的核心原因。 理想业务场景下,一个被微应用改造之后的微应用集成到其他业务系统,应该无需关心后台接口,开箱即用。但是后台业务系统具有各自独立的鉴权、账户、业务逻辑,相互之间差异性极大,完全无法做到开箱即用。
传统的方式是主应用去主动接入微应用的后台系统,缺点很明显,主应用要了解微应用系统的后台逻辑,且每接入一个系统就要重复上述的工作,成本高且低效。
微前端脱胎于微应用,我们也希望微应用自己也实现微服务级别的逻辑自治,依托于腾讯云 API 托管,对外提供微应用自身的服务。这样的好处是主应用只需要要对接腾讯云生态,即可实现鉴权、账号转化、监控等能力。主应用和子应用只需要做一次,无需重复,缺点就是必须要依托于腾讯云生态。假如你的业务本身就是依托于微信、企业微信、腾讯云发展起来的,上述方式值得引入。
一个业务系统并不是一开始就有被集成的价值的,往往是在业务发展到一定程度,经过市场验证其价值之后,大家才会明确这个业务系统具有微应用改造的价值,这就导致一个窘迫的境地:你需要改造已经成型的项目,使其成为可快速接入的微应用。
qiankun 的微应用改造相对比较简单,一般在开启严格沙箱模式之后,微应用和主应用之间建立比较好的环境隔离,你并不需要太多的工作。
首先样式隔离,参考下面 FAQ 2、css 隔离 章节的介绍。其核心的麻烦在于 qiankun 启动严格沙箱木事之后,会导致 dialog、Modal 等组件无法找到 body 节点,进而无法挂在到 DOM 中。
其次 JS 作用域隔离,这里主要是一些第三方库会在 window 上挂在单例实例,导致主应用和微应用之间单例配置相互覆盖,常见于日志上报、微信 SDK、QQ SDK 等第三方应用。解决方案分为两个方向:
本文采用的是第二种方式,假如主应用需要进行数据共享或者配置共享,可以通过主应用和微应用之间的参数和数据传递的方式来实现共享,微应用提供丰富的监听 hooks。
微信和企业微信的 SDK 是不可以自行构建 Http 请求加载的,这是由于其安全策略导致的,且每次返回的内容有安全限制的改动,无法复用。 因此必须要在 html 的 header 中引入,但 qiankun 会对 html header 所有 script link 资源构造请求链接,进而导致获取第三方 SD K 的请求报错,整个 qiankun 客户端加载微应用进程报错,无法加载出对应页面。
官网在常见问题给了三个解决方案:
前两种方案需要在乾坤渲染函数中增加一个对应的参数,这里面有个坑点,则是 prefetchApps 不支持这些参数,因此一旦启用预加载函数,则会导致渲染函数的传入配置失效,因此需要关闭使用预加载函数。
微前端核心理念:解耦 / 技术栈无关,简单来说就是希望微应用之间,基站应用和微应用之间的技术栈可以互相隔离,从而各种定制自己的技术体系来实现开发效率和产品质量的最优化配置,这也是微前端的核心价值体现。
然而理想是丰满的,现实是骨感的。主流的沙箱模式是通过创建一个独立的作用域隔离作用域链,同时克隆全局变量来实现的,但是这种隔离 + 克隆方案并不完美,在复杂运行场景中,无论性能还是安全性都是难以保证的,特别是 CSS 的隔离。
本文基于乾坤的微前端架构,在此基础上做了一些查漏补缺的补充。
首先是基础页面的 CSS,采用的是成熟的 CSS module 方案,简单来说就是将 CSS 变成局部生效,每个 class 生成一个独一无二的名字。从最早的 Less、SASS,到后来的 PostCSS,再到最近的 CSS in JS,都是为了解决 CSS 全局生效带来的副作用。
css: {
loaderOptions: {
less: {
javascriptEnabled: true,
modifyVars: {
'@ant-prefix': 'industryAnt', // 前缀
'primary-color': '#0052d9',
'link-color': '#0052d9',
'btn-primary-color': '#ffffff',
'btn-danger-bg': '#e34d59',
'disabled-color': 'rgba(15, 24, 41, .3);',
'btn-disable-bg': '#eaedf2',
'text-color': '#0F1829',
},
module: true,
},
postcss: {
plugins: [
AddAntClass({ prefix: 'industryAnt' })
],
}
},
}
参考上面的配置,直接开启即可。
在前端开发过程中,我不可避免的会使用到各种前端的组件库,例如 antd、echarts,且都支持自定义主题配置,假如基站应用和微应用之间主题配置冲突了,就需要我们采用类似 CSS module 的方案,将各自应用的 CSS 应用范围控制在各自的组件控件内。
细心的同学已经发现,我上面的代码就包含了 antd 的类名定制的配置 - '@ant-prefix': 'xxxAnt' ,给所有 antd 组件增加类名前缀。
你以为到这里就完美解决了吗?不,这才刚开始。回到我们的业务场景中去,很多页面的复用并不是一开始就设计好的,往往是产品中后期发现业务体系之间存在高度的复用性,你需要对老的项目进行改造使它支持微应用架构模式。经验丰富的你发现已有项目中,存在很多全局 class 样式,甚至全局地方类库的组件 class 样式,如果手动去调整,那绝对 boom 。
这里给大家提供一些工程化的方法。第三方类库的样式类名往往都是有通用类名前缀,这是我们能够解决这个问题的基本前提条件,我们在在 CSS 构建阶段对 css class 进行替换和调整。有兴趣的同学可以看下 less、 postcss 提供的插件机制,以下代码仅供参考。
const postcss = require('postcss');
module.exports = postcss.plugin('postcss-add-css-prefix', function(opts = {}) {
const {
prefix = 'xxxAnt'
} = opts
// 接收两个参数,第一个是每个css文件的ast,第二个参数中可获取转换结果相关信息(包括当前css文件相关信息)
function plugin(css, result) {
if (!prefix) return; // 没传入prefix,不执行下面的逻辑
css.walkRules(rule => { // 遍历当前ast所有rule节点
const {
selector
} = rule;
// 只有当节点是ast根节点直属子节点时才添加前缀
// 简单做了容错处理,只要带有根选择器的都不添加前缀,本身带有前缀了也不添加
// 加了个flag,防止节点更新后重复执行该逻辑进入死循环
if (rule.parent.type === 'root' && selector.indexOf('.ant-') >= 0 && selector.indexOf(`${prefix}-`) < 0 && !rule.flag) {
rule.flag = true
const clone = rule.clone();
clone.selector = selector.split(' ').map(item => item.replace('.ant-', `.${prefix}-`)).join(' ');
rule.replaceWith(clone)
}
})
}
return plugin
})
经过以上调整,样式终于隔离成功,微前端接入后展示完美,此时测试的反馈打破了你的幻想:confirm 的弹窗提示不显示了。经过排查发现 ant-prefix + css 插件的方案,无法对动态生成的组件样式进行调整,因此它的样式会丢失。 最终在 Ant Design of React 官方的 FAQ 中找到了线索,
但是这个方案并不适用于本文使用 antdv 1.x 版本的微应用项目,不支持这些 API。在 3.x 版本 FAQ 中给了一个推荐方案。
这种方案是适用于 vue 3.x,对于 vue 2.x 的项目则需要使用 "@vue/composition-api" 来兼容 getCurrentInstance 等 API。经过实践发现本文的项目的 antv 的版本是 1.x 的,无法支持 appContext 参数,最终探索了最终的解决方案。
this.$confirm({
prefixCls: `${ANT_PREFIX}-modal`,
title: '正在上传,确定要停止吗?',
cancelText: '取消',
cancelButtonProps: {
props: {
prefixCls: `${ANT_PREFIX}-btn`,
},
},
okText: '停止',
centered: true,
okButtonProps: {
props: {
type: 'danger',
prefixCls: `${ANT_PREFIX}-btn`,
},
} asany,
onOk: () => {
},
} asany)
confirm 类型定义中并不包含 prefixCls,因此需要使用 as any 忽略其类型校验,实际会透传到底层 rc-dialog 组件中。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/S8eFeKaRKT6LxfIMtN9L5g
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。