微前端中实现沙箱环境的方案调研

前端,实现,沙箱,环境,方案,调研 · 浏览次数 : 381

小编点评

**前言** 在微前端实践中,全局作用域变量的污染问题尤为严重,因为主子应用可能会通过获取和修改全局对象,导致数据相互污染。为了解决这个问题,在应用之间实现数据隔离的技术非常重要。 **iframe方案** iframe可以提供数据隔离的解决方案,因为它本身是一个独立的窗口,可以完全隔离与主应用隔离。但是,iframe无法作为沙箱执行业务代码,因此它不是一个理想的选择。 **快照方案** 快照方案是一种在应用加载时保存最初的window对象,并在卸载应用之时通过diff操作记录改过的属性来制作快照的技术。快照方案的优点是兼容性好,但缺点是会污染window,多个应用无法同时处于激活状态。 **ES6 proxy方案** ES6中的proxy语法可以创建一个对自定义对象代理,这样当在沙箱内部对window对象修改的时候,实际上修改的是自定义的全局对象,而不会影响到真正的window对象。其优点是不会污染window,但缺点是部分浏览器不支持proxy。 **结论** 通过使用ES6的proxy方案,我们可以实现一种安全且易于维护的数据隔离机制,即使在多个应用之间处于激活状态。这种方案可以有效地解决全局作用域变量污染问题,确保数据安全。

正文

前言

在微前端实践过程中有一个必然会遇到的问题:全局作用域变量的污染问题,具体来说就是window对象挂载数据会被主子应用获取和修改导致数据相互污染问题,这时候如果能在应用之间做个数据隔离,最好能实现一个沙箱环境,对解决问题很有帮助。

iframe方案

说到沙箱隔离,首先想到的是iframe,自带数据隔离能力,从iframe中获取到的window对象是一个全新和纯净的对象,然而在如果要作为沙箱执行业务代码的话是不行的,但是完全可以作为一个执行脚本环境,既安全,又简单:

const parent = window;
const frame = document.createElement('iframe');

const data = [1, 2, 3, 4, 5, 6];

// 当前页面给 iframe 发送消息
frame.onload = function (e) {
  frame.contentWindow.postMessage(data);
};

document.body.appendChild(frame);

// iframe 接收到消息后处理
const code = `return dataInIframe.filter((item) => item % 2 === 0)`;

frame.contentWindow.addEventListener('message', function (e) {
  const func = new frame.contentWindow.Function('dataInIframe', code);
  parent.postMessage(func(e.data));
});

// 父页面接收 iframe 发送过来的消息
parent.addEventListener(
  'message',
  function (e) {
    console.log('message from iframe:', e.data);
  },
  false,
);

快照方案

在微前端框架qiankun中提供了快照方案,其原理就是在应用加载之时保存最初的window对象,卸载应用之时通过diff操作记录改过的属性即制作快照,当再次激活应用的时候恢复之前的快照。该方案的缺点是会污染window导致,多个应用无法同时处于激活状态,优点是兼容性好。

// 保存差异的方式
function createSandbox(){
  let originWindow = {}
  let diffMap = {};
  return {
    toActive(){
      originWindow = {};
      // 保存初始window对象
      Object.keys(window).forEach(prop=>{
        originWindow[prop] = window[prop];
      })
      // 将上次退出的时候保存的差异还原回去,也就是恢复快照
      Object.keys(diffMap).forEach(prop=>{
        window[prop] = diffMap[prop];
      })
    },
    toInActive(){
      Object.keys(window).forEach(prop=>{
        if(window[prop] !== originWindow[prop]){
          // 保存差异
          diffMap[prop] = window[prop]
          // 还原现场
          window[prop] = originWindow[prop];
        }
      })
    }
  }
}


window.originData = '最初的window上的数据';

console.log(window.originData, window.a1, window.b1); // 最初的window上的数据 undefined undefined
const sandbox1 = createSandbox();  // 创建应用的时候,同时创建沙箱
sandbox1.toActive(); // 沙箱激活
window.a1 = 'aaaaa'; // 应用修改window上的属性
console.log(window.originData, window.a1, window.b1); // 最初的window上的数据 aaaaa undefined
sandbox1.toInActive(); // 切换应用前沙箱1退出
const sandbox2 = createSandbox(); // 创建应用的时候,同时创建沙箱
sandbox2.toActive(); // 沙箱激活
console.log(window.originData, window.a1, window.b1); // 最初的window上的数据 undefined undefined
window.b1 = 'bbbbb'; // 应用修改window上的属性
console.log(window.originData, window.a1, window.b1); // 最初的window上的数据 undefined bbbbb   和上面的数据做个对比
sandbox2.toInActive();  // 从应用2切换至1
sandbox1.toActive(); // 从应用2切换至1
console.log(window.originData, window.a1, window.b1); // 最初的window上的数据 aaaaa undefined 和上面的数据做个对比

