乾坤js隔离

2023-10-24 06:28
文章标签 js 隔离 乾坤

本文主要是介绍乾坤js隔离,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

乾坤,作为一款微前端领域的知名框架,其建立在single-spa基础上。相较于single-spa,乾坤做了两件重要的事情,其一是加载资源,第二是进行资源隔离。而资源隔离又分为Js资源隔离和css资源隔离。

我们把Js隔离机制常常称作沙箱,事实上,乾坤有三种Js隔离机制,并且在源代码中也是以 SnapshotSandbox、LegacySandbox、ProxySandbox三个类名来指代三种不同的隔离机制。下文我们统一以快照沙箱、支持单应用的代理沙箱、支持多应用的代理沙箱,来代表这三种不同的Js隔离机制。

1、快照沙箱SnapshotSandbox:有个缺点,就是需要遍历window上的所有属性,性能较差。
2、LegacySandbox,可以实现和快照沙箱一样的功能,但是却性能更好,和SnapshotSandbox一样,由于会污染全局的window,LegacySandbox也仅仅允许页面同时运行一个微应用,所以我们也称LegacySandbox为支持单应用的代理沙箱。
3、ProxySandbox,可以支持一个页面运行多个微应用,因此我们称ProxySandbox为支持多应用的代理沙箱。
LegacySandbox在未来应该会消失,因为LegacySandbox可以做的事情,ProxySandbox都可以做,而SanpsshotSandbox因为向下兼容的原因反而会和ProxySandbox长期并存。下面我们就编码实现这三种沙箱机制的核心逻辑。

1、快照沙箱-极简版

class SnapshotSandBox{windowSnapshot = {};modifyPropsMap = {};active(){for(const prop in window){this.windowSnapshot[prop] = window[prop];}Object.keys(this.modifyPropsMap).forEach(prop=>{window[prop] = this.modifyPropsMap[prop];});}inactive(){for(const prop in window){if(window[prop] !== this.windowSnapshot[prop]){this.modifyPropsMap[prop] = window[prop];window[prop] = this.windowSnapshot[prop];}}}
}
// 验证:
let snapshotSandBox = new SnapshotSandBox();
snapshotSandBox.active();
window.city = 'Beijing';
console.log("window.city-01:", window.city);
snapshotSandBox.inactive();
console.log("window.city-02:", window.city);
snapshotSandBox.active();
console.log("window.city-03:", window.city);
snapshotSandBox.inactive();//输出:
//window.city-01: Beijing
//window.city-02: undefined
//window.city-03: Beijing

从上面的代码可以看出,快照沙箱的核心逻辑很简单,就是在激活沙箱和沙箱失活的时候各做两件事情。

注:沙箱激活 就是此时我们的微应用处于运行中,这个阶段有可能对window上的属性进行操作改变;沙箱失活 就是此时我们的微应用已经停止了对window的影响

在沙箱激活的时候:

  • 记录window当时的状态(我们把这个状态称之为快照,也就是快照沙箱这个名称的来源);

  • 恢复上一次沙箱失活时记录的沙箱运行过程中对window做的状态改变,也就是上一次沙箱激活后对window做了哪些改变,现在也保持一样的改变。

在沙箱失活的时候:

  • 记录window上有哪些状态发生了变化(沙箱自激活开始,到失活的这段时间);

  • 清除沙箱在激活之后在window上改变的状态,从代码可以看出,就是让window此时的属性状态和刚激活时候的window的属性状态进行对比,不同的属性状态就以快照为准,恢复到未改变之前的状态。

从上面可以看出,快照沙箱存在两个重要的问题:

  • 会改变全局window的属性,如果同时运行多个微应用,多个应用同时改写window上的属性,势必会出现状态混乱,这也就是为什么快照沙箱无法支持多各微应用同时运行的原因。关于这个问题,下文中支持多应用的代理沙箱可以很好的解决这个问题;

  • 会通过for(prop in window){}的方式来遍历window上的所有属性,window属性众多,这其实是一件很耗费性能的事情。关于这个问题支持单应用的代理沙箱支持多应用的代理沙箱都可以规避。

2、支持单应用的代理沙箱-极简版

class LegacySandBox{addedPropsMapInSandbox = new Map();modifiedPropsOriginalValueMapInSandbox = new Map();currentUpdatedPropsValueMap = new Map();proxyWindow;setWindowProp(prop, value, toDelete = false){if(value === undefined && toDelete){delete window[prop];}else{window[prop] = value;}}active(){this.currentUpdatedPropsValueMap.forEach((value, prop)=>this.setWindowProp(prop, value));}inactive(){this.modifiedPropsOriginalValueMapInSandbox.forEach((value, prop)=>this.setWindowProp(prop, value));this.addedPropsMapInSandbox.forEach((_, prop)=>this.setWindowProp(prop, undefined, true));}constructor(){const fakeWindow = Object.create(null);this.proxyWindow = new Proxy(fakeWindow,{set:(target, prop, value, receiver)=>{const originalVal = window[prop];if(!window.hasOwnProperty(prop)){this.addedPropsMapInSandbox.set(prop, value);}else if(!this.modifiedPropsOriginalValueMapInSandbox.has(prop)){this.modifiedPropsOriginalValueMapInSandbox.set(prop, originalVal);}this.currentUpdatedPropsValueMap.set(prop, value);window[prop] = value;},get:(target, prop, receiver)=>{return target[prop];}});}
}
// 验证:
let legacySandBox = new LegacySandBox();
legacySandBox.active();
legacySandBox.proxyWindow.city = 'Beijing';
console.log('window.city-01:', window.city);
legacySandBox.inactive();
console.log('window.city-02:', window.city);
legacySandBox.active();
console.log('window.city-03:', window.city);
legacySandBox.inactive();
// 输出:
// window.city-01: Beijing
// window.city-02: undefined
// window.city-03: Beijing

