首页 » 搜狗SEO » js跳转和ifame影响seo_微前端常识梳理

js跳转和ifame影响seo_微前端常识梳理

访客 2024-10-16 0

扫一扫用手机浏览

文章目录 [+]

什么是微前端,微前真个方案有哪些,微前端方案的比拟,微前真个适用场景,微前真个实现思路,微前真个底层源码剖析,微前端技能的延伸--Vue如何利用React项目的组件和web componments,以及微前真个一些开源项目。

1. 什么是微前端

微前端是一种将前端运用程序拆分成更小,更独立的部分,以便于团队协作和掩护的架构模式,可以理解为项目更加细粒度。

js跳转和ifame影响seo_微前端常识梳理 js跳转和ifame影响seo_微前端常识梳理 搜狗SEO

微前端许可不同的团队独立开拓和支配各自的功能模块,同时保持全体运用程序的同等性和折衷性,可以理解为一个项目分为了多个模块,多个模块可以自己开拓和支配,符合我们高内聚,低耦合的期望。

js跳转和ifame影响seo_微前端常识梳理 js跳转和ifame影响seo_微前端常识梳理 搜狗SEO
(图片来自网络侵删)

微前真个核心思想是将前端运用程序视为一个由多个独立的微运用组成的整体,每个微运用都可以独立开拓,测试,支配和运行。

微前端可以帮助团队更好的管理繁芜的前端运用程序,提高开拓效率和代码质

2. 场景引入

我列出一些场景出来,大家可以思考下如何实现以及对我们的增益。

Vue,React,jQuery这三个技能栈的项目,领导让你把这三个项目合为一个。
你的Vue,React项目不用npm install,不用***node_modules,如何运行。
你的项目可以调用其他项目的组件,不用copy代码,便是直接调用。
多个项目之间可以进行状态互换(这个延伸下没准可以增加个单点登录的实现方案)。
UI组件库的开拓,每个组件的开拓都不用考虑会影响到其他组件,而且可以实现可插拔以及增量构建。

实在还有很多好玩的场景,我就不一一列举了,上述这些都可以用微前端来实现。

3. 方案梳理

微前真个实现方案有很多,我做一个比较全面的梳理,目前各种前端方案层出不穷,找到最适宜自己团队业务的方案极为主要。

Single-SPA用于构建微前端运用的JavaScript框架,它可以将多个独立的运用程序组合成一个整体运用程序。
是一个将多个单页面运用聚合为一个整体运用的 JavaScript 微前端框架。
在同一页面上利用多个前端框架,而不用刷新页面。
不限技能栈。
支持独立支配每一个单页面运用。
新功能利用新框架,旧的单页运用不用重写可以共存。
有效改进初始加载韶光,延迟加载代码。
文档地址:zh-hans.single-spa.js.org/docs/gettin…qiankun基于Single-SPA的微前端框架,它供应了更加完全的微前端办理方案,包括运用程序的注册,加载,通信等功能。
基于 single-spa 封装,供应了更加开箱即用的 API,不限技能栈。
HTML Entry 接入办法,让你接入微运用像利用 iframe 一样大略。
样式隔离,确保微运用之间样式相互不滋扰。
JS 沙箱,确保微运用之间全局变量/事宜不冲突。
资源预加载,在浏览器空闲韶光预加载未打开的微运用资源,加速微运用打开速率。
umi 插件,供应了 @umijs/plugin-qiankun 供 umi 运用一键切换成微前端架构系统。
文档地址:qiankun.umijs.org/zh/cookbookPiral用于构建微前端运用的框架,支持多种前端框架和库,同时供应了一些额外的功能,如插件系统和扩展性。
渐进迁移。
共享库。
共享现有布局和程序框架。
文档地址:github.com/smapiot/pir…Bit用于构建和共享组件的平台,可以用于构建微前端运用,Bit供应了一些核心的AP和工具,例如组件注册,版本管理,依赖管理,测试和构建等,可以快速构建和支配微前端运用,这个建媾和Cloud Alfa搭配利用。
I具有传统单体式前真个安全性和健壮性。
介接入办法大略、可伸缩性强。
通过 大略的解耦代码库、自治团队、小型定义良好的 API、独立的发布管道 和 持续增量升级,增强事情流程。
文档地址:bit.dev/docs/quick-…Module Federation用于构建微前端运用的框架,基于webpack,供应了一些高等功能,例如动态加载和模块化,远程动态容器,可以实现项目间的组件共享,依赖共享,函数共享,状态共享,其核心思想是代码统一。
是webpack给出的微前端方案。
使 JavaScript 运用可以动态运行另一个 JavaScript 运用中的代码,同时可以共享依赖。
依赖自动管理,可以共享 Host 中的依赖,版本不知足哀求时自动 fallback 到 Remote 中依赖。
共享模块粒度自由掌控,小到一个单独组件,大到一个完全运用。
既实现了组件级别的复用,又实现了微做事的基本功能。
共享模块非常灵巧,模块中所有组件都可以通过异步加载调用。
我做了一个Module Federation的知识梳理,比官方文档好理解一些:juejin.cn/post/719804…文档地址:webpack.js.org/concepts/mo…PuzzleJS用于构建微前端运用的框架,可以将一个大型的前端运用拆分成多个小型的子运用,并实现子运用之间的通信和协作。
SEO 友好,在做事端进行准备和渲染。
当片段所需的 api 涌现故障时,PuzzleJs 可担保其他页面片段仍正常事情。
文档地址:github.com/puzzle-js/p…Micro-app基于微前端架构的前端运用程序,采取了Single-SPA框架作为根本,同时也进行了一些定制化的开拓,目前Micro-app已经在京东的多个业务场景中得到了运用俏丽如京东商城,京东金融,京东物流等。
利用大略,接入微前端本钱低。
零依赖。
兼容所有框架(不须要供应脚手架工具)。
供应了JS沙箱、样式隔离、元素隔离、预加载、资源地址补全、插件系统、数据通信等一系列完善的功能。
文档地址:zeroing.jd.com/docs.html#/Luigi基于微前端架构的前端运用程序,采取了Single-SPA框架作为根本,同时也进行了一些定制化的开拓,Luigi的特点是易于利用和扩展,它供应了一些高等功能,例如动态加载,按需加载,异步加载等,同时也供应了一些插件和扩展机制,可以让开发职员可以根据自己的需求进行定制化开拓。
Luigi在Zakabdo,SAP,ThoughtWorks中已经得到了运用。
许可 Web 运用程序与运用程序包含的微前端进行通信。
可以配置路由、导航、授权和 UX 元素等设置。
文档地址:github.com/SAP/luigiMosaic和上面的Luigi,Micro-app实在大差不差。
利用了片段(Fragments)的机制,这些片段由单独的做事程序供应做事,并根据模板定义在运行时组合在一起。
由一堆软件包组成,这些软件包处理不同的问题,例如路由、布局、模板存储、乃至展示 UI。
文档地址:www.mosaic9.org/

做个总结,上面我梳理出来了九种微前端架构的方案,我们可以根据自己的需求来选择一个得当的方案,我们可以不闇练地利用它,但是我们该当去知道它以及它的特性,有利于我们对需求的方案选择。

毕竟,很多时候,选择比努力更主要。

4. 方案比拟

我这边准备列出两个框架来比拟,分别是qiankun和Module Federation,qiankun算是我们最常用也是最周知的微前端框架了,而Module Federation则是webpack5的新特性,功能十分强大,我也很看好Module Federation的发展。

4.1 核心思想

qiankun的核心思想是: 路由统一。

可以理解为,qiankun可以做到同一项目,子运用是Vue,React平分歧技能栈编写而成,子运用集成到了一个主运用中,主项目通过路由统一来管理这些子运用。

