0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

主流的微前端的实现库原理及其用法

454398 来源:vivo互联网技术 作者:Tan Xin 2020-10-10 14:24 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

本文对微前端的概念和场景进行科普,介绍一些主流的微前端的实现库及其用法,并讲解部分这些库的原理和实践知识。

一、微前端

在项目迭代中,随着业务的发展壮大,项目的功能模块通常也会越来越多。可能原来所有的代码模块都在一个仓库里,由一个团队负责。但随着功能模块越来越多,一个团队可能负责不过来,需要多个团队来专门维护不同的模块。相应的代码也会被拆到多个仓库里,并且各模块能独立开发、部署更新。通常虽然项目被拆成了多个模块,但为了维持整体统一性以及用户体验,各模块依然都会挂在统一的入口下。

上面所述场景就是典型的微前端场景,类似于后端的微服务架构,它将web应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。

通常,要实现上面类似的需求,我们很容易会想到使用iframe的方式来实现。在入口框架中用iframe来显示子模块的页面,切换子模块时,iframe也跟着切换成对应子模块页面的url。

虽然iframe是比较容易实现的,但通常也会有一些问题:

显示区域受限制,比如子项目中显示弹窗蒙层时,蒙层只会覆盖iframe区域,无法覆盖整个页面,内容也无法真正居中。

页面浏览记录无法自动被记录,刷新页面后iframe又自动回到首页。

全局上下文完全隔离,变量不共享,页面间通信比较麻烦,比如子项目与主题框架、子项目之间通信等,只能采用postMessage方式。

速度较慢,每次进入子应用时都要重建整个上下文。

上面所列问题,有些可以解决,有些甚至都没法或者很难解决。总的来说,iframe是一个比较快捷的方案,但不是最好的方案,会对体验有很多限制。如果强行打各种patch,复杂度又上来了,最后可能得不偿失。

二、single-spa

刚才我们讲了iframe实现微前端的一些弊端,主要原因就是这些应用还是在各自独立的页面内,这就导致了一些天然的限制。而single-spa微前端方案结合了MPA和SPA的优势,可以在单个页面内集成多个应用,并且是技术栈无关的。

如上图就是采用single-spa实现微前端的整体流程:

资源模块加载器:用来加载子项目初始化资源。我们将子项目的入口js构建成umd格式,然后使用模块加载器远程加载,通常会使用SystemJs(不是必须)通用模块加载器来进行加载。

子应用资源配置表:用来记录各个子应用的入口资源url信息,以便在切换不同子应用时使用模块加载器去远程加载。因为每次子应用更新后入口资源的hash通常会变化,所以需要服务端定时去更新该配置表,以便框架能及时加载子应用最新的资源。

注意:single-spa本身是不支持子应用资源列表的,每个子应用只能将自己所有初始化资源打包到一个入口js中。如果子应用初始化资源有多个文件(可以通过webpack-manifest-plugin生成应用初始化资源清单),就需要按照上述方式来添加额外处理。

1、框架入口