sandbox1.toInActive();  // 从应用1切换至2
sandbox2.toActive(); // 从应用1切换至2
console.log(window.originData, window.a1, window.b1); // 最初的window上的数据 undefined bbbbb 和上面的数据做个对比

代理方案

使用ES6中的proxy语法对自定义的全局对象代理,这样当在沙箱内部对window对象修改的时候,实际上修改的是自定义的全局对象,而不会影响到真正的window对象。其优点是不会污染window,支持多个应用同时激活。 缺点是部分浏览器不支持proxy,

function createProxySandBox(){
  const rawWindow = window;
  const fakeWindow = {};
  const proxy = new Proxy(fakeWindow, {
    get:(target, p)=>{
      if(target.hasOwnProperty(p)){
        return target[p];
      }
      return rawWindow[p];
    },
    set(target, p, value){
      if(!target.hasOwnProperty(p) && rawWindow.hasOwnProperty(p)){
        rawWindow[p] = value
      } else {
        target[p] = value;
      }
    }
  })
  return proxy;
}
const sandbox1 = createProxySandBox();

((window) => {
  window.a = 'a';
})(sandbox1);

const sandbox2 = createProxySandBox();

((window) => {
  console.log(window.a)
  window.a = 'fff';
})(sandbox2);
console.log(window.a)

总结

proxy方案是比较优雅和实用的方案

与微前端中实现沙箱环境的方案调研相似的内容:

微前端中实现沙箱环境的方案调研

前言 在微前端实践过程中有一个必然会遇到的问题:全局作用域变量的污染问题,具体来说就是window对象挂载数据会被主子应用获取和修改导致数据相互污染问题,这时候如果能在应用之间做个数据隔离,最好能实现一个沙箱环境,对解决问题很有帮助。 iframe方案 说到沙箱隔离,首先想到的是iframe,自带数

Vue微前端架构与Qiankun实践理论指南

这篇文章介绍了微前端架构概念,聚焦于如何在Vue.js项目中应用Qiankun框架实现模块化和组件化,以达到高效开发和维护的目的。讨论了Qiankun的原理、如何设置主应用与子应用的通信,以及如何解决跨域问题和优化集成过程,从而实现前端应用的灵活扩展与组织。

微前端项目部署方案

本文旨在通过部署微前端项目的实践过程中沉淀出一套部署方案,针对项目分别部署在不同的服务器上的场景,就一些重点步骤、碰到的问题做了一些总结。

如何将微前端项目部署在同一台服务器同一个端口下

本文旨在通过部署微前端项目的实践过程中沉淀出一套部署方案,现就一些重点步骤、碰到的问题做了一些总结。

【数据中台商业化】数据中台微前端实践

我们设计目前的门户基座,可以快速浏览各个平台,同时串联数据开发与管理的工作,减少用户的试错成本,提升工作效率。

.NET 高效灵活的API速率限制解决方案

前言 FireflySoft.RateLimit是基于.NET Core和.NET Standard构建,支持多种速率限制算法和策略,包括固定窗口、滑动窗口、漏桶、令牌桶等。通过简单的配置和集成,开发者可以快速地将其应用到现有的Web API、微服务或中间件中,实现对请求的精确控制。 同时,该库还支

物理学又不存在了?ChatGPT:室温超导是物理学的一个梦想

大家好,我是小彭。 就在前天,一组微信聊天记录突然开始在各大群中流传: 随后,这一新闻直接引爆各大社交媒体,物理学又双叒叕不存在了吗? 到底是什么重磅消息呢? 原来在美国物理学会的三月会议上,美国纽约罗切斯特大学的 Ranga Dias 团队发布了一项研究成果 —— 他们发现了能够在室温环境下实现超

基于Traefik如何实现向后转发自动去掉前缀?

实践中, 往往会有这样的需求, 用户输入的url是ewhisper.cn/alert-manager/#/alerts, 但是转发到后端要变成/#/alerts, 如何基于 Traefik on K8S 实现?

在 NGINX 中根据用户真实 IP 进行限制

需求 需要根据用户的真实 IP 进行限制, 但是 NGINX 前边还有个 F5, 导致 deny 指令不生效. 阻止用户的真实 IP 不是 192.168.14.* 和 192.168.15.* 的访问请求. 实现 最简单的实现如下: :notebook: 前置条件: 需要 nginx 前边的 lo

[转帖]基于腾讯云微服务引擎(TSE) ,轻松实现云上全链路灰度发布

https://my.oschina.net/u/4587289/blog/8570699 1. 概述 软件开发过程中,应用发布非常频繁,通常情况下,开发或运维人员会将系统里所有服务同时上线,使得所有用户都使用上新版本。这样的操作时常会导致发布失败,或因发布前修改代码,线上出现 Bug。 假设一个在