从上面的代码可以看出,其实现的功能和快照沙箱是一模一样的,不同的是,通过三个变量来记住沙箱激活后window发生变化过的所有属性,这样在后续的状态还原时候就不再需要遍历window的所有属性来进行对比,提升了程序运行的性能。但是这仍然改变不了这种机制仍然污染了window的状态的事实,因此也就无法承担起同时支持多个微应用运行的任务。

3、支持多应用的代理沙箱-极简版

class ProxySandBox{proxyWindow;isRunning = false;active(){this.isRunning = true;}inactive(){this.isRunning = false;}constructor(){const fakeWindow = Object.create(null);this.proxyWindow = new Proxy(fakeWindow,{set:(target, prop, value, receiver)=>{if(this.isRunning){target[prop] = value;}},get:(target, prop, receiver)=>{return  prop in target ? target[prop] : window[prop];}});}
}
// 验证:
let proxySandBox1 = new ProxySandBox();
let proxySandBox2 = new ProxySandBox();
proxySandBox1.active();
proxySandBox2.active();
proxySandBox1.proxyWindow.city = 'Beijing';
proxySandBox2.proxyWindow.city = 'Shanghai';
console.log('active:proxySandBox1:window.city:', proxySandBox1.proxyWindow.city);
console.log('active:proxySandBox2:window.city:', proxySandBox2.proxyWindow.city);
console.log('window:window.city:', window.city);
proxySandBox1.inactive();
proxySandBox2.inactive();
console.log('inactive:proxySandBox1:window.city:', proxySandBox1.proxyWindow.city);
console.log('inactive:proxySandBox2:window.city:', proxySandBox2.proxyWindow.city);
console.log('window:window.city:', window.city);
// 输出:
// active:proxySandBox1:window.city: Beijing
// active:proxySandBox2:window.city: Shanghai
// window:window.city: undefined
// inactive:proxySandBox1:window.city: Beijing
// inactive:proxySandBox2:window.city: Shanghai
// window:window.city: undefined

从上面的代码可以发现,ProxySandbox,完全不存在状态恢复的逻辑,同时也不需要记录属性值的变化,因为所有的变化都是沙箱内部的变化,和window没有关系,window上的属性至始至终都没有受到过影响。我们可能会问,ProxySandbox已经这么好了,性能优良还支持多个微应用同时运行,那自然也支持单个微应用运行,那LegacySandbox存在还有什么意义呢,这个问题问得很好,其实本身在未来存在的意义也不大了,只不过是因为历史原因还在服役罢了,从Legacy这个单词就已经能推断出LegacySandbox在乾坤中的位置。我们可能还会继续问,那SnapshotSandbox存在还有什么意义呢,这个还真有不小作用,Proxy是新ES6的新事物,低版本浏览器无法兼容所以SnapshotSandbox还会长期存在。虽然这里极简版本逻辑很少,但是由于ProxySandbox要支持多个微应用运行,里面的逻辑会SnapshotsSandbox、LegacySandbox的都要丰富一些。

其实到了这里,如果读者朋友已经理解了上面的思路,就可以说已经理解了乾坤的Js隔离机制。

这篇关于乾坤js隔离的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/273292

相关文章

PostgreSQL 默认隔离级别的设置

《PostgreSQL默认隔离级别的设置》PostgreSQL的默认事务隔离级别是读已提交,这是其事务处理系统的基础行为模式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一 默认隔离级别概述1.1 默认设置1.2 各版本一致性二 读已提交的特性2.1 行为特征2.2

使用Python获取JS加载的数据的多种实现方法

《使用Python获取JS加载的数据的多种实现方法》在当今的互联网时代,网页数据的动态加载已经成为一种常见的技术手段,许多现代网站通过JavaScript(JS)动态加载内容,这使得传统的静态网页爬取... 目录引言一、动态 网页与js加载数据的原理二、python爬取JS加载数据的方法(一)分析网络请求1

MySQL中的事务隔离级别详解

《MySQL中的事务隔离级别详解》在MySQL中,事务(Transaction)是一个执行单元,它要么完全执行,要么完全回滚,以保证数据的完整性和一致性,下面给大家介绍MySQL中的事务隔离级别详解,... 目录一、事务并发问题二、mysql 事务隔离级别1. READ UNCOMMITTED(读未提交)2

MySQL中隔离级别的使用详解

《MySQL中隔离级别的使用详解》:本文主要介绍MySQL中隔离级别的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录引言undo log的作用MVCC的实现有以下几个重要因素如何根据这些因素判断数据值?可重复读和已提交读区别?串行化隔离级别的实现幻读和可

VSCode中配置node.js的实现示例

《VSCode中配置node.js的实现示例》本文主要介绍了VSCode中配置node.js的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一.node.js下载安装教程二.配置npm三.配置环境变量四.VSCode配置五.心得一.no

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

Node.js 数据库 CRUD 项目示例详解(完美解决方案)

《Node.js数据库CRUD项目示例详解(完美解决方案)》:本文主要介绍Node.js数据库CRUD项目示例详解(完美解决方案),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考... 目录项目结构1. 初始化项目2. 配置数据库连接 (config/db.js)3. 创建模型 (models/

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Node.js net模块的使用示例

《Node.jsnet模块的使用示例》本文主要介绍了Node.jsnet模块的使用示例,net模块支持TCP通信,处理TCP连接和数据传输,具有一定的参考价值,感兴趣的可以了解一下... 目录简介引入 net 模块核心概念TCP (传输控制协议)Socket服务器TCP 服务器创建基本服务器服务器配置选项服