这意味着,主项目卖力处理所有的路由要求,并将这些要求转发给了相应的子运用,这种办法使得多个子运用之间的情由管理更加统一和折衷,从而提升了全体运用的可掩护性和可扩展性。

Module Federation的核心思想是:代码统一。

Module Federation的核心思想是将多个独立的Webpack构建之间共享模块,从而实当代码统一。

这意味着,多个独立的运用可以共享一个代码库,从而避免了代码的重复和冗余,就像多个运用共享一个代码库中的组件,依赖,乃至函数,我们的这些运用乃至可以做到不***依赖,利用UI组件,不须要公共函数,全都利用其他运用的,来进行开拓。

4.2 技能实现

qiankun利用了浏览器真个路由和通信机制,通过web components实现运用之间的隔离和通信。

Module Federation是在webpack层面上实现的,通过共享模块和动态加载实现模块之间的通信和复用。

4.3 集成方案

qiankun以单页运用为核心,可以将多个子运用作为独立的SPA运行。

SPA:单页面运用程序,这是一种Web运用程序的架构模式,其特点是在一个页面中加载所有的HTML,Css,JavaScript资源,通过JavaScript动态的更新页面内容,实现无需刷新的页面效果。

可以理解为,在qiankun的架构中,主运用是一个单页面运用,卖力管理路由和状态,而子运用也是单页面运用,卖力详细的业务逻辑,每个子运用都可以独立运行,也可以通过qiankun框架集成到主运用中,实现了多个子运用在同一个页面中运行的效果。

这种办法实现了多个独立的子运用间的隔离和解耦,同时也可以共享主运用的路由和状态管理。

Module Federation以多个模块作为核心,可以将多个子运用作为多个模块运行。

可以理解为在Module Federation的架构中,每个子运用都可以抛出自身的状态,路由,组件,函数,依赖等供其他子运用获取,我们也可以做到多个中央模块乃至直接去中央化,例如A,B,C三个项目,A利用了B共享的组件,B利用C共享的组件,C利用A共享的组件,我们也可以以A和B为核心,C利用的A与B的共享。

这个和next.js的模块化方案很相似,是很不错的模块化方案。

4.4 运用处景

qiankun适用于多个独立的子运用须要集成到一个主运用的场景。

例如电商平台的商品详情页,购物车页,订单页等,在这种情形下,每个子运用都是独立的单页面运用,可以通过qiankun框架集成到主运用中,便是完全的电商网页了。

Module Federation适用于多个独立的模块须要共享和复用的场景。

例如企业级运用的公共组建,工具库,业务模块等。

4.5 资源加载

qiankun在加载子运用时,须要加载全体子运用的资源。

这些资源包括HTML,Css,JavaScript等文件,因此在切换子运用的时候可能会涌现加载韶光过长的问题。

Module Federation子加载子运用,只须要加载子运用的入口文件和须要共享的模块,而不须要加载全体子运用的资源。

Module Federation可以自动共享模块,因此加载子运用时不会涌现加载韶光过长的问题。

4.6 资源冗余

qiankun加载子运用时,如果多个子运用中都引入了同一个库,导致重复加载,会涌现资源冗余的问题。

Module Federation通过自动共享模块来避免了资源冗余,不会重复加载同一个库或者依赖。

4.7 资源更新

qiankun在切换子运用时,须要重新加载全体子运用的资源。

Module Federation可以通过自动共享模块来实现资源的实时更新。

4.8 内存占用

qiankun在切换子运用时,须要卸载当前子运用的所有组件和状态,然后加载新的子运用,会导致内存占用过高,影响性能。

Module Federation可以通过自动共享模块来减少内存占用。

4.9 内存管理

qiankun须要手动管理子运用之间的内存,包括组件和状态等。

Module Federation通过自动共享模块来减少内存管理的繁芜度。

4.10 内存透露

qiankun在切换子运用时,如果涌现了未能精确卸载当前子运用的组件和状态等场景,则会涌现内存透露的问题。

Module Federation通过自动共享模块来避免内存透露。

4.11 通信办法

qiankun须要手动配置子运用间的通信办法。

例如利用CustomEvent,postMessage等,这让qiankun的通信效率变低以及通信繁芜度提高。

Module Federation通过自动共享模块来实现通信。

4.12 兼容性

qiankun支持IE11及以上版本的浏览器,但不支持微信小程序和支付宝小程序等非浏览器环境。

Module Federation须要webpack5及以上版本,不支持webpack4及以下版本,也不支持IE11及以下版本的浏览器。

4.13 样式隔离机制

qiankun利用CSS Modules和Scoped CSS等技能来实现样式隔离,但须要手动配置。

这种办法可能涌现样式冲突和样式性能的问题,都须要手动办理。

Module Federation通过自动共享模块来实现样式隔离。

4.14 JS隔离机制

qiankun利用沙箱技能来实现JS隔离,每个子运用都运行在独立的沙箱中,从而避免了JS代码之间的冲突。

沙箱技能可能会影响JS性能。

Module Federation通过自动共享模块来实现JS隔离。

可能会涌当代码冲突,须要开拓者手动办理。

4.15 生态

qiankun是一个相对独立的项目,生态相对较小,但是可以与其他前端框架和工具集成利用。

Module Federation的生态相对完善,可以与其他webpack插件和工具集成利用,例如webpack-dev-server,webpack-merge等。

5. 实现方案

比拟了qiankun和Module Federation之后,我们来看一下这两个微前端框架的实现方案。

5.1 qiankun运用隔离:qiankun采取了基于Web Components的运用拆分方案,将全体运用拆分成了多个独立的Web组件,可以独立开拓,独立支配,独立运行。
每个子运用都运行在独立的沙箱环境中,避免了运用之间的命名冲突和样式污染。
运用加载:qiankun采取了Single-SPA的运用加载机制,可以动态加载和卸载子运用,实现了运用的动态化管理。
在加载子运用时,qiankun会先加载子运用的manifest.json文件,获取子运用的基本信息和依赖关系,然后再加载子运用的入口文件。
通信机制:qiankun采取了基于CustomEvent的通信机制,实现了子运用之间的通信和数据共享。
在子运用中,可以通过window.dispatchEvent()方法触发CustomEvent事宜,然后在父运用中通过window.addEventListener()方法监听CustomEvent事宜,实现子运用和父运用之间的通信。
资源加载:qiankun采取了基于Fetch的资源加载机制,可以实现子运用的异步加载和缓存管理。
在加载子运用时,qiankun会先检讨本地缓存,如果缓存中已经存在该子运用,则直接从缓存中加载,否则再通过Fetch API从做事器上加载。
沙箱环境:qiankun采取了基于Proxy的沙箱环境,可以实现子运用的隔离和安全性保障。
在加载子运用时,qiankun会为每个子运用创建一个独立的沙箱环境,通过Proxy工具对子运用的全局变量和方法进行代理,避免了子运用之间的相互影响。
打包优化:qiankun采取了基于Webpack的打包优化,可以实现子运用的按需加载和代码分割,提高了运用的性能和用户体验。
在打包子运用时,qiankun会将子运用的代码分割成多个小块,然后在加载子运用时,只加载当前须要的代码块,避免了不必要的资源摧残浪费蹂躏。
模块化管理:qiankun采取了基于ES6 Module的模块化管理,可以实现子运用的模块化开拓和管理,提高了代码的可掩护性和可复用性。
在子运用中,可以通过export关键字将模块暴露出去,然后在父运用中通过import关键字引入模块,实现子运用和父运用之间的模块化管理。
5.2 Module Federation远程模块加载:Module Federation采取了远程模块加载的办法,可以将模块从一个运用加载到另一个运用中。
在加载远程模块时,Module Federation会先从远程运用中获取模块的描述信息,然后再通过网络要求获取模块的代码。
模块描述信息:Module Federation中的模块描述信息包括了模块的名称、版本、入口文件等信息。
在加载远程模块时,Module Federation会先从远程运用中获取模块的描述信息,然后再根据描述信息加载模块的代码。
共享模块:Module Federation可以实现不同运用之间的模块共享,可以将一个运用中的模块共享给其他运用利用。
在共享模块时,Module Federation会将模块的代码打包成一个独立的库,然后在其他运用中通过远程模块加载的办法利用该库。
动态加载:Module Federation可以实现动态加载模块,可以根据用户的需求动态加载不同的模块。
在动态加载模块时,Module Federation会先从远程运用中获取模块的描述信息,然后再根据描述信息动态加载模块的代码。
Webpack插件:Module Federation是基于Webpack实现的,它供应了一系列的Webpack插件,可以实现模块的共享、动态加载和远程模块加载等功能。
在利用Module Federation时,须要在Webpack配置文件中添加相应的插件。
模块映射表:Module Federation会天生一个模块映射表,用于记录不同运用之间的模块依赖关系。
在加载远程模块时,Module Federation会根据模块映射表查找模块的依赖关系,然后再加载相应的模块。
模块缓存:Module Federation会缓存已经加载的模块,避免重复加载和网络要求。
在加载远程模块时,Module Federation会先检讨本地缓存,如果缓存中已经存在该模块,则直接从缓存中加载,否则再通过网络要求获取模块的代码。
5.3 方案总结

