[前端架构]微前端原理了解

微前端原理简介

大致方案

  • Nginx路由转发
  • iframe嵌套
  • Web Components
  • 组合式应用路由分发

实际项目中,一般是Nginx路由转发搭配某个微前端框架实现,而微前端框架又是由上面几种技术+一些补充混合实现的。

例如:

  • qiankun: function + proxy + with
  • micro-app: web components
  • wujie: web components 和 iframe。

使用具体的微前端框架时,主要考虑的点有

  • 应用间的通讯
  • 应用间的状态管理
  • js 沙箱
  • css 隔离
  • 预加载
  • 公共依赖的处理
  • 路由状态管理
  • 是否支持html entry
  • 应用支不支持保活
  • 子应用之间的互相嵌套
  • 是否有生命周期的设计

接下来看看微前端框架主要需要干一些什么。

核心功能点

一、监听路由变化

这块比较简单,了解一下即可。

  • 监听 hash 路由: window.onhashchange
  • 监听 history 路由history.go、history.back、history.forward 使用 popstate 事件 window.onpopstate
  • 监听popstats变化

监听路由的变化后,拿到当前路由的路径 window.location.pathname,然后根据 registerMicroApps 的参数 apps 查找子应用,然后去执行加载逻辑。

二、子应用加载

HTML Entry

子应用插槽绑定好一个dom节点,然后动态地插拔加载页面内容…

首先importHTML的参数为需要加载的页面url,拿到后会先通过 fetch方法 读取页面内容。例如:

export const importHTML = url => {
  const html = await fetch(currentApp.entry).then(res => res.text()

  const template = document.createElement('div')

  template.innerHTML = html

  const scripts = template.querySelectAll('script')

  const getExternalScripts = () => {
    console.log('解析所有脚本: ', scripts)
  }

  const execScripts = () => {}

  return {
    template, // html 文本
    getExternalScripts, // 获取 Script 脚本
    execScripts, // 执行 Sript 脚本
  }
}

最后的返回为一个对象,属性为:

- template:处理过的html模板

- assetPublicPath:静态资源地址

- getExternalScripts:获取前面解析的脚本数组的方法

- getExternalStyleSheets:获取前面解析的样式表数组的方法

- execScripts:执行该模板文件中所有的 JS 脚本文件,并且可以指定脚本的作用域 - proxy 对象

注意:内联script一般会用eval去执行。

接下来是处理模板的逻辑,js脚本和css链接会通过特定的方法去执行请求,请求回来后经过一层处理后才会作用于子应用中,最后处理完了就会渲染到绑定的节点上。

IFrame

简单省事,给个链接直接搞定,自带加载体系,JS、CSS隔离,但是弊端也很多:

  • ⽆论是使⽤postMessage还是通过 iframeEl.contentWindow 去获取 iFrame 元素的 Window 对象,⼜或者是直接⽗⻚⾯调⽤⼦⻚⾯⽅法:FrameName.window.childMethod();⼦⻚⾯调⽤⽗⻚⾯⽅法:parent.window.parentMethod();来通讯都并不是太过友好的事情,需要设计⼀套规范的通讯标准,实在过于麻烦,并且状态管理、公共依赖的处理等都能通过其他的方式封装实现

  • 路由状态丢失,刷新一下,iframe 的 url 状态就丢失了

  • dom 割裂严重,弹窗只能在 iframe 内部展示,无法覆盖全局,并且事件传递上存在者很大的问题,例如拖拽

  • 白屏时间太长,对于SPA 应用应用来说无法接受

  • 难以做预加载

  • 应用完全没办法保活,每次都是新的加载

  • iframe和主⻚⾯共享连接池,⽽浏览器对相同域的连接有限制,所以会影响⻚⾯的并⾏加载,出现iframe中的资源占⽤了可⽤连接⽽阻塞了主⻚⾯的资源加载

  • 样式和布局限制:IFrame 的内容在页面中是独立的,它们具有自己的 CSS 样式和布局上下文。这导致在微前端架构中难以实现全局样式的一致性,以及子应用之间的布局和交互的协调问题。

  • 浏览器安全性限制:由于安全策略的限制,IFrame 之间的跨域通信可能受到限制,特别是在涉及跨域资源访问和共享数据时。这可能导致在微前端架构中需要处理复杂的安全性问题。

三、子应用运行时隔离

JS沙箱(作用域隔离)
  • worker

    • 每个子应用对应一个worker脚本…
  • IFrame

    • 天然的JS沙箱
    • 可以直接使用隐藏的Iframe来专门执行js
  • LegacySandbox

    • 自己维护一个对象,与dom分隔开,动态地记录和保存
  • SnapshotSandbox

    • 使用Proxy配合Window完成维护工作,但还是有污染window的风险
  • ProxySandbox

    • 直接基于Proxy实现一个fakeWindow,直接完全隔离
    • ProxySandbox 是最完备的沙箱模式,完全隔离了主子应用的状态。

除了window上的东西还有一些需要相互隔离的副作用,比如事件监听、定时器等。监听劫持主要做了以下处理,了解即可:

  • patchTimer(计时器劫持)
  • patchWindowListener(window 事件监听劫持)
  • patchHistoryListener(路由监听)
CSS沙箱(样式隔离)
  • Shadow DOM

    • 可以参考web component的原理
    • strictStyleIsolation 模式下 qiankun 会为每个微应用的容器包裹上一个 shadow dom 节点,所有的子应用都被 #shadow-root 所包裹,从而确保微应用的样式不会对全局造成影响。
    • 作用域问题,例如内部弹窗到Body时样式丢失
  • CSS Scoped

    • 第三方依赖需要特殊处理

    • mount,和unmount时:统一走HTML Entry拦截处理加上公共前缀

    • 运行时动态加载的样式:

      有一些样式文件或者js文件是使用的远程请求的方式动态append到页面中的,这里面就又有一层隔离的问题了。

      qiankun的解决方案和上述的一些解决方案同出一辙:增强 appendChildinsertBefore 方法,并添加了一些其它的逻辑,让其根据是否是子应用决定 linkstylescript 元素的插入位置是在主应用还是微应用 ,并且劫持 script 标签的添加,支持远程加载脚本和设置脚本的执行上下文,也就是前面说的Proxy

四、父子应用通信

主要方案:

  • 全局eventBus(发布订阅模式)

  • props 通信,基座应用可以通过props向子应用注入数据和方法

  • window 通信,由于在设计上子应用运行的iframe的src和主应用是同域的,所以相互可以直接通信

具体框架实现方案

qiankun

qiankun我们可以认为是由single-spaimport-html-entry两个库结合,并进行二次开发的产物。下面简单介绍一下实现的大致方案。

子应用加载

使用的是上面提到的HTML Entry。

JS沙箱

使用的是上面提到的ProxySandbox。

CSS沙箱

qiankun 的样式隔离有两种方式,一种是严格样式隔离,通过 shadow dom 来实现,另一种是实验性的样式隔离,就是 scoped css,两种方式不可共存。

父子应用通信

使用的是GlobalState,原理其实就是上面提到的全局公共BUS

wujie

wujie 是一个iframe + webComponent 的解决方案,且很好地解决了Iframe做沙箱的痛点。

在应用 A 中构造一个shadowRoot 和iframe,然后将应用 B 的html写入shadowRoot中,js运行在iframe中,因为iframe的js隔离真的很完美。

即:子应用加载在一个shadowRoot里,JS在iframe中执行并代理到这个shadowRoot上。

子应用加载

Htmlentry + 渲染到shadowRoot上

创建了一个标签名为wujie-appWebcomponent,JS、CSS加载。

JS沙箱
  • 基本原理:直接把js丢在一个隐藏的iframe里执行
  • 细节:在iframe中拦截document对象,统一将dom指向shadowRoot,此时比如新建元素、弹窗或者冒泡组件就可以正常约束在shadowRoot内部。
CSS沙箱

基于上面说的shadowRoot

父子应用通信
  • Props 注入

    • 子应用通过 $wujie.props 可以拿到主应用注入的数据
  • window.parent 通信

    • 子应用和主应用同源,可以通过 window.parent 和主应用通信

    • window.document.querySelector("iframe[name=子应用id]").contentWindow.xxx
      
    • // 子应用调用
      window.parent.xxx
      
  • 去中心化的通信

    • 上面提到的EventBus

简易JS沙盒

const rawWindow = window

function exeCode(code, sandbox) {
  window.__MICRO_TOY_CONTEXT__ = sandbox.global
  const _code = `;(function(window, self) {
    with(window) {
    ${code}
  }).call(window.__MICRO_TOY_CONTEXT__,window.__MICRO_TOY_CONTEXT__)`

  // 规避with严格模式问题, new Function -- vuejs template compiler
  ;(0, eval)(_code)
}

export function createSandbox() {
  const fakeWindow = {}

  const sandbox = {
    global: {}, // proxy window
    run, // script run window inject
    stop, // 暂停沙箱
    isRun: false // 是否运行
  }

  function run(code) {
    // 访问fakeWindow的属性,没有的话,从全局window取

    // 设置fakeWindow的属性,设置到fakeWindow上

    sandbox.global = new Proxy(fakeWindow, {
      get(target, key) {
        if (Reflect.has(target, key)) {
          return Reflect.get(target, key)
        }

        const rawValue = Reflect(rawWindow, key)

        if (typeof rawValue === 'function') {
          const valueStr = rawValue.toString()

          // console、alert等方法正常从window取
          // 如果不是函数,且不是class
          if (!/^function\s+[A-Z]/.test(valueStr) && !/^class\s+/.test(valueStr)) {
            return rawValue.bind(rawWindow)
          }
          return rawValue
        }
      },

      set(target, key, value) {
        target[key] = value
        return true
      }
    })

    exeCode(code, sandbox)
  }

  function stop() {
    sandbox.isRun = false
  }

  return sandbox
}

参考资料

《基于wujie的解决方案来简单聊聊微前端 (2022年团队内分享PPT)》https://juejin.cn/post/7377567987119620147?searchId=202407042059284DD9DF8539CDD9CE2571

https://github.com/phodal/microfrontends?tab=readme-ov-file#%E7%BA%AF-web-components-%E6%8A%80%E6%9C%AF%E6%9E%84%E5%BB%BA

《微前端框架 之 qiankun 从入门到源码分析》https://juejin.cn/post/6885211340999229454#heading-34

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/783242.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

30多款简洁个人博客网站网页模板演示学习

30多款个人博客个人网站divcss,html在线预览,静态页面模板免费下载.这些简洁和优雅的博客网页模板,为那些想成为创建博客的个人或媒体提供灵感设计。网页模板可以记录旅游、生活方式、食品或摄影博客等网站。 http://www.bokequ.com/blog/1/ http://www.bokequ.com/blog/2/ htt…

近千含分步骤做法图片菜谱ACCESS\EXCEL数据库

菜谱类的数据已经有一些了,比如《近5万份有图菜谱大全》、《3万多条含图片的菜谱资料数据库》、《无图片的2万多条菜谱》、《5000个菜谱食谱大全》、《4千多带图片的美食菜谱数据内容采集》,但是我还是偏向更喜欢有步骤图片的菜谱,比如《2千8…

2025 百度提前批校招内推

百度2025校园招聘内推开始啦,被推荐人可以免笔试直接面试,提前批结果不影响校招,机会1,还可直推心仪部门,可扫描下面二维码或点击链接进行投递,快来投递你心仪的职位吧( 网申链接地址 &#xff…

机器学习的遗忘——基于文章“Forgetting“ in Machine Learning and Beyond: A Survey

文章概要 这篇调查文章仅关注选择性遗忘,承认遗忘某些信息可以通过允许模型优先考虑和保留更重要或相关的信息,以及保护用户隐私,从而带来好处。选择性遗忘(Selective forgetting)涉及有选择地忽略无关或噪声数据。这…

C语言 | Leetcode C语言题解之第220题存在重复元素III

题目&#xff1a; 题解&#xff1a; struct HashTable {int key;int val;UT_hash_handle hh; };int getID(int x, long long w) {return x < 0 ? (x 1ll) / w - 1 : x / w; }struct HashTable* query(struct HashTable* hashTable, int x) {struct HashTable* tmp;HASH_F…

亚马逊如何用自养号测评打造权重提升排名带来更多的自然流量

亚马逊通过自养号测评来提升流量是一种被广泛采用的运营手段&#xff0c;它可以帮助卖家快速提高商品的曝光度和吸引潜在买家。以下是自养号测评的详细分析&#xff1a; 一、自养号测评的定义与原理 自养号测评是指卖家通过注册并管理海外买家账号&#xff0c;对自家商品进行…

PyQT: 开发一款ROI绘制小程序

在一些基于图像或者视频流的应用中&#xff0c;比如电子围栏/客流统计等&#xff0c;我们需要手动绘制一些感兴趣&#xff08;Region of Interest&#xff0c;简称ROI&#xff09;区域。 在这里&#xff0c;我们基于Python和PyQt5框架开发了一款桌面应用程序&#xff0c;允许用…

java中Request和Response的详细介绍

1.Request和Response的概述 # 重点 1. service方法的两个参数request和response是由tomcat创建的void service(ServletRequest var1, ServletResponse var2) 2. request 表示请求数据, tomcat将浏览器发送过来的请求数据解析并封装到request对象中servlet开发者可以通过reques…

AI免费英语学习在线工具:Pi;gpt;其他大模型AI 英语学习智能体工具

1、pi(强烈推荐&#xff1a;可以安卓下载使用) https://pi.ai/talk &#xff08;网络国内使用方便&#xff09; 支持实时聊天与语音对话 2、chatgpt&#xff08;强烈推荐&#xff1a;可以安卓下载使用) https://chat.openai.com/ &#xff08;网络国内使用不方便&#xf…

element-ui el-select选择器组件下拉框增加自定义按钮

element-ui el-select选择器组件下拉框增加自定义按钮 先看效果 原理&#xff1a;在el-select下添加禁用的el-option&#xff0c;将其value绑定为undefined&#xff0c;然后覆盖el-option禁用状态下的默认样式即可 示例代码如下&#xff1a; <template><div class…

27_电子电路设计基础

电路设计 电路板的设计 电路板的设计主要分三个步骤&#xff1a;设计电路原理图、生成网络表、设计印制电路板。 (1)设计电路原理图&#xff1a;将元器件按逻辑关系用导线连接起来。设计原理图的元件来源是“原理图库”,除了元件库外还可以由用户自己增加建立新的元件&#…

@Builder注解详解:巧妙避开常见的陷阱

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 Builder注解详解&#xff1a;巧妙避开常见的陷阱 前言1. Builder的基本使用使用示例示例类创建对…

YOLOv5改进系列(32)——替换主干网络之PKINet(CVPR2024 | 面向遥感旋转框主干,有效捕获不同尺度上的密集纹理特征)

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制 YOLOv5改进系列(2)——添加CBAM注意力机制 YOLOv5改进系列(3)——添加CA注意力机制 YOLOv5改进系列(4)——添加ECA注意力机制 YO…

21. Java AQS 原理

1. 前言 本节内容主要是对 AQS 原理的讲解&#xff0c;之所以需要了解 AQS 原理&#xff0c;是因为后续讲解的 ReentrantLock 是基于 AQS 原理的。本节内容相较于其他小节难度上会大一些&#xff0c;基础薄弱的学习者可以选择性学习本节内容或者跳过本节内容。 了解什么是 AQ…

从无计划到项目管理高手,只需避开这两大误区!

在项目管理的过程中&#xff0c;制定计划是不可或缺的一环。然而&#xff0c;在实践中&#xff0c;我们往往会遇到两种常见的误区&#xff0c;这些误区不仅阻碍了计划的有效实施&#xff0c;还可能让我们在追求目标的道路上迷失方向。 误区一&#xff1a;认为没有什么可计划的…

Nacos 国际化

项目需要&#xff0c;后端异常信息需要进行国际化处理。所有想有没有方便易用的可选项。 1、国际化配置调整&#xff0c;不需要重启系统 2、可支持添加不同或自定义语言包&#xff08;就是配置的资源文件&#xff09; 参考&#xff1a; Nacos实现SpringBoot国际化的增强_spr…

硅纪元视角 | Speak火了!3个月收入翻倍,OpenAI为何频频下注?

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

LabVIEW自动探头外观检测

开发了一套基于LabVIEW的软件系统&#xff0c;结合视觉检测技术&#xff0c;实现探头及连接器外观的自动检测。通过使用高分辨率工业相机、光源和机械手臂&#xff0c;系统能够自动定位并检测探头表面的细微缺陷&#xff0c;如划痕、残胶、异色、杂物等。系统支持多种探头形态&…

王老师 linux c++ 通信架构 笔记(二)

&#xff08;7&#xff09;本条目开始配置 linux 的固定 ip 地址&#xff0c;以作为服务器使用&#xff1a; 首先解释 linux 的网口编号&#xff1a; linux 命令 cd &#xff1a; change directory 改变目录。 ls &#xff1a; list 列出某目录下的文件 根目录文件名 / etc &a…

Kubernetes运维工程师必备:K8s 基础面试题精编(一)

Kubernetes运维工程师必备:K8s 基础面试题精编(一) 1. 什么是Kubernetes?2. Kubernetes如何实现容器编排?3. 说出k8s的常见资源对象?4. 什么是pod?5. Deployment介绍及使用?6. statefulesets介绍及使用?7. statefulesets和deployment区别?8. 什么是调度器(Scheduler…