微前端介绍

微前端

微前端介绍

微前端定义

微前端(Micro-Frontends)是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。

简单的说:微前端就是在一个Web应用中独立运行其他的Web应用

微前端特点

  1. 技术栈无关:主框架不限制接入应用的技术栈,微应用具备完全自主权;
  2. 独立开发、独立部署:微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新;
  3. 增量升级:在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略;
  4. 独立运行时:每个微应用之间状态隔离,运行时状态不共享;
  5. 环境隔离:应用之间 JavaScript、CSS 隔离避免互相影响;
  6. 消息通信:统一的通信方式,降低使用通信的成本;
  7. 依赖复用:解决依赖、公共逻辑需要重复维护的问题;

微前端常见框架

路由分发式微前端

路由分发式微前端,即通过路由将不同的业务分发到不同的独立前端应用上。最常用的方案是通过 HTTP 服务的反向代理来实现。

下面是一个基于路由分发的 Nginx 配置:

javascript 复制代码
 http {
        server {
            listen 80;
            server_name  xxx.xxx.com;
            location /api/ {
                proxy_pass http://localhost:3001/api;
            }
            location /web/admin 

 - List item

{
                proxy_pass http://localhost:3002/api;
            }
            location / {
                proxy_pass /;
            }
        }
    }

优点:

  • 实现简单;
  • 不需要对现有应用进行改造;
  • 完全技术栈无关;

缺点:

  • 用户体验不好,每次切换应用时,浏览器都需要重新加载页面;
  • 多个子应用无法并存;
  • 局限性比较大;
  • 子应用之间的通信比较困难;
  • 子应用切换时需要重新登录;

iframe

iframe 作为一项非常古老的技术,也可以用于实现微前端 。通过 iframe,我们可以很方便的将一个应用嵌入到另一个应用中,而且两个应用之间的 css 和 javascript 是相互隔离的,不会互相干扰。

优点:

实现简单;

  • css 和 js 天然隔离,互不干扰;
  • 完全技术栈无关;
  • 多个子应用可以并存;
  • 不需要对现有应用进行改造;

缺点:

  • 用户体验不好,每次切换应用时,浏览器需要重新加载页面;
  • UI 不同步,DOM 结构不共享;
  • 全局上下文完全隔离,内存变量不共享,子应用之间通信、数据同步过程比较复杂;
  • 对 SEO 不友好;
  • 子应用切换时可能需要重新登录,体验不好;

single-spa

single-spa 提供了新的技术方案,可以帮忙我们实现类似单页应用的体验。

single-spa 方案中,应用被分为两类:基座应用子应用 。其中,子应用 就是文章上面描述的需要聚合的子应用;而基座应用 ,是另外的一个单独的应用,用于聚合子应用

和单页应用的实现原理类似,single-spa 会在基座应用 中维护一个路由注册表每个路由对应一个子应用。基座应用启动以后,当我们切换路由时,如果是一个新的子应用,会动态获取子应用的 js 脚本,然后执行脚本并渲染出相应的页面;如果是一个已经访问过的子应用,那么就会从缓存中获取已经缓存的子应用,激活子应用并渲染出对应的页面。

优点:

  • 切换应用时,浏览器不用重载页面,提供和单页应用一样的用户体验;
  • 完全技术栈无关;
  • 多个子应用可并存;
  • 生态丰富;

缺点:

  • 需要对原有应用进行改造,应用要兼容接入 sing-spa 和独立使用;
  • 有额外的学习成本;
  • 使用复杂,关于子应用加载、应用隔离、子应用通信等问题,需要框架使用者自己实现;
  • 子应用间相同资源重复加载;
  • 启动应用时,要先启动基座应用;

qiankun

single-spa 一样,qiankun 也能给我们提供类似单页应用 的用户体验。qiankun 是在 single-spa 的基础上做了二次开发 ,在框架 层面解决了使用 single-spa 时需要开发人员自己编写子应用加载、通信、隔离等逻辑 的问题,是一种比 single-spa 更优秀的微前端方案。

优点:

  • 切换应用时,浏览器不用重载页面,提供和单页应用一样的用户体验;
  • 相比 single-spa,解决了子应用加载、应用隔离、子应用通信等问题,使用起来相对简单;
  • 完全和技术栈无关;
  • 多个子应用可并存;

缺点:

  • 需要对原有应用进行改造,应用要兼容接入 qiankun 和独立使用;
  • 有额外的学习成本;
  • 相同资源重复加载;
  • 启动应用时,要先启动基座应用;

webpack5:module federation

webpack5,提供了一个新的特性 - module federation 。基于这个特性,我们可以在一个 javascript 应用中动态加载并运行另一个 javascript 应用的代码,并实现应用之间的依赖共享。