上面讲述了两个微前端库框架由哪些功能模块组成,以及功能模块的实现方案,我们须要知道该框架有这个功能,至于详细的底层代码实现,我后面会梳理。

6. Web Componments

上面提了这么多Web Componments,那这个东西到底是什么呢,我可以不闇练,但是我们须要知道它。

Web Components是一种新的Web开拓技能,它可以将Web运用拆分成多个独立的组件,每个组件都可以独立开拓、测试和支配。

它的涌现缘故原由是由于早期组件生态很乱,有各种各样的框架和库,都有各自的规范,导致一个团队切换框架很困难 。

为理解决这种分解的形式,让 Web 组件模型统一化,以是才有了Web Components规范的涌现。
目标是供应一种威信的、浏览器能理解的办法来创建组件。

但是有一定的性能问题:

vue是通过 模板->虚拟DOM->真实DOM 来实现自定义组件的。

web components是在浏览器中通过 自定义组件->Shadow Dom 实现自定义组件的。

浏览器中组件越多,vue的js打算韶光比web components越少,性能越好。

Web Components由三个紧张技能组成:Custom Elements、Shadow DOM和HTML Templates。

Custom Elements:Custom Elements是一种自定义元素的技能,可以创建自定义的HTML元素。
通过Custom Elements,开拓者可以创建自己的HTML标签,并定义标签的行为和样式。
Custom Elements可以实现组件的封装和复用,提高了代码的可掩护性和可复用性。
Shadow DOM:Shadow DOM是一种DOM的封装技能,可以将DOM树封装在一个独立的浸染域中。
通过Shadow DOM,开拓者可以将组件的样式和行为封装在一个独立的浸染域中,避免了组件之间的样式污染和命名冲突。
HTML Templates:HTML Templates是一种HTML模板的技能,可以创建可复用的HTML模板。
通过HTML Templates,开拓者可以将组件的HTML构造封装在一个模板中,然后在须要利用组件时,动态地插入模板中的HTML构造。

Web Components的优点包括:

组件化开拓:Web Components可以将Web运用拆分成多个独立的组件,每个组件都可以独立开拓、测试和支配。
组件化开拓可以提高代码的可掩护性和可复用性。
样式隔离:Web Components可以通过Shadow DOM实现样式的隔离,避免了组件之间的样式污染和命名冲突。
HTML模板:Web Components可以通过HTML Templates创建可复用的HTML模板,提高了组件的可复用性和可掩护性。
自定义元素:Web Components可以通过Custom Elements创建自定义的HTML元素,提高了代码的可读性和可掩护性。
跨平台兼容:Web Components可以在不同的浏览器和平台上运行,具有很好的兼容性和可移植性。

Web Components的缺陷包括:

学习本钱:Web Components须要节制多个技能,包括Custom Elements、Shadow DOM和HTML Templates,学习本钱较高。
兼容性:Web Components在一些老旧的浏览器上可能存在兼容性问题,须要进行兼容性处理。
性能问题:Web Components可能存在性能问题,须要进行优化和测试。
7. 适用场景

上面我引入了一些场景,供大家思考,但是微前真个适用场景远不止我引入的那些,详细可以分为下面十类。

大型单页运用(SPA)的拆分:将一个弘大的单页运用拆分成多个小型运用,每个运用都可以独立开拓、测试和支配。
多团队协作开拓:不同团队可以独立开拓和掩护自己的微前端运用,通过统一的框架和协议进行集成和交互。
前端模块化:将前端运用拆分成多个独立的模块,每个模块都可以独立开拓、测试和支配,提高代码复用性和可掩护性。
多措辞支持:不同措辞的前端运用可以通过微前端框架进行集成和交互,实现多措辞支持。
前后端分离:将前端运用和后端运用分离,通过微前端框架进行集成和交互,实现前后端分离。
多端适配:将前端运用拆分成多个独立的模块,每个模块都可以适配不同的终端,如PC端、移动端、小程序等。
做事化架构:将前端运用拆分成多个独立的做事,每个做事都可以独立支配和扩展,实现做事化架构。
动态加载:通过微前端框架实现动态加载,根据用户需求动态加载不同的前端运用和模块,提高用户体验和性能。
统一管理:通过微前端框架实现统一管理,可以集中管理前端运用和模块的版本、依赖和配置等信息,提高管理效率和可控性。
代码复用:通过微前端框架实当代码复用,不同的前端运用和模块可以共享同一份代码,提高代码复用性和可掩护性。
8. 微前端和iframe的比较

首先,我们再仔细理解一下iframe。

iframe是HTML中的一个标签,用于在一个网页中嵌入另一个网页或文档。

它可以将一个网页作为一个独立的窗口嵌入到另一个网页中,使得用户可以在同一个页面中同时浏览多个网页或文档。

利用iframe可以实现以下功能:

在一个网页中嵌入另一个网页或文档,实现多个网页的同时浏览。
在一个网页中嵌入广告或其他内容,实现网页内容的扩展。
在一个网页中嵌入***或音频,实现网页的多媒体功能。
在一个网页中嵌入舆图或其他运用程序,实现网页的交互性和实用性。

利用iframe须要把稳以下几点:

iframe的src属性必须指向一个有效的网页或文档。
iframe的宽度和高度必须指定,否则可能会导致页面布局混乱。
iframe的内容可能会被浏览器的安全策略限定,例如跨域访问等。
iframe的加载速率可能会影响全体页面的性能,因此须要谨慎利用。

总之,iframe是一种非常有用的HTML标签,可以实现网页内容的扩展和多媒体功能,但须要把稳安全性和性能问题。

iframe确实存在很多缺陷,但是在选择一个方案的时候还是要详细场景详细剖析,它可能在当下很盛行,但它不一定在任何时候都是最优解:iframe的这些缺陷对我来说是否能够接管?它的缺陷是否有其它方法可以填补?利用它到底是利大于弊还是弊大于利?我们须要在优缺陷之间找到一个平衡。

以是:

如果页面本身比较大略,是一个没有弹窗、浮层、高度也是固定的纯信息展示页的话,用iframe一样平常没什么问题;如果页面是包含弹窗、信息提示、或者高度不是固定的话,须要看iframe是否霸占了全部的内容区域,如果是像下图这种经典的导航+菜单+内容构造、并且全体内容区域都是iframe,那么可以放心大胆地考试测验iframe,否则,须要慎重考虑方案选型。

比拟总结:

如果是一些比较大略的业务场景,我们是可以直策应用ifame的,没有必要为了大略的业务场景而套上微前真个架构。

但是如果业务场景繁芜或者iframe不知足,我们就可以考虑微前真个架构。

我们不能由于iframe在业界名声不好而直接去否定它,我们还是须要将它定为一种方案,列入我们的参考当中。

9. 项目架构

如果我们利用的微前端,那么我们全体项目架构我们须要有清晰的思路。

9.1 qiankun

利用qiankun搭建微做事,全体项目的架构梳理如下:

主运用:主运用是全体项目的入口,卖力加载和管理所有子运用。
主运用利用qiankun供应的API来注册和启动子运用,同时也卖力处理子运用之间的通信和状态共享。
子运用:子运用是独立的运用程序,可以利用不同的技能栈和框架来构建。
子运用须要利用qiankun供应的API来注册和暴露自己的生命周期钩子,以便主运用可以管理它们的生命周期。
路由管理:qiankun供应了一种基于history模式的路由管理方案,可以让主运用和子运用之间共享路由信息。
主运用可以利用qiankun供应的API来注册和管理路由,同时也可以将路由信息通报给子运用。
状态管理:qiankun供应了一种基于props和emit的状态管理方案,可以让主运用和子运用之间共享状态信息。
主运用可以利用qiankun供应的API来注册和管理状态,同时也可以将状态信息通报给子运用。
共享组件库:为了提高开拓效率和代码复用性,可以将一些通用的组件封装成独立的库,并在主运用和子运用中共享利用。
qiankun供应了一种基于Webpack的组件库共享方案,可以让主运用和子运用之间共享组件库。
支配方案:qiankun供应了一种基于微做事架构的支配方案,可以将主运用和子运用分别支配到不同的做事器上,从而实现高可用性和负载均衡。
同时,qiankun还供应了一种基于Docker的容器化支配方案,可以更方便地管理和支配运用程序。

9.2 Module Federation主运用:主运用是全体项目的入口,卖力加载和管理所有子运用。
主运用利用Webpack 5供应的module federation功能来注册和启动子运用,同时也卖力处理子运用之间的通信和状态共享。
子运用:子运用是独立的运用程序,可以利用不同的技能栈和框架来构建。
子运用须要利用Webpack 5供应的module federation功能来注册和暴露自己的模块,以便主运用可以加载和利用它们。
路由管理:利用Webpack 5供应的module federation功能可以让主运用和子运用之间共享路由信息。
主运用可以利用Webpack 5供应的API来注册和管理路由,同时也可以将路由信息通报给子运用。
状态管理:利用Webpack 5供应的module federation功能可以让主运用和子运用之间共享状态信息。
主运用可以利用Webpack 5供应的API来注册和管理状态,同时也可以将状态信息通报给子运用。
共享组件库:为了提高开拓效率和代码复用性,可以将一些通用的组件封装成独立的库,并在主运用和子运用中共享利用。
利用Webpack 5供应的module federation功能可以让主运用和子运用之间共享组件库。
支配方案:利用Webpack 5供应的module federation功能可以将主运用和子运用分别支配到不同的做事器上,从而实现高可用性和负载均衡。
同时,利用Docker等容器化技能可以更方便地管理和支配运用程序。

10. 源码讲解

讲了这么多,我们可以进行到源码层面了,对付我们前端开拓来说,我们要做的不仅仅是会用,还须要知道它是如何实现的。

由于函数式一环套一环的,我只能用chatGPT给大家讲解些一些功能模块的大方向实现,而不是一个功能的代码都列举出来,详细阐明。

这块给人的收益不算多,可自行选择阅读。

10.1 qiankun

源码地址:github.com/umijs/qiank…

以下是qiankun中一些精良功能的源码及讲解:

10.1.1 微前端运用的注册和卸载

在qiankun中,我们可以通过registerMicroApps方法来注册微前端运用,通过unregisterMicroApps方法来卸载微前端运用。
这两个方法的源码如下:

注册微前端运用function registerMicroApps( apps: Array<RegistrableApp<{}>>, lifeCycles?: LifeCycles<any>,) { apps.forEach((app) => { const { name, entry, container, activeRule, props, ...rest } = app; if (!name || !entry || !container) { throw new Error('[qiankun] name、entry、container are required for each micro application to register.'); } if (microApps.some((app) => app.name === name)) { return console.warn(`[qiankun] Micro app ${name} already registered.`); } microApps.push({ name, entry, container, activeRule, props, ...rest, }); lifeCycles && (lifecycles[name] = lifeCycles); }); reroute();}// 卸载微前端运用function unregisterMicroApps() { setUnmountByApp(); microApps = []; currentApp = null; nextTick(() => { render({ appContent: '', loading: false }); });}复制代码

我们可以看到,registerMicroApps方法会将传入的微前端运用信息存储到microApps数组中,并将生命周期函数存储到lifecycles工具中。
而unregisterMicroApps方法则会清空microApps数组和currentApp变量,并调用render方法将运用内容清空。

10.1.2 微前端运用的路由匹配

在qiankun中,我们可以通过activeRule属性来指定微前端运用的路由匹配规则。
当前路由与activeRule匹配时,qiankun会将该微前端运用渲染到页面中。
activeRule的源码如下:

function matchActiveRule(location: Location, activeRule: string | ((location: Location) => boolean)) { if (typeof activeRule === 'string') { return location.pathname.startsWith(activeRule); } return activeRule(location);}复制代码

我们可以看到,matchActiveRule方法会根据传入的location和activeRule参数来判断当前路由是否匹配该微前端运用。
如果activeRule是字符串类型,则判断location.pathname是否以该字符串开头;如果activeRule是函数类型,则调用该函数并传入location参数。

10.1.3 微前端运用的沙箱隔离

在qiankun中,我们可以通过single-spa的single-spa-sandbox库来实现微前端运用的沙箱隔离。
qiankun对single-spa-sandbox库进行了封装,供应了createSandbox方法来创建沙箱实例。
createSandbox的源码如下:

function createSandbox(appName: string) { const { strictStyleIsolation, experimentalStyleIsolation, ...sandboxProps } = getMicroAppConfig(appName); if (strictStyleIsolation || experimentalStyleIsolation) { return new SnapshotSandbox(sandboxProps); } return new ProxySandbox(sandboxProps);}复制代码

我们可以看到,createSandbox方法会根据微前端运用的配置信息来判断是否须要开启严格的样式隔离。
如果须要,则创建SnapshotSandbox实例;否则创建ProxySandbox实例。

10.1.4 微前端运用的生命周期管理

在qiankun中,我们可以通过addGlobalUncaughtErrorHandler方法来添加全局缺点处理函数,通过addErrorHandler方法来添加微前端运用的缺点处理函数,通过addAppStatusChangeHandler方法来添加微前端运用状态变革的处理函数。
这些方法的源码如下:

// 添加全局缺点处理函数function addGlobalUncaughtErrorHandler(handler: (event: Event) => void) { globalUncaughtErrorHandler = handler;}// 添加微前端运用的缺点处理函数function addErrorHandler(handler: (event: Event) => void) { errorHandler = handler;}// 添加微前端运用状态变革的处理函数function addAppStatusChangeHandler(handler: (status: string, prev: string) => void) { appStatusChangeHandler = handler;}复制代码