{ “imports”: { “app1”: “http://localhost:8081/js/app.js”, “app2”: “http://localhost:8082/js/app.js”, “single-spa”: “https://cdnjs.cloudflare.com/ajax/libs/single-spa/4.3.7/system/single-spa.min.js”, “vue”: “https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js”, “vue-router”: “https://cdn.jsdelivr.net/npm/vue-router@3.0.7/dist/vue-router.min.js”, “vuex”: “https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.2/vuex.min.js” } }location.pathname.startsWith(‘/app2’) ) // 启动 singleSpa.start(); }) })()

为了简单展示,上述只是框架入口html的一个简单demo,并没有解析子应用资源配置表来加载相应资源。在入口中我们注册了子应用,并确定了子应用的激活时机。

子应用资源配置表是完全自定义的,只要入口加载器这边按照约定的规范来解析加载资源,并按照single-spa的生命周期钩子来处理好这些资源的挂载。

我们还可以将一些公共的资源库资源库(如上vue、vue-router等)抽取到入口中,这样各个子应用不需要再包含这些库文件了,可以减小资源文件大小,提升加载速度。子应用中构建时要外置这些库,比如用webpack构建时如下:

externals: [‘vue’, ‘vue-router’, ‘vuex’]

2、子应用入口

import ‘。/set-public-path’ import Vue from ‘vue’ import App from ‘。/App.vue’ import router from ‘。/router’ import singleSpaVue from ‘single-spa-vue’ Vue.config.productionTip = false if (process.env.NODE_ENV === ‘development’) { // 开发环境直接渲染 new Vue({ router, render: h =》 h(App) }).$mount(‘#app’) } const vueLifecycles = singleSpaVue({ Vue, appOptions: { render: (h) =》 h(App), router } }) export const bootstrap = vueLifecycles.bootstrap export const mount = vueLifecycles.mount export const unmount = vueLifecycles.unmount

如上我们的子应用是vue开发的,需要用single-spa-vue来包装下,然后导出生命周期的钩子函数。为了方便开发,我们可以判断下运行环境,如果是开发环境的话,就直接渲染到页面上。

set-public-path.js

细心的同学就会注意到,子应用代码中运行了set-public-path.js。那么这个文件是干嘛用的呢?先来看下:

import { setPublicPath } from ‘systemjs-webpack-interop’ setPublicPath(‘app1’, 2)

从名字也能看出,systemjs-webpack-interop是针对在systemjs中使用webpack构建的bundle的场景的。众所周知,webpack构建代码时,可以通过output.publicPath选项指定要加载资源的url前缀,这在传统的spa中不会有问题,但在single-spa的页面中可能会有问题。比如output.publicPath: ‘/xx’的情况,webpack会认为异步资源加载的url域名为当前页面的域名,这在传统spa中不会有问题,但在single-spa的场景下异步资源就会加载失败,因为子应用的异步资源与框架页面的url域名并不是一样的。所以需要各个子应用自行在入口中执行上述代码,这会设置子应用的异步资源url前缀与子应用的入口js一致,这样加载的路径就不会错误了。

setPublicPath代码如下:

export function setPublicPath(systemjsModuleName, rootDirectoryLevel) { if (!rootDirectoryLevel) { rootDirectoryLevel = 1; } if ( typeof systemjsModuleName !== “string” || systemjsModuleName.trim().length === 0 ) { throw Error( “systemjs-webpack-interop: setPublicPath(systemjsModuleName) must be called with a non-empty string ‘systemjsModuleName’” ); } if ( typeof rootDirectoryLevel !== “number” || rootDirectoryLevel 《= 0 || !Number.isInteger(rootDirectoryLevel) ) { throw Error( “systemjs-webpack-interop: setPublicPath(systemjsModuleName, rootDirectoryLevel) must be called with a positive integer ‘rootDirectoryLevel’” ); } let moduleUrl; try { moduleUrl = window.System.resolve(systemjsModuleName); if (!moduleUrl) { throw Error() } } catch (err) { throw Error( “systemjs-webpack-interop: There is no such module ‘” + systemjsModuleName + “’ in the SystemJS registry. Did you misspell the name of your module?” ); } __webpack_public_path__ = resolveDirectory(moduleUrl, rootDirectoryLevel); } function resolveDirectory(urlString, rootDirectoryLevel) { const url = new URL(urlString); const pathname = new URL(urlString).pathname; let numDirsProcessed = 0, index = pathname.length; while (numDirsProcessed !== rootDirectoryLevel && index 》= 0) { const char = pathname[--index]; if (char === “/”) { numDirsProcessed++; } } if (numDirsProcessed !== rootDirectoryLevel) { throw Error( “systemjs-webpack-interop: rootDirectoryLevel (” + rootDirectoryLevel + “) is greater than the number of directories (” + numDirsProcessed + “) in the URL path ” + fullUrl ); } url.pathname = url.pathname.slice(0, index + 1); return url.href; }

三、single-spa的不足

如上面提到过,如果子应用初始化资源有多个文件(比如通常我们会将css、npm模块抽离成一个单独的文件),那么我们就要自行维护一个子应用资源列表并做一些额外处理,这个工作往往也是比较繁琐的;

将多个子应用都集成在一个页面中,css和js都是很有可能产生冲突的。虽然我们可以制定规范,比如各子项目使用唯一地命名前缀等,但这种人为约定往往又是不那么靠谱。对于css,我们还可以在构建时使用一些工具自动添加前缀,这样可以比较靠谱的避免冲突;对于js来说,比较靠谱的方式可能就是人为制造沙箱,让子应用的js都运行在各自的沙箱中,但这实现起来就比较复杂了。

四、qiankun

其实,已经有个基于single-spa的开源库qiankun已经帮我们解决了上面提到的问题,其有如下特征:

解析子应用入口时,不是解析的js文件,二是直接解析子应用的html文件。就算子应用更新了,其入口html文件的url始终不会变,并且完整的包含了所有的初始化资源url,所以不用再自行维护子应用的资源列表了。

子应用挂载时,会自动进行一些特殊处理,可以确保子应用所有的资源dom(包括js添加的style标签等)都集中在子应用根节点dom下。子应用卸载时,对应的整个dom都移除了,这样也就避免了样式冲突。

提供了js沙箱,子应用挂载时,会对全局window对象代理、对全局事件监听进行劫持等,确保各子应用都运行在自己的沙箱内,这样也就避免了js冲突。

包含多个spa应用的demo

子应用 dom 结构如下

当然,在前端越来越庞大复杂的场景中,微前端方案也不是银弹,但确是值得探索实践的方向。

五、参考文献

single-spa

qiankun

可能是你见过最完善的微前端解决方案
编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 前端
    +关注

    关注

    1

    文章

    241

    浏览量

    18677
  • Web应用
    +关注

    关注

    0

    文章

    16

    浏览量

    3665
  • 微服务架构
    +关注

    关注

    0

    文章

    26

    浏览量

    3150
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    与标准C的区别

    主要的差异: 1.代码(数据)量更小(对比上图)。 2.缺少一些不常用标准库函数,比如:文件 I/O 的库函数。 3.最大程度优化代码量,可能会导致有些代码运行速度更慢。 4.可用于RTOS这类
    发表于 12-09 07:49

    ProfiNet嵌入式板卡,主流替代可实现ProfiNet工业以太网的应用实例

    ProfiNet嵌入式板卡,主流替代可实现ProfiNet工业以太网的应用实例
    的头像 发表于 12-01 17:11 909次阅读
    ProfiNet嵌入式板卡,<b class='flag-5'>主流</b>替代可<b class='flag-5'>实现</b>ProfiNet工业以太网的应用实例

    射频前端“硬骨头”之战:昂瑞啃下中高端模组市场

    恭喜昂瑞二反挂网,这是射频前端行业的重大事件!笔者曾有幸与昂瑞团队有过接触与交流,今天也来说说对昂瑞和射频前端这个赛道的认识。    
    的头像 发表于 10-13 15:49 1246次阅读

    昂瑞:射频前端的“破局者”,迈向中高端模组新纪元

    。 在射频前端国产替代的浪潮中,不少企业凭借分立器件切入市场,实现了初步的规模扩张。然而,真正的竞争高地始终在于中高端模组市场——这里技术壁垒高、附加值大,也是国际厂商长期垄断的领域。 昂瑞则选择了一条更难走
    的头像 发表于 10-12 15:03 291次阅读

    浅析昂瑞的技术竞争力

    近期昂瑞已经完成二轮问询,在IPO的冲刺道路上又前进了一小步,作为射频前端行业老兵,昂瑞一直走稳健经营路线,成立第二年即推出高集成CMOS GSM射频前端芯片,直接将传统GSM射频
    的头像 发表于 10-10 10:23 328次阅读

    昂瑞冲刺科创板IPO:国产射频前端龙头,打破垄断驶入5G黄金赛道

    射频前端模组领域实现关键技术突破,成为国内少数打破国际厂商垄断格局的企业之一。此次IPO,昂瑞拟公开发行不超过2,488.29万股,募集资金约20.67亿元,主要用于5G射频前端芯片
    的头像 发表于 10-09 18:22 3783次阅读

    昂瑞,凭啥?

         近几年射频前端市场异常火爆,多家射频前端公司应运而生,其中不乏有卓胜、唯捷创芯两家优秀的射频前端公司,并分别于2019年和2022年上市成功。昂瑞
    的头像 发表于 10-03 19:49 309次阅读

    国产射频前端行业,第二次冲锋

    起来,催生出了卓胜、唯捷创芯、昂瑞、飞骧科技、锐石创芯和慧智等多家优秀的射频前端厂商。2023年,随着国内头部手机厂商突破制裁,成功量产基于全国产芯片的旗舰机,国产射频
    的头像 发表于 09-11 12:49 501次阅读

    射频前端的反内卷之路

    近期随着卓胜和唯捷创芯半年报公布,两家头部射频前端公司扣非后净利润都出现不同程度的亏损,一时间关于射频前端内卷和关于射频卷到“血流成河”的文章不断爆出,笔者采访了多位未上市或者在上市准备阶段的射频
    的头像 发表于 08-29 10:39 505次阅读

    射频前端公司如何抉择?IDM或Design House

    近年来,随着半导体行业受到的关注度和注入的资金不断提升,国内射频前端厂商也发展迅速,催生出如卓胜、唯捷创芯、昂瑞、飞骧、锐石创芯和慧智等优秀厂商。这些企业崛起到一定规模后,资金实
    的头像 发表于 08-05 15:28 694次阅读
    射频<b class='flag-5'>前端</b>公司如何抉择?IDM或Design House

    三款主流国产数据的技术特点

    随着数字经济的快速发展和数据安全要求的提升,国产数据正迎来前所未有的发展机遇。在信创浪潮推动下,达梦数据、TiDB、华为高斯数据等国产数据产品技术日趋成熟,在金融、政府、电信等
    的头像 发表于 07-14 11:08 810次阅读

    2.4 GHz 前端 skyworksinc

    电子发烧友网为你提供()2.4 GHz 前端相关产品参数、数据手册,更有2.4 GHz 前端的引脚图、接线图、封装手册、中文资料、英文资料,2.4 GHz 前端真值表,2.4 GHz 前端
    发表于 06-20 18:31
    2.4 GHz <b class='flag-5'>前端</b> skyworksinc

    芯片前端设计与后端设计的区别

    前端设计(Front-end Design):聚焦于电路的逻辑功能实现。本质上是在“纸上”设计电路,包括芯片要“干什么”,要“如何运算”。
    的头像 发表于 05-16 14:56 1009次阅读

    用腾讯ima和Deepseek建立个人信知识

    腾讯AI图书馆来了,是时候升级英飞凌工业半导体的《信图书馆》啦。(对于工程师零难度)近日腾讯推出了AI智能工作台ima.copilot,本人亲测,可以在信平台上建立方便实用的私人图书馆
    的头像 发表于 02-25 17:33 2029次阅读
    用腾讯ima和Deepseek建立个人<b class='flag-5'>微</b>信知识<b class='flag-5'>库</b>

    前端的作用

    前端的作用 在智能手机中,“前端”一词可以指代两个不同的概念:手机前端开发和射频前端技术。以下是这两个概念在智能手机中的作用: 手机前端开发
    的头像 发表于 01-03 14:03 888次阅读