通过 module federation,我们可以在一个应用里面动态渲染另一个应用的页面,这样也就实现了多个子应用的聚合。

优点:

  • 不需要对原有应用进行改造,只需改造打包脚本;
  • 切换应用时,浏览器不用重载页面,提供和单页应用一样的用户体验;
  • 多个子应用可并存;
  • 相同资源不需要重复加载;
  • 开发技术栈无关;
  • 应用启动后,无需加载与自己无关的资源;
  • 免登友好;

缺点:

  • 构建工具只能使用 webpack5;
  • 有额外的学习成本;
  • 对老项目不友好,需要对 webpack 进行改造;

Web Component

基于 Web ComponentShadow Dom 能力,我们也可以实现微前端,将多个子应用聚合起来。

Shadow Dom 的用法如下:

javascript 复制代码
const shadow = document.querySelector('#hostElement').attachShadow({mode: 'open'});
// url 为应用的地址,基于 fetch,我们可以获取到应用的 html 模板,添加到指定节点下
fetch(url).then(res => {
    shadow.innerHTML = res
});

优点:

  • 实现简单;
  • css 和 js 天然隔离,互不干扰;
  • 完全技术栈无关;
  • 多个子应用可以并存;
  • 不需要对现有应用进行改造;

缺点:

  • 主要是浏览器兼容性问题;
  • 开发成本较高;

微前端问题总结

微前端框架中,js隔离样式隔离元素隔离是必须解决的三个问题,下面我们就来分别说说这三个问题是什么?怎么解决?

js隔离

问题:

情况1:都对全局变量赋值

应用A,写 window.r = 1;然后有应用B,又写 window.r = 2,这就乱套了

情况2:都设置事件

应用A,window.addEventListener('click',()=>console.log('A'));

应用B,window.addEventListener('click',()=>console.log('B'));,这就乱套了

解决:

方法一用 Proxy 代理

es2015 Reflect属于一个静态类或者设置属性等用法

javascript 复制代码
const rawWindow = window
const proxyWindow = new Proxy({},{
    get: (target, key): unknown => {
        // 原 target 上有就返回,否则返回 rawWindow 属性
        return Reflect.has(target, key) ? Reflect.get(target, key) : Reflect.get(rawWindow, key)
    },
    set: (target, key, value): boolean => {
        if(!Object.prototype.hasOwnProperty.call(target, key) && Object.prototype.hasOwnProperty.call(rawWindow, key)){
            const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key)
            const { configurable, enumerable, writable, set } = descriptor!
            // set value because it can be set
            rawDefineProperty(target, key, {
              value,
              configurable,
              enumerable,
              writable: writable ?? !!set,
            })
        } else {
            Reflect.set(target, key, value)
        }
    }
})

window.r = 1window.addEventListener('click',()=>console.log('A')) 包括到自执行函数里面

A和B互不干扰

javascript 复制代码
;(function(window){
    window.r = 1
    window.addEventListener('click',()=>console.log('A'))
})(proxyWindowA)
javascript 复制代码
;(function(window){
    window.r = 2
    window.addEventListener('click',()=>console.log('B'))
})(proxyWindowB)

法二 用快照

快照隔离有个前提条件是,当前还有一个应用显示,不能出现多个应用并存显示在界面上,应用A,B切换时,比如当前应用是A,现在要切入到应用B

  • 暂存起来应用A的全局变量和事件
  • 恢复全局变量和事件到应用A之前
  • 检查之前是否保持有应用B的全局变量和事件,如果有,则载入

样式隔离

问题

同理,各个应用之前可能相互设置标签样式,会相互影响,或者影响全局样式,比如应用A给body设置样式,应用B也给body设置样式

方法一 样式增加不同前缀

每个应用通过前缀独立区分开,京东micro-app默认是采用的这个策略,唯一注意的一个小点是,基座样式会影响子应用的样式,所以需要注意基座中不要写太多样式


方法二 ShadawDom

大多数Html标签都有 attachShadow() 方法给指定的元素挂载一个 Shadow DOM。参数是openclosed

ShadawDom 样式绝对隔离,不用加前缀,如下图

javascript 复制代码
//open 是外界可以访问到Element.shadowRoot再访问到内部元素,closed就是完全不能访问内部元素
var shadowroot = element.attachShadow('open|closed')  


元素隔离

元素隔离是 基座应用和子应用都有一个元素<div id='root'></div>,此时子应用通过document.querySelector('#root'),因为js隔离已经做了代理,此时document.querySelector只是子应用本身了

相关推荐
Apifox7 分钟前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
树上有只程序猿35 分钟前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼1 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下1 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox1 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞2 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行2 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758102 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周2 小时前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队2 小时前
Vue自定义指令最佳实践教程
前端·vue.js