前端资源共享方案对比-笔记:iframe/JS-SDK/微前端
Author:zhoulujun Date:
前端页面资源如何分享,常见的有iframe,其次是js-sdk。这两类的在地图类工具经常用。微前端是最佳比较火的方式。本篇是他们的对比分析。
下一篇讲 BK-VISION如何在让用户自由选择 iframe/JS-SDK/微前端的模式共享
iframe
iframe嵌入是目前使用很广泛的一种嵌入方案,直接使用iframe标签+网页地址就可以嵌入。
iframe优劣
iframe优点
使用简单: iframe嵌入是目前使用很广泛的一种嵌入方案,直接使用iframe标签+网页地址就可以嵌入。
隔离性好:主页面和嵌入页面相互隔离,不存在脚本冲突,样式影响问题
iframe缺点
iframe标签性能消耗较大,过多的iframe标签会造成页面卡顿
子应用切换,每次都要重新加载资源,速度慢
iframe主子页面之间通信困难。
cookie无法跨域携带,子应用在做免登的时候处理麻烦
url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用
JS-SDK
SDK(Software Development Kit) 即软件开发工具包, 一般是一些软件工程师为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件时的开发工具的集合。
软件开发工具包 就是一种代码包,使开发人员能够为特定平台开发应用程序。SDK通常包括一个或多个API,编程工具和文档。
说的再通俗点就是一个面向开发者,针对特定领域的软件包。比如Java SDK(JDK),就是一个Java领域的软件包。基于它,开发人员就可以快速构建自己的Java应用。比较规范的SDK一般都会包含若干的API、开发工具集和说明文档。
JS SDK也无外于此,不过鉴于JS语言本身的特性,基于Ta封装的SDK更多常见于
UI组件库
性能监控工具, 如阿里 arms
统计分析工具
阿里云智能验证sdk
极验验证sdk
SDK优劣
sdk优点
实现较简单,可以在不允许跨域的情况下实现分享;
实现的业务逻辑更加灵活,可以根据应用需要,灵活地进行定制。
可以实现模块化的编程,大大减少了重复的工作量;
可以提高程序的可维护性和可扩展性
sdk方式缺点
体积较大:SDK 通常会包含大量的代码,可能会影响应用程序的下载速度和安装体验。
版本更新:随着软件版本的更新,SDK 也可能需要更新,如果不更新,可能会影响应用程序的正常运行。
难以维护:由于 SDK 包含大量的代码,如果其中出现了问题,开发者可能需要花费大量时间来解决问题。
限制自定义:使用 SDK 可能限制了开发者对应用程序的自定义,开发者可能无法根据自己的需求来定制应用程序。
SDK设计原则
如何设计SDK,其实更多取决于你的场景,或者SDK最终的用途。比如实现一个给网页调用的SDK与用于服务端的SDK就有明显的差异,但这之间确实存在着一些共通的目的:提高项目的开发效能, 安全性和便捷性等问题, 所以我们在设计 sdk 时一定要遵循一些原则, 如下:
最小可用性原则: 也就是没有必要的功能/代码尽量不额外添加, 使代码达到最简
即能用确定的方法实现,就不要再去搞复杂的内容。我理解,比如获取DOM,如果GetElementById可以实现,就不要再设计一下GetElementsByTagName、 document.querySelector等方法封装,除非有其他的开发需要无法满足。
最少依赖原则: 也就是没有必要的依赖坚决不添加, 以达到最低限度的外部依赖
SDK减少依赖,要避免Lodash、JQuery、Moment、Dayjs等库,尽可能自行实现必要的方法,或者引入尽量小的库。否则会导致SDK打包后过大,或者更新版本带来的兼容问题
易扩展: 插件化,最大限度支持扩展和自定义
模块化实现方法,尽量小的封装函数,保持函数功能的单一性原则,这样就可以更好的增加SDK的能力。
稳定性: 绝不能导致宿主应用崩溃,向后兼容, 可测试
减少BreakChange,绝不能导致载体应用崩溃,同时做好文档说明
我们的 sdk 就好像一个完整系统的一个零件, 可以和系统中的其他模块通信, 互相交换数据. 总体而言 sdk 是为宿主系统服务的。
计JavaScript SDK时需要考虑以下三个用例:
嵌入式控件 -嵌入到发布者网页上的小型交互式应用程序(Disqus,Google Maps,Facebook 窗体控件)
分析和指标 -用于收集有关访问者及其与发布者网站(GA,Flurry,Mixpanel)互动的数据
Web服务API包装器 -用于开发与外部Web服务进行通信的客户端应用程序。(Facebook Graph API)
明确了SDK的边界以及各部分的职责,结合前端监控的特性,我们可以开始设计SDK的整体框架了。
SDK如何实现
首先要明确我们写的SDK是用来做什么的?
比如我本次实现的是用户H5页面的一键登录和号码检测。
那么我们需要暴露两个实例,供其他开发者使用,为了满足易扩展的原则,我们将声明两个类,来实现(如果每个实例都很多能力,可以拆分成两个SDK也是可以的)
下面我们将通过剖析岳鹰前端监控SDK的设计过程,来看看上述的设计原则是如何应用到实际的开发过程中的。
明职责,定边界
前面章节提到,岳鹰前端监控SDK是前端稳定性和性能监控的SDK,主要面向前端H5领域。因此,稍加分析即可得出以下结论:
前端领域,稳定性方面主要的关注点
JS异常
资源加载异常
API请求异常
白屏异常
性能方面,核心的关注点
白屏时间
可交互时间(TTI)
首屏时间
FP / FMP / FCP 等
上述监控内容实际上都相对独立,因此我们可以把它们横向划分为如下几大部分:
明确了SDK的边界以及各部分的职责,结合前端监控的特性,我们可以开始设计SDK的整体框架了。
领域分析,模块划分
可以先看一下岳鹰前端监控SDK最后的整体模块划分:
SDK底层提供基础的能力,包括上面提到的内核、插件机制的实现、工具类库以及暴露给用户的基础API。
可以看到,我们前面提到的所有模块都以插件的形式存在,即各领域的功能都各自松散的做实现,这样使得底层能力更具通用性,同时扩展能力也更强,用户甚至也可以封装自己的插件。
Biz部分更多是对于不同宿主环境的多入口适配,当前支持浏览器、Weex以及NodeJS。
确定SDK的引用形式
SDK整体而言是一个大模块,前端模块有多种表现形式:ES Module、CommonJS、AMD/CMD/UMD,而在引用方面则大体分 CDN和 NPM两种。即无论我们实现的是哪种形式的模块,最终都是通过CDN或者NPM的方式提供给用户引用。
// ES Module import wpkReporter from 'wpkReporter' // CommonJS const wpkReporter = require('wpkReporter') // AMD,requireJS引用 require.config({ paths: { "wpk": "https://g.alicdn.com/woodpeckerx/jssdk/wpkReporter.js", } }) require(['wpk', 'test'], function (wpk) { // do your business }) 乍看有点眼花,但事实上今时今日的前端工程领域,已有很多利器可以帮助我们达到目的。比如webpack,通过简单的配置就可以构建出一个UMD的bundle。 // webpack.config.js module.exports = { output: { filename: '[name].js', path: `${__dirname}/dist`, globalObject: 'this', library: '[name]', libraryTarget: 'umd' } }
综上,我们可以通过webpack将SDK构建为一个UMD bundle,这样可以自动适配所有形式的模块。同时我们也将同时提供CDN和NPM两种引用方式,给用户更多选择。
SDK CDN资源优化
异步语法
为了将SDK包含在面向用户的环境中,使用异步语法加载脚本是一个好习惯。
这有助于优化使用SDK的网站上的用户体验。这种方法减少了SDK库干扰主要内容加载的机会。
针对现代浏览器时使用async/defer语法。
<script async src="http://<DOMAIN>.com/sdk.js"></script>
async/defer区别
defer 异步加载,但要等到dom文档全部解析完才会被执行。而且能保证顺序
async 异步加载,加载完就执行,async只能加载外部脚本,不能把js写在script标签里。
确定SDK的版本管理机制
微前端
微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将单页面前端应用由单一的单体应用转变为把多个小型前端应用聚合为一的应用。各个前端应用还可以独立开发、独立部署。简单说就是将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的小块,而在用户看来仍然是内聚的单个产品。
微前端的优劣概述
微前端优点
应用自治。只需要遵循统一的接口规范或者框架,以便于系统集成到一起,相互之间是不存在依赖关系的。
单一职责。每个前端应用可以只关注于自己所需要完成的功能。
技术栈无关。你可以使用 Angular 的同时,又可以使用 React 和 Vue。
微前端缺点
应用的拆分基础依赖于基础设施的构建,一旦大量应用依赖于同一基础设施,那么维护变成了一个挑战。
拆分的粒度越小,便意味着架构变得复杂、维护成本变高。
技术栈一旦多样化,便意味着技术栈混乱
微前端的设计理念
中心化:应用注册表。这个应用注册表拥有每个应用及对应的入口。在前端领域里,入口的直接表现形式可以是路由,又或者对应的应用映射。
标识化应用。 我们需要一个标识符来标识不同的应用,以便于在安装、卸载的时候,能寻找到指定的应用。一个简单的模式,就是通过康威定律来命名应用。
应用生命周期管理。
高内聚,低耦合。
在micro-frontends上对微前端做了如下阐述:
微前端背后的想法是将网站或 Web 应用程序视为由独立团队拥有 的 功能的组合。每个团队都有不同的业务领域或任务,它关心和专注于,一个团队是跨职能的,从数据库到用户界面,端到端地开发其功能,但这个想法并不新鲜,它与自包含系统的概念有很多共同之处。在过去,这种方法被称为垂直系统的前端集成。但微前端显然是一个更友好、更简洁的术语。
进一步提炼出以下5点微前端的核心思想
与技术无关
每个团队都应该能够选择和升级他们的堆栈,而无需与其他团队协调。自定义元素是隐藏实现细节同时为其他人提供中性界面的好方法。隔离团队代码
不要共享运行时,即使所有团队都使用相同的框架。构建自包含的独立应用程序。不要依赖共享状态或全局变量。建立团队前缀
就尚无法隔离的命名约定达成一致。命名空间 CSS、事件、本地存储和 Cookie,以避免冲突并明确所有权。优先使用本机浏览器功能而不是自定义 API
使用浏览器事件进行通信,而不是构建全局 PubSub 系统。如果你真的需要构建一个跨团队的 API,尽量让它尽可能简单。构建弹性站点
即使 JavaScript 失败或尚未执行,您的功能也应该很有用。使用通用渲染和渐进增强来提高感知性能。
微前端之模块联邦
以上阶段1里强调的5点特性看起来似乎给微前端下了一个相当完美的定义,以至于后来的各种微前端框架都在这5个核心思想指导下去做实现,直到2020年 webpack 5 module federation
(以下我们简称MF)模块联邦诞生,并对此特性在官网做了一个很简单的介绍:
模块联合的动机,让多个单独的构建应该可组合为一个应用程序,这些单独的构建之间不应该有依赖关系,因此它们可以单独开发和部署,这通常被称为微前端,但不限于此。
细细玩味这段话,我们发现webpack 5视角下的微前端仅需要包含3个特点:独立开发、独立部署、运行时组合。
如果你基于webpack 5 MF
发布过远程模块,你会知道它并不包含micro-frontends站点里提到的隔离团队代码这个关键点,尽管我们知道涉及到代码运行隔离需要用上shadowrealm(未来的隔离方案)、proxy window、iframe 等方案,但MF并未强调这一点,所以看起来MF理解下的微前端是阉割版的微前端?
再思考微前端
我们将micro-frontends 和 webpack 5 两个出处的微前端定义做一个对比,并提出一个灵魂的拷问,是否以下表达成立?
微前端 = 技术无关 + 独立部署 + JS&CSS隔离
事实上随着模块联邦这个概念开始逐渐深入人心,微前端架构已经分裂出两个方向:
容器型微前端
我们把以single-spa为代表的这一类方案统称为微容器,在single-spa走红之后市面很多基于single-spa二次封装的库如雨后春笋般涌出,典型的代表作如阿里的qiankun,意在解决一些single-spa未解决的问题并让其更适合企业级开发,同时也诞生了很多非singlespa系的框架,如京东的micro-app、腾讯的wujie等,它们的细节实现各有差异,包含js沙箱隔离、css隔离、iframe编排、启用web-component、window代理、接入过程等各个地方的细节也各有千秋,但它们都一个很显著的特点,对应的模块粒度是整个应用,做出的产品可以理解为一种以宏观态的方式来组合多个应用交付给用户使用。
试想一下,你不会极端到以运行时隔离的方式去渲染多个按钮吧?
模块型微前端
相较于微容器宏观态的组合应用方式,微模块 则可以形容为微观态的组合方式,它的粒度更小,小到可以是一个函数,一个基础的组件,对于开发者来说,引入微模块和引入一个普通的js包没有任何区别,他们在使用上也并无任何区别,但恰恰是这一点!是它和微容器最大差异之一,微模块的使用方式回归到了js语法本身。
微容器和微模块在开源社区均有很多实现,它们的特点很明显
所以基于两种方案搭建的微前端架构也是区别非常明显的
微前端技术该如何选型
我们只要深刻理解到微模块是天然就假定运行在同一个宿主里(即同个一js环境里),它要解决的核心问题是大规模独立构建的应用间如何快速动态共享公共模块这个棘手问题。
例如你有100个内部的前端项目依赖了lodash-1.0.0,突然该库暴露了一个漏洞,你需要100个前端项目全部重新构建升级到1.0.1才代表安全解决此漏洞问题,而基于模块联邦的lodash,你仅需要构建一次mf-lodash,其他项目即可引用到最新的安全代码。
再综合考虑到两个以下关键点,就很容易得出技术如何选型的结论了:
1 是否需要多技术栈混合开发(react、vue…)
2 是否需要多版本技术栈同时迭代等(vue2, vue3…)
因为微模块是微观态的组合方式,它可以迅速的将你逐渐庞大的应用拆为一个个可独立部署的组件并再次组合起来,相对于微容器方案,大多数时候或许你的新项目并不需要介入微容器。
当你需要组合一些第三方应用或自己的其他技术栈应用,并需要让它的就是被隔离起来安全的运行时,微容器是你武器库里适合拿出来的强力武器。
事实上它们搭配起来混合使用将是相辅相成的完美组合,你可以先使用微容器再接入微模块做跨应用模块动态共享,或先使用微模块再套上微容器做运行时隔离,取决于你的项目发展到了什么阶段。
生命周期
前端微架构与后端微架构的最大不同之处,也在于此——生命周期。微前端应用作为一个客户端应用,每个应用都拥有自己的生命周期:
Load,决定加载哪个应用,并绑定生命周期bootstrap,获取静态资源
Mount,安装应用,如创建 DOM 节点
Unload,删除应用的生命周期Unmount,卸载应用,如删除 DOM 节点、取消事件绑定
这部分的内容事实上,也就是微前端的一个难点所在,如何以合适的方式来加载应用——毕竟每个前端框架都各自不同,其所需要的加载方式也是不同的。当我们决定支持多个框架的时候,便需要在这一部分进入更细致的研究。
微前端应用间的关系来
微前端应用间的关系来看,分为两种:基座模式(管理式)、自组织式。分别也对应了两者不同的架构模式:
基座模式
通过一个主应用,来管理其它应用。
设计难度小,方便实践,但是通用度低。
自组织模式
应用之间是平等的,不存在相互管理的模式。
设计难度大,不方便实施,但是通用度高。
当前基座模式用的比较多。
拆分应用
按技术拆分
路由分发式。通过 HTTP 服务器的反向代理功能,来将请求路由到对应的应用上。
前端微服务化。在不同的框架之上设计通讯、加载机制,以在一个页面内加载对应的应用。
微应用。通过软件工程的方式,在部署构建环境中,组合多个独立应用成一个单体应用。
微件化。开发一个新的构建系统,将部分业务功能构建成一个独立的 chunk 代码,使用时只需要远程加载即可。
前端容器化。通过将 iFrame 作为容器,来容纳其它前端应用。
应用组件化。借助于 Web Components 技术,来构建跨框架的前端应用。
微件(widget),指的是一段可以直接嵌入在应用上运行的代码,它由开发人员预先编译好,在加载时不需要再做任何修改或者编译。
而微前端下的微件化则指的是,每个业务团队编写自己的业务代码,并将编译好的代码部署(上传或者放置)到指定的服务器上,在运行时,我们只需要加载相应的业务模块即可。对应的,在更新代码的时候,我们只需要更新对应的模块即可
按业务拆分
按照业务拆分。
按照权限拆分。
按照变更的频率拆分。
按照组织结构拆分。利用康威定律来进一步拆分前端应用。
跟随后端微服务划分。实践证明, DDD 与事件风暴是一种颇为有效的后端拆分模式,对于前端来说,它也颇有有效——直接跟踪后端服务。
每个项目都有自己特殊的背景,切分微前端的方式便不一样。即使项目的类型相似,也存在一些细微的差异。
参考文章:
什么是微前端架构?它有什么优缺点以及构建思路 https://blog.51cto.com/u_14785218/3007821
如何iframe_前端: 如何快速将应用封装成js-sdk? https://blog.csdn.net/weixin_29086203/article/details/112582351
关于微前端,你理解到究极奥义了么? https://cnodejs.org/topic/6368996df0ccaef869e0e3f9
如何打造一款标准的JS SDK? https://developer.aliyun.com/article/777451
如何开发JS-SDK https://www.ucloud.cn/yun/127966.html
帮你对比多种微前端方案 https://juejin.cn/post/6898268972178178061
转载本站文章《前端资源共享方案对比-笔记:iframe/JS-SDK/微前端》,
请注明出处:https://www.zhoulujun.cn/html/webfront/engineer/Architecture/8925.html