我们可以看到,这些方法都是将传入的处理函数存储到相应的变量中,以便在须要时调用。
个中,globalUncaughtErrorHandler和errorHandler用于处理全局缺点和微前端运用的缺点,而appStatusChangeHandler用于处理微前端运用状态的变革。

10.2 Module Federation

源码地址:github.com/webpack/web…

以下是module federation中一些精良功能的源码及讲解:

10.2.1 远程模块的加载

在module federation中,我们可以通过利用remoteEntry来加载远程模块。
remoteEntry是一个Jsonp文件,包含了远程模块的信息和代码。
我们可以利用import()函数来动态加载远程模块,如下所示:

import('http://localhost:3001/remoteEntry.js').then((remote) => { const module = remote.get('./module'); // 利用远程模块});复制代码

在上面的代码中,我们利用import()函数加载了http://localhost:3001/remoteEntry.js文件,并通过remote.get()方法获取了远程模块中的./module模块。

10.2.2 共享模块的利用

在module federation中,我们可以通过利用exposes和remotes来共享模块。
exposes用于将本地模块暴露给其他运用程序利用,而remotes用于引用其他运用程序暴露的模块。

下面是一个利用exposes和remotes的示例:

// app1.jsconst path = require('path');const { ModuleFederationPlugin } = require('webpack').container;module.exports = { // ... plugins: [ new ModuleFederationPlugin({ name: 'app1', filename: 'remoteEntry.js', exposes: { './module': './src/module', }, }), ],};// app2.jsconst path = require('path');const { ModuleFederationPlugin } = require('webpack').container;module.exports = { // ... plugins: [ new ModuleFederationPlugin({ name: 'app2', filename: 'remoteEntry.js', remotes: { app1: 'app1@http://localhost:3001/remoteEntry.js', }, }), ],};复制代码

在上面的代码中,我们在app1中利用exposes将./src/module模块暴露出去,然后在app2中利用remotes引用了app1运用程序,并通过app1@http://localhost:3001/remoteEntry.js指定了app1运用程序的remoteEntry文件的URL。

10.2.3 共享模块的版本掌握

在module federation中,我们可以通过利用shared选项来掌握共享模块的版本。
shared选项是一个工具,个中的键是模块名称,值是模块的版本号或版本范围。

下面是一个利用shared选项的示例:

// app1.jsconst path = require('path');const { ModuleFederationPlugin } = require('webpack').container;module.exports = { // ... plugins: [ new ModuleFederationPlugin({ name: 'app1', filename: 'remoteEntry.js', exposes: { './module': './src/module', }, shared: { react: { singleton: true, requiredVersion: '^17.0.0', }, 'react-dom': { singleton: true, requiredVersion: '^17.0.0', }, }, }), ],};// app2.jsconst path = require('path');const { ModuleFederationPlugin } = require('webpack').container;module.exports = { // ... plugins: [ new ModuleFederationPlugin({ name: 'app2', filename: 'remoteEntry.js', remotes: { app1: 'app1@http://localhost:3001/remoteEntry.js', }, shared: { react: { singleton: true, requiredVersion: '^17.0.0', }, 'react-dom': { singleton: true, requiredVersion: '^17.0.0', }, }, }), ],};复制代码

在上面的代码中,我们在app1和app2中都利用了shared选项来掌握共享模块的版本。
在这个例子中,我们指定了react和react-dom模块的版本范围为^17.0.0,表示只假如17.x.x版本都可以共享利用。
同时,我们还将singleton选项设置为true,表示这些模块只会被加载一次。

11. Vue和React组件互用

这算是一个技能延伸,大家思考下,我们用不用技能栈的子项目来构建微前端时,组件如何通用呢,也便是标题所写的Vue和React组件如何互用。

这个听起来便是一个很故意思的技能点,让我们来好好研究一下。

我目前总结了三种方案,来实现组件互用。

分别是web componments,node中间件,利用第三方库(vue-reactivity,react-vue等)

把稳:

虽然可以实现Vue和React组件的互用,但是在实际开拓中,我们还是只管即便避免这种情形,由于不同的框架有不同的设计理念和实现办法,组件互用可能导致代码繁芜度增加,掩护本钱增加等问题。

11.1 web componments

将web componments封装成Vue和React都可以利用的组件,须要进行以下步骤:

11.1.1 创建web componments

利用原生的web conponments技能或者第三方库(例如 Polymer)创建组件,并且将组件封装成自定义元素。

例如,我们创建一个大略的web conponments,它可以显示一个Hello Feng的文本。

<!-- hello-feng.html --><template> <h1>Hello Feng</h1></template><script> class HelloFeng entends HTMLElement{ super(); const template = document.getElementById('hello-feng-template'); const content = template.content.cloneNode(true); this.attachShadow({mode:'open'}).appendChild(content) customElement.defien('hello-feng',HelloFeng)}复制代码

在类的布局函数中,我们利用attachShadow方法创建了一个Shadow DOM,然后我们将模板内容添加到了Shadow DOM中。

末了我们利用customElements.define方法将组件注册为自定义元素。

super:1. super继续:在constructor中调用super,子类没有定义constructor方法,super方法会被默认添加。

子类的布局函数中,只有调用super之后,才可以利用this关键字,否则会报错 。
(由于ES5 的继续的本色是先创造子类的实例工具this,然后再将父类的方法添加到this上面(Parent.apply(this))。
ES6 的继续机制完备不同,本色是先将父类实例工具的属性和方法,加到this上面(以是必须先调用super方法),然后再用子类的布局函数修正this)

2.super方法:作为函数时,super()只能用在子类的布局函数之中。

3.super作为工具时,在普通方法中,指向父类的原型工具,在静态方法中,指向父类。

11.1.2 将web componments封装为Vue组件

利用Vue的自定义元素插件(例如vue-custom-element)将web componments封装为Vue组件。

例如,我们可以将上面的hello-feng封装为Vue组件。

<!-- hello-feng-vue.js -->import Vue from 'vue';import vueCustomElement from 'vue-custom-element'import HelloFeng from './hello-feng.html'Vue.use(vueCustomElement)Vue.cusetomElement('hello-feng-vue',HelloFeng)复制代码

在上面的代码中,我们利用了vue-custom-element插件将web componments封装为了Vue组件。

利用Vue.customElement方法将组件注册为自定义元素。

自定义元素:

自定义元素是web componments中的一个主要的特性,自定义元素许可开拓者自定义HTML元素,然后在页面中利用这些元素。

例如。

自定义元素和Vue中的组件十分类似,Vue组件也是一种可复用的组件,可以在页面利用自定义标签来调用。

11.1.3 将web componments封装为React组件

实在和Vue的流程差不多,利用的是React的自定义元素插件(例如 react-custom-elements)将web componments封装为React组件。

例如,我们可以将上面的web componments封装为React组件。

<!-- hello-feng-reatc.js -->import React from 'react'import ReactDOM from 'react-dom'import {defineCustomElement} from 'react-custom-elemets'import HelloFeng from './hello-feng.html'class HelloFengReact extends React.Componment{ render(){ return ReactDOM.createProtal( <HelloFeng/>, this.shadowRoot ) }}defineCustomElement('hello-feng-react',HelloFengReact)复制代码

在上面的代码中,我们利用了react-custom-elements插件将web componments封装为了React组件。

利用defineCustomElement方法将组件注册为自定义元素。

在组件的render方法中,我们利用ReactDOM.createProtal方法将组件渲染到Shadow DOM中。

11.2 node中间件

node中间件方法的思想是:把vue和react的组件都渲染成HTML字符串,然后在组件中利用。

详细中间件有:vue-server-renderer,react-dom/server,express-vue,react-express-views。

11.3 第三方库vue-reactivity: Vue官网供应的库,可以让Vue组件利用React组件。
react-vue: 第三方的库,可以在React组件中利用Vue组件。
vue-custom-element: 这也是Vue官方供应的库,可以将Vue组件转换成自定义元素,然后在React中利用。
react-custom-element: 第三方的库,可以讲React组件转换为自定义元素,然后在Vue中利用。
vue-in-react: 第三方的库,可以让Vue组件在React中利用。
react-in-vue: 第三方的库,可以让React组件在Vue中利用。
12. 经典案例

github.com/module-fede…

这个是Module Federation的案例,里面有很多demo,例如Vue项目的,React项目的,Angular项目的,还有webpack,vue-cli以及vite的,连Vue+React的demo也供应了。

github.com/umijs/qiank…

这个是qiankun的案例,里面三大主流框架包括Vue3的demo都供应了。

github.com/single-spa/…

这个是single-spa的案例,里面不仅有三大主流框架,svelte这种编译时框架也有demo,还有preact等非主流框架案例。

阅读 27647

微前端先容

什么是微前端?微前端是一种多个团队通过独立发布功能的办法,来共同构建当代化 web 运用的技能手段及方法策略。

不同于纯挚的前端框架/工具,微前端是一套架构体系,这个观点最早在2016年底由 ThoughtWorks 提出。
微前端是一种类似于后端微做事的架构,它将微做事的理念运用于浏览器端,将 Web 运用从全体的「单体运用」转变为多个小型前端运用的「聚合体」。

各个前端运用「原子化」,可以独立运行、开拓、支配,从而知足业务的快速变革,以及分布式、多团队并行开拓的需求。

微前真个特点

技能栈无关 主框架不限定接入运用的技能栈,子运用可自主选择技能栈 独立开拓/支配 各个团队之间仓库独立,单独支配,互不依赖 增量升级 当一个运用弘大之后,技能升级或重构相称麻烦,而微运器具备渐进式升级的特性 独立运行时 微运用之间运行时互不依赖,有独立的状态管理 提升效率 运用越弘大,越难以掩护,协作效率越低下。
微运用可以很好拆分,提升效率

办理问题的理念有了,那要通过若何的技能去实现呢?

iframe 最早也是最熟习的办理方案便是通过iframe,由于它可以独立运行另一个项目,这种方案的上风:非常大略,无需任何改造完美隔离,JS、CSS 都是独立的运行环境不限定利用,页面上可以放多个 iframe 来组合业务

当然也是逃不过事务的两面性,有优点就有缺点:

无法保持路由状态,刷新后路由状态就丢失(这点也不是完备不能办理,可以讲路由作为参数拼接在链接后,刷新时去参数进行页面跳转)完备的隔离导致与子运用的交互变得极其困难iframe 中的弹窗无法打破其本身全体运用全量资源加载,加载太慢

既然有这么明显的问题,那就会有新的方案被创造出来

基于 single-spa 路由挟制方案

single-spa 通过挟制路由的办法来做子运用之间的切换,但接入办法须要领悟自身的路由,有一定的局限性。

qiankun 孵化自蚂蚁金融科技基于微前端架构的云产品统一接入平台。
它对 single-spa 做了一层封装。
紧张办理了 single-spa 的一些痛点和不敷。
通过 import-html-entry 包解析 HTML 获取资源路径,然后对资源进行解析、加载。

通过对实行环境的修正,它实现了 JS 沙箱、样式隔离 等特性。
接下来我就好好的讲讲qiankun是怎么落地莅临盆项目的,中间碰着过哪些坑。

qiankun

qiankun官网 qiankun.umijs.org/zh/guide

按照官方文档快速搞起来主运用

安装

$ yarn add qiankun # 或者 npm i qiankun -S复制代码在主运用注册微运用 main.js

放在main.js的尾部

import { registerMicroApps, start } from 'qiankun';registerMicroApps([ { name: 'vue app', entry: '//localhost:7100', container: '#container-vue', activeRule: '/micro-vue', // 通报给子运用的参数 props: { routerBase: '/micro-vue', } }, { name: 'micro-clouds', entry: '//localhost:7000', activeRule: '/micro-clouds', container: '#subapp2', // 子运用挂载的div // 通报给子运用的参数 props: { routerBase: '/micro-clouds', } }]);// 启动微运用start();复制代码

当微运用信息注册完之后,一旦浏览器的 url 发生变革,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微运用就会被插入到指定的 container 中,同时依次调用微运用暴露出的生命周期钩子。

如果微运用不是直接跟路由关联的时候,你也可以选择手动加载微运用的办法:

import { loadMicroApp } from 'qiankun';loadMicroApp({ name: 'micro-clouds', entry: '//localhost:7000', activeRule: '/micro-clouds', container: '#subapp2', // 子运用挂载的div // 通报给子运用的参数 props: { routerBase: '/micro-clouds', }});复制代码

微运用main.js

import './public-path';import { createApp } from 'vue'import App from '@/App.vue'import { createRouter, createWebHistory } from 'vue-router';import routes from '@/router'import store from '@/store'let instance = nullasync function render(props = {}) { const { container, routerBase } = props; // 实例化vue instance = createApp(App); // 路由挂载 const router = createRouter({ history: createWebHistory(`${routerBase}`), routes }); instance.use(router); instance.use(store); instance.mount(container ? container.querySelector("#app") : "#app");}// eslint-disable-next-line no-underscore-dangleif (!window.__POWERED_BY_QIANKUN__) { render({ container: "", routerBase: "/micro-clouds" });}export async function bootstrap() { console.log('[vue] vue app bootstraped');}export async function mount(props) { await render(props);}export async function unmount() { instance.unmount();}复制代码

public-path.js

(function () { if (window.__POWERED_BY_QIANKUN__) { // eslint-disable-next-line __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ // __webpack_public_path__ = `${process.env.BASE_URL}/` }})()复制代码

到这里我们就把主运用和微运用领悟起来了,涉及的代码量也就百八十行,至于跳转可以自己在主运用加几个按钮通过history.pushState(null, item.activeRule, item.activeRule)

这个是我当时模拟的demo: github.com/fengxianqi/…

实在跑通以上demo还是挺大略的,我一开始也是看着qiankun官网,在网上找了个demo比划比划,很大略嘛!
如果我这边文章只分享到这里那完备没有必要去分享了,今后看才是实际生产项目的搭法。

qiankun + vue3 搭建生产项目

通过上面的demo例子抛转引玉,该当对微前端是怎么跑起来的有了大致的认识。
我当时也以为搞定了上面的demo就可以直接用在生产项目了。
没料到实际用起来和demo完备是两回事,下面请听我逐一解析这当中碰着的坑。

1. 路由

由于demo项目没有用vue-router,直接是通过history.pushState跳转,统统看来都是那么的没有,没有告警没有报红。
但在实际的vue项目中,路由的改变,vue-router是会自动去匹配路由的,当匹配到子路由在当前router中没有配置时就会报警报红,如果直接跳转又没法利用到vue-router中的能力,怎么领悟路由才能达到哀求呢?

答案是通过一个component组件来承载,它可以使qiankun的代码与基座项目极大的降落耦合,代码可掩护性增强,最主要的是可以统一将微运用路由归并到这个组件路由来,相称于接入微前端就像增加了一个普通组件这么大略。

主运用配置

src/router/index.js

{ path: '/:micro(micro-vue|micro-clouds):endPath(.)', name: 'MicroApp', meta: { title: '微前端运用' }, component: () => import(/ webpackChunkName: "qiankun" /'@/views/qiankun/MicroApp.vue') },复制代码

通过正则匹配路由,只假如微运用的路由都可以匹配进来,当然须要事先定义好微运用的路由前缀,比如micro-xxx,增加微运用就在这里(micro-vue|micro-clouds)加一个匹配前缀。

新建MicroApp.vue组件src/views/qiankun/MicroApp.vue

<template> <div class="layout-container micro-container" v-loading="$store.state.app.isLoadingMicro" element-loading-text="Loading..."> <div id="subapp1"></div> <div id="subapp2"></div> </div></template><script>import { onMounted, watch, reactive, onUnmounted} from 'vue'import { loadMicroApp, addGlobalUncaughtErrorHandler } from 'qiankun'import { useRoute } from 'vue-router'import { microApps, registerApps } from '@/views/qiankun/micro-app'export default { name: 'MicroApp', setup() { const microList = reactive({}) const route = useRoute() const state = reactive({ elementLink: null }); const activationHandleChange = async (path) => { const activeRules = microApps.map((app) => app.activeRule) const isMicro = activeRules.some((rule) => path.startsWith(rule)) if (!isMicro) return const microItem = microApps.find((app) => path.startsWith(app.activeRule.toString())) if (!microItem) return // 如果已经加载过一次,则无需再次加载 const current = microList[microItem.activeRule.toString()] if (current) return // 缓存当前子运用 const micro = loadMicroApp({ ...microItem }) microList[microItem.activeRule.toString()] = micro try { await micro.mountPromise } catch (e) { console.error('=======', e) } } // qiankun全局非常捕获 addGlobalUncaughtErrorHandler((event) => console.log(event)) // 监测路由变革 watch(() => route.path, async (newValue) => { activationHandleChange(newValue) }) onMounted(async () => { if (window.qiankunStarted) return window.qiankunStarted = true registerApps() activationHandleChange(route.path) }) onUnmounted(() => { window.qiankunStarted = false // 离开微运用路由时会卸载开启的微运用 Object.values(microList).forEach((mic) => { mic.unmount() }) }) return { } }}</script><style lang="scss" scoped>.micro-container{ background: var(--system-container-main-background);}</style>复制代码

src/views/qiankun/micro-app.js

import { registerMicroApps, start } from 'qiankun'import store from '@/store'import utils from '@/assets/js/utils';export const microApps = [ { name: 'micro-clouds', entry: process.env.VUE_APP_CLOUDS, activeRule: '/micro-clouds', container: '#subapp2', // 子运用挂载的div props: { routerBase: '/micro-clouds', mainStore: store, user: utils.getStorage('user') } }]export const registerApps = () => { registerMicroApps(microApps, { beforeLoad: (app) => { store.commit('app/loadingMicro', true) console.log('before load app.name====>>>>>', app.name) }, beforeMount: [ (app) => { console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name) } ], afterMount: [ (app) => { store.commit('app/loadingMicro', false) console.log('[LifeCycle] after mount %c%s', 'color: green;', app.name) } ], afterUnmount: [ (app) => { console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name) } ] }) start({ sandbox: { // 默认开启预加载 prefetch: 'all', // qiankun供应的样式隔离方法(严格模式) strictStyleIsolation: true, experimentalStyleIsolation: true } })}复制代码

微运用配置mian.js

import './public-path';import { createApp } from 'vue'import ElementPlus from 'element-plus'import NProgress from '@/assets/js/nprogress';import as ELIcons from '@element-plus/icons-vue'import zhCn from 'element-plus/es/locale/lang/zh-cn'import draggable from 'vuedraggable'import 'element-plus/dist/index.css'import 'normalize.css' // css初始化import '@/assets/style/common.scss' // 公共cssimport '@/assets/style/iconfont.css'import App from '@/App.vue'import { createRouter, createWebHistory } from 'vue-router';import routes from '@/router'import store from '@/store'let instance = nullasync function render(props = {}) { const { container, routerBase } = props; instance = createApp(App); const router = createRouter({ history: createWebHistory(`${routerBase}`), routes }); instance.use(router); instance.use(ElementPlus, { locale: zhCn }); instance.use(store); instance.component('draggable', draggable) / eslint-disable / for (const [key, component] of Object.entries(ELIcons)) { instance.component(key, component); } instance.mount(container ? container.querySelector("#app") : "#app"); // eslint-disable-next-line no-underscore-dangle if (window.__POWERED_BY_QIANKUN__) { router.afterEach((to) => { const matched = to.matched.map((item) => { return { ...item, path: `${routerBase}${item.path}`, redirect: `${routerBase}${item.path}`, }; }) props.mainStore.dispatch("app/getMicroBreadcrumb", [ ...matched ]); }); } router.beforeEach(async (to, from, next) => { NProgress.start(); if (store.getters["user/isLogin"]) { next(); } else if (store.state.app.whiteList.includes(to.path)) { // 白名单页面 next(); } else { next("/login"); // 全部重定向到登录页 } }); router.afterEach((to) => { const keepAliveComponentsName = store.getters["keepAlive/keepAliveComponentsName"] || []; const { name } = to.matched[to.matched.length - 1].components.default; if (to.meta && to.meta.cache && name && !keepAliveComponentsName.includes(name)) { store.commit("keepAlive/addKeepAliveComponentsName", name); } NProgress.done(); });}// eslint-disable-next-line no-underscore-dangleif (!window.__POWERED_BY_QIANKUN__) { render({ container: "", routerBase: "/micro-clouds" });}export async function bootstrap() { console.log('[vue] vue app bootstraped');}export async function mount(props) { // 标记当前启动形式为微做事启动 store.commit("app/microChange", true); await render(props);}export async function unmount() { instance.unmount();}复制代码

public-path.js

(function () { if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; }}());复制代码

vue.config.js

const path = require('path')module.exports = { publicPath: process.env.NODE_ENV === 'development' ? '/' : '/micro-clouds', configureWebpack: { output: { library: 'micro-clouds-[name]', libraryTarget: 'umd', jsonpFunction: 'webpackJsonp_[name]' }, }, devServer: { port: 4003, disableHostCheck: true, headers: { 'Access-Control-Allow-Origin': '' // 主运用获取子运用时跨域相应头 }, proxy: { '/api': { target: process.env.VUE_APP_TEST, changeOrigin: true, pathRewrite: { '^/api': '' } } } }}复制代码

到这为止,一个完全的微前端架构就搭建好了,下面来先容各个细节的交互是怎么处理的。

2. 父子运用耦合以及单独利用

大略讲便是为了知足更多更灵巧的利用哀求,咱们设计要能知足主运用与微运用耦合在一起利用,也可以子运用单独利用,不仅知足了用户的需求,还可以方便开拓职员调试。
(联调子运用时子运用一定是能独立启动的,并且有自己的登录、菜单功能,说白了便是一个完全的系统)

为了区分微前端运行还是单独运行,我们须要在子运用中插入微前端标识。

# 单独启动子运用时会通过这里来启动项目if (!window.__POWERED_BY_QIANKUN__) { render({ container: "", routerBase: "/micro-clouds" });}复制代码

# 这是子运用中微前真个钩子函数,如果是通过微前端启动,就通过这里来启动项目export async function mount(props) { // 标记当前启动形式为微做事启动 store.commit("app/microChange", true); // props是主运用传过来的参数 await render(props);}复制代码

区分好了之后,就须要将子运用中的菜单、头部状态栏隐蔽,只留下内容区,连内容区周围的间隙都要去掉,由于主运用内容区也有间隙,避免样式冲突。

3. 父子运用通信

原则上该当尽可能得降落父子运用之间的耦合,以免子运用单独利用时无法正常运行,也会增加掩护本钱。
但是像登录信息的通报是有必要的,父运用登录了将登录信息传到子运用,避免子运用检测到无登录状态跳转到登录页面。

# 主运用export const microApps = [ { name: 'micro-clouds', entry: process.env.VUE_APP_CLOUDS, activeRule: '/micro-clouds', container: '#subapp2', // 子运用挂载的div props: { routerBase: '/micro-clouds', // 父运用的store通报给子运用 mainStore: store, // 父运用通报给子运用的登录信息 user: utils.getStorage('user') } }]复制代码

# 子运用接管父运用传过来的登录信息并写入storage中export async function mount(props) { // 父级传过来的登录信息写进子系统缓存中 utils.setStorage("user", props.user); // 标记当前启动形式为微做事启动 store.commit("app/microChange", true); await render(props);}复制代码4. 面包屑怎么领悟父子运用中的路由

现在的后台运用基本都有面包屑要么便是有一个tabs标签栏,在非微前端运用中,是一个非常大略的问题,直接to.matched就能查找出当前的路由嵌套关系,面包屑遍历出来就OK。
但是在微前端中怎么知道子运用中的路由嵌套关系,那么就涉及父子运用的通信,当子运用监听到路由变革时上报子运用的to.match给父运用。
由于这里涉及到主子运用的to.match切换,当打开的是主运用的页面就用主运用的to.match,打开的是子运用的页面就用子运用的to.match,以是我这里统一用vuex来处理

# 主运用vuexconst actions = { // 处理父运用的to.match getBreadcrumb({ commit }, matched) { const levelList = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false); commit('setBreadcrumb', JSON.parse(JSON.stringify(levelList))); }, // 处理微运用的to.match getMicroBreadcrumb({ commit }, matchedList) { const microLevel = matchedList.filter((ele) => ele.meta && ele.meta.title && ele.meta.breadcrumb !== false); / 为理解决这个告警采取 JSON.parse(JSON.stringify(microLevel)) vue3.0须要把稳的地方 runtime-core.esm-bundler.js:198 [Vue warn]: Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead. / commit('setMicroBreadcrumb', JSON.parse(JSON.stringify(microLevel))); }}复制代码

# 主运用面包屑组件中const getBreadcrumb = async () => { // 判断是微运用就去微运用上报的to.match if (route.path.startsWith('/micro')) { // 这里须要做一个vuex异步处理,否则取不到子运用上报的to.match store.subscribe((mutation, state) => { if (mutation.type === 'app/setMicroBreadcrumb') { levelList.value = store.state.app.microBreadcrumbs } }) } else { await store.dispatch('app/getBreadcrumb', route.matched) levelList.value = store.state.app.breadcrumbs }}复制代码

# 子运用上报to.match给父运用# 子运用main.js# 好好回忆一下,在父运用中通报给子运用的props中就有一个`mainStore`,这个便是父运用的store,可以直接在子运用中利用`props.mainStore.dispatch("app/getMicroBreadcrumb", [ ...matched ]);`function render(props = {}) { ... if (window.__POWERED_BY_QIANKUN__) { router.afterEach((to) => { // 你可能创造这里为啥要改造`path、redirect`,路径前面拼接了一个`routerBase` // 是由于在父运用中,如果子运用路由不带子运用的前缀标识路由是没法跳转进子运用页面的!!! const matched = to.matched.map((item) => { return { ...item, path: `${routerBase}${item.path}`, redirect: `${routerBase}${item.path}`, }; }) props.mainStore.dispatch("app/getMicroBreadcrumb", [ ...matched ]); }); } ...}复制代码

到这里基本上配置完成了,后面是一些碰着的样式bug,一定要实践,光看可能效果不大

5. 父子运用中样式冲突的问题

当在父运用打开子运用时涌现样式问题,正常在vue + element-plus的项目中,是这么引组件的样式

通过审查元素创造这个弹窗的dom节点的class样式少了一些属性,也便是光有class少了样式值,以是导致样式问题。
目测是import引入样式的没法浸染到子运用节点,改成在public/index.html通过link的办法引用

成功办理样式问题,须要把稳的是当碰着样式问题时可以从 引入办法、append-to-body属性(element-plus组件)动手排查。

配置完全个微前端架构,看似百八十行代码,实在碰着的问题还是蛮多的,跟撸个demo完备是两回事,网上可查的资料又少,官方文档只是先容了那几个api的利用,干完这票确实提升不少,值得去考试测验,我认为这是构架师必备技能。

6. qiankun加载子运用的协议会转成http的问题

前面由于都是在本地以及测试环境验证过,由于都是http协议以是没有暴露出这个问题。
当到沙盒以及生产环境时,子运用页面加载不出来,我也没有改动代码,测试和生产是同一份代码,一贯报这个缺点

也便是主运用是 https 协议,子运用是http协议,导致了这个问题。
现在问题清楚了,又有其余一个疑问,我的主运用指向子运用的路由是 https 啊,为什么被改成了 http,这个问题我不太清楚是不是qiankun这家伙干的,官方也没说。

办理办法:

public/index.html 头部加上这个

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">复制代码

如果是统一加上这个,测试环境和本地环境又弗成了,得区分开发,怎么办?

我们都知道,打包后天生的index.html是由 html-webpack-plugin 插件天生的,沿着这条线索找,插件文档果真有meta这个配置项github.com/jantimon/ht…

那么在vue-cli天生的项目中没有单独利用html-webpack-plugin,而是在 vue.config.js里的pages入口这里配置,详细代码:

pages: { index: { entry: 'src/main.js', template: 'public/index.html', filename: 'index.html', title: '采贝中台', meta: process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test' ? {} : { 'Content-Security-Policy': { 'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests' } } }},复制代码

完美办理!

项目支配

支配办法很多种,我这里只先容我在项目中的实践。

主运用:main.xxx.com/home 子运用A: clouds.xxx.com/projectA/ho… 子运用B: clouds.xxx.com/projectB/ho… 子运用C: clouds.xxx.com/projectC/ho…

现在的支配都盛行CICD,docker + k8s,以是我采取的是每个项目都单独支配一个docker,末了通过 域名 + 项目前缀进入项目,nginx配置如下:

# 主运用location / { root /usr/share/nginx/html; index index.html; # 入口文件 try_files $uri $uri/ /index.html;}复制代码

# 子运用location /micro-projectA { add_header Access-Control-Allow-Origin ; add_header Access-Control-Allow-Methods 'GET'; add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; alias /usr/share/nginx/html; try_files $uri $uri/ /micro-projectA/index.html;}location / { add_header Access-Control-Allow-Origin ; add_header Access-Control-Allow-Methods 'GET'; add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; root /usr/share/nginx/html; try_files $uri $uri/ /index.html;}复制代码

标签:

相关文章

博学鲸鱼秘闻海洋中的智慧之星

海洋,这个神秘而又广袤的世界,孕育着无数生命。在众多海洋生物中,有一种被誉为“智慧之星”的动物——鲸鱼。它们不仅是海洋中的霸主,更...

搜狗SEO 2025-01-14 阅读0 评论0

卡点sayit,引爆你的魅力瞬间!

在人际交往中,沟通是连接彼此心灵的桥梁。而如何运用魅力沟通,让自己在瞬间脱颖而出,成为众人瞩目的焦点,成为我们每个人都应该掌握的技...

搜狗SEO 2025-01-14 阅读0 评论0

厂里IT岗位,职场新贵!快来围观!

在信息化、智能化的时代背景下,IT行业成为我国经济发展的重要支柱。作为职场新贵,厂里IT岗位不仅薪资待遇优厚,更是众多年轻人向往的...

搜狗SEO 2025-01-14 阅读0 评论0