从阿里QianKun看前端沙箱隔离
关于QianKun: qiankun(乾坤)是由蚂蚁金服退出的微前端解决方案,基于Single-Spa进行二次开发,用于实现Web应用由单体应用到多个前端项目聚合的应用。而qiankun在Single-Spa上的封装的核心之一就是实现前端的沙箱隔离机制。
沙箱: 沙箱其实是一种工具,或者可以理解为一个黑盒,用于隔离当前执行的环境作用域和外部的其他作用域。而在JavaScript中就意味着,在沙箱中的操作被限死在当前作用域,不会对其他模块和个人沙箱造成任何影响。
Qiankun的沙箱隔离主要实现了三种模式:
- LegacySandbox
- ProxySandBox
- snapshotSanBox
其中LegacySanBox和ProxySanBox都主要是依赖于Proxy API实现(也由此可见Proxy + Reflect的组合在前端的使用中越来越突出了)。 而当在浏览器不支持Proxy的情况下才会降级为使用snapshotSandBox. LegacySandBox和ProxySanBox的不同之处在于:LegacySanBox用于singular单例模式,而多实例的场景将切换为ProxySanBox
LegacySanBox
export default class SingularProxySanBox implements SanBox {private addedPropsMapInSandbox = new map<PropertyKey, any>();private modifiedPropsOriginalValueMapInSandbox = new map<PropertyKey, any>();private currentUpdatedPropsValueMap = new map<PropertyKey, any>();name: string;proxy: WindowProxy;sandboxRunning = true;active(){...}inactive(){...}constructor(name: string){...}
}
字段 | 解释 |
---|---|
addedPropsMapInSandbox | 用于记录在沙盒运行期间新增的全局变量,用于在卸载自应用时还原全局变量 |
modifiedPropsOriginalValueMapInSandbox | 记录沙盒运行期间更新的全局变量,用于在卸载自应用时还原全局变量 |
currentUpdatedPropsValueMap | 记录在沙盒运行期间操作过的全局变量,用于在激活子应用是还原沙盒的状态 |
name | 沙盒名称 |
proxy | 可以理解为子应用中的Window/Global对象 |
sandboxRunning | 沙盒是否处于运行状态 |
active | 激活沙箱,在子应用挂载时使用 |
inactive | 卸载沙箱,在子应用卸载时使用 |
现在从Window.Proxy的set和get来分析LegacySandBox的沙箱实现:
const rawWindow = window;
const fakeWindow = Object.create(null) as Window;
const proxy = new Proxy(fakeWindow, {set(_: Window, p: PropertyKey, value: any): boolean {if(sandboxRunning){if(!rawWindow.hasOwnProperty(p)){addedPropsMapInSandbox.set(p, value);} else if(!modifiedPropsOriginalValueMapInSandbox.has(p) {const originalValue = (rawWindow as any)[p];modifiedPropsOriginalValueMapInSandbox.set(p, originalValue);}currentUpdatedPropsValueMap.set(p, value);(rawWindow as any)[p] = value;return true;}return true;}get(_: Window, p: PropertyKey): any {if(p === "top" || p === "window" || p === "self"){return proxy;}const value = (rawWindow as any)[p];if(typeof === "function" && !isConstructor(value)){if(value[boundValueSymbol]){return value[boundValueSymbol];}const boundValue = value.bind(rawWindow);Object.keys(value).forEach(key => (boundValue[key] = value[key]));Object.defineProperty(value, boundValueSymbol, { enumerable: false, value: boundValue });return boundValue;}return value;}
})
当调用set向子应用的proxy/window对象设置属性时,所有的属性设置和更新都会记录在addedPropsMapInSandbox,modifiedPropsOriginalValueMapInSandbox,然后在统一记录到currentUpdatedPropsValueMap中。而调用get从子应用proxy/window中取值时,对于非构造函数的函数取值将会this绑定到window之后在进行返回。
LegacySandBox在激活和卸载子应用时进行沙盒状态的还原和主应用状态的还原过程:
active(){if(!sandboxRunning){this.currentUpdatedPropsValueMap.forEach((v, p) => setWindowProp(p, v));}this.sandboxRunning = true;
}
inactive(){this.modifiedPropsOriginalValueMapInSandbox.forEach((v, p) => setWindowProp(p, v));this.addedPropsMapInSandbox.forEach((v, p) => setWindowProp(p, undefined, true));this.sandboxRunning = false;
}
- 在激活沙箱是会通过currentUpdatedPropsValueMap查询子应用的独立状态池(即沙箱激活状态下更新的全局状态),还原子应用状态
- 卸载沙箱时,通过addedPropsMapInSandbox删除在沙箱运行期间新增的全局状态,通过modifiedPropsOriginalValueMapInSandbox还原沙箱运行期间更新的全局状态
ProxySandbox
export default class ProxySandbox implements SanBox {private updateValueMap = new map<PropertyKey, any>();name: string;proxy: WindowProxy;sandboxRunning = true;active(){...}inactive(){...}constructor(name: string){...}
}
字段 | 解释 |
---|---|
updateValueMap | 记录沙箱更新的值,也即是每个子应用中的独立状态值 |
现在从window.Proxy的set和get来分析ProxySandBox如何实现沙箱隔离
constructor(name: string){this.name = name;const { proxy, sandboxRunning, updateValueMap } = this;const boundValueSymbol = Symbol("bound value");const rawWindow = window;const fakeWindow = Object.create(null) as Window;this.props = new Proxy(fakeWindow, {set(_: Window, p: PerpertyKey, value: any): boolean {if(sandboxRunning){updateValueMap.set(p, value);}return true;},get(_: Window, p: PropertyKey: string): any {if(p === "top" || p === "window" || p === "self"){return proxy;}const value = updateValueMap.get(p) || (rawWindow as any)[p];if(typeof === "function" && !isConstructor(value)){if(value[boundValueSymbol]){return value[boundValueSymbol];}const boundValue = value.bind(rawWindow);Object.keys(value).forEach(key => (boundValue[key] = value[key]));Object.defineProperty(value, boundValueSymbol, { enumerable: false, value: boundValue });return boundValue;}return value;}})
}
- 当调用set向子应用proxy/window中设置值时,所有的属性的设置和更新都会命中updateValueMap, 从而避免对全局状态产生影响,这样就完全隔离了子应用之间的状态,设置值和取值都优先对子应用的updateValueMap进行操作。
SnapshotSandbox
在浏览器不支持Proxy的情况下,将会降级为SnapshotSandbox实现沙箱:
export default class SingularProxySanBox implements SanBox {name: string;proxy: WindowProxy;sandboxRunning = true;private windowSnapshot!: Window;private modifyPropsMap: Record<any, any> = {};active(){...}inactive(){...}constructor(name: string){this.name = name;this.proxy = window;this.active();}
}
字段 | 解释 |
---|---|
windowSnapshot | window状态的快照 |
modifyPropsMap | 沙箱运行期间被修改的属性 |
- SnapshotSandbox沙箱主要是通过active激活子应用时记录window状态记录,在卸载是通过快照还原window对象实现.
active(){if(this.sandboxRunning){return;}this.windowSnapshot = {} as Window;iter(window, prop => {this.windowSnapshot[prop] = window[prop];})Object.keys(this.modifyPropsMap).forEach((p: any) => {window[p] = this.modifyPropsMap[p];})this.sandboxRunning = true;
}
inactive(){this.modifyPropsMap = {};iter(window, prop => {if(window[prop] !== this.windowSnapshot[prop]){this.modifyPropsMap[prop] = window[prop];window[prop] = this.windowSnapshot[prop];}})this.sandboxRunning = false;
}
- active函数: 在子应用激活时,为window对象打一个快照,记录沙箱激活前的状态,打完快照之后内部通过modifyPropsMap将window还原到上次沙箱运行环境,也就是恢复沙箱运行期间的window状态
- inactive函数:沙箱关闭时,通过遍历比较每个属性,将被改变的window对象属性记录在modifyPropsMap中,并通过快照还原被激活前沙箱的状态,相当于时清除沙箱运行期间全局变量的污染。
- SnapshotSandbox沙箱实现对window状态的隔离管理,但是在子应用运行期间将会对全局window对象进行污染,所以SnapshotSandbox只可以用于在单实例的情况下,在多实例的场景下将不在支持隔离。
从阿里QianKun看前端沙箱隔离相关推荐
- 前端技术探索 - 你不知道的JS 沙箱隔离
点击上方关注 前端技术江湖,我们一起学习,天天进步 一些「炒冷饭」背景介绍 本文并不会从头开始介绍 Web Worker 的基础知识和基本 API 的使用等(只是部分有涉及),若还未了解过 Web W ...
- 微前端之实践环境变量设置、快照沙箱隔离、代理沙箱隔离、css 样式隔离、父子应用间通信和子应用间通信
一.微前端之实践环境变量设置.快照沙箱隔离.代理沙箱隔离.css 样式隔离.父子应用间通信和子应用间通信 微前端环境变量设置,如下所示: 在 micro 下的 sandbox 中 performScr ...
- 你不知道的JS 沙箱隔离
点击上方 前端Q,关注公众号 回复加群,加入前端Q技术交流群 一些「炒冷饭」背景介绍 本文并不会从头开始介绍 Web Worker 的基础知识和基本 API 的使用等(只是部分有涉及),若还未了解过 ...
- qiankun 微前端_微前端方案 qiankun(实践及总结)
❝ 作者:沉末_ 链接:https://juejin.im/post/5ed73b73e51d4578724e3fa4 ❞ 什么是微前端? 我们先来看两个实际的场景: 1. 复用别的的项目页面 通常, ...
- 一文彻底搞懂前端沙箱
大厂技术 高级前端 Node进阶 点击上方 程序员成长指北,关注公众号 回复1,加入高级Node交流群 什么是"沙箱" 也称作:"沙箱/沙盒/沙盘".沙箱是 ...
- 【Web技术】1589- 带你了解前端沙箱到底是什么
什么是"沙箱" 也称作:"沙箱/沙盒/沙盘".沙箱是一种安全机制,为运行中的程序提供隔离环境.通常是作为一些来源不可信.具破坏力或无法判定程序意图的程序提供实验 ...
- vue项目集成乾坤(qiankun)微前端
vue项目集成qiankun微前端 集成乾坤微前端主应用: 1.安装 2.main.js中进行配置 3.配置路由 4.将主应用路由模式设置为history模式 子应用中配置(子应用无需安装乾坤) 1. ...
- qiankun微前端学习
微前端学习(qiankun.singleSpa) 一.微前端的优势 什么是微前端 微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略 几个核心价值:技术栈无关 ...
- 奇舞周刊第 440 期:一文彻底搞懂前端沙箱
记得点击文章末尾的" 阅读原文 "查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 一文彻底搞懂前端沙箱 沙箱是一种安全机制,为运行中的程序提供隔离环境.通常 ...
最新文章
- springboot 配置多线程
- Java 多线程同步和异步详解
- python数据分析准备_使用Python进行数据分析I 环境准备
- 解决 spring-cloud-starter-zipkin 启动错误
- shrio反序列漏洞修复_Apache Shiro Java 反序列化漏洞分析
- react新特性实例详解(memo、lazy、suspense、hooks)
- (七)Oracle学习笔记—— 游标
- Bailian4115 鸣人和佐助【BFS】
- 字符串,列表,元组,字典基本函数
- 高级I/O复用技术:Epoll的使用及一个完整的C实例
- 哈工大密码学实验CA
- linux makefile教程,Makefile简单入门教程
- Arm 中国原 CEO 被“罢免”,新指定“官方”:已获员工大力支持
- FineBI 新增字段后 更新缓慢问题
- (18)UVM sequencer和sequence
- SSH远程操作——一台电脑的的NAS之旅
- 虚拟盒子下装linux系统,eUnoBox(虚拟盒子) v3.14免费版
- 响应式扩展_响应式和无限扩展的JS动画
- Elasticsearch - 压测方案之 esrally 简介
- 深度学习-85:智慧地球/智慧城市/智慧家庭
热门文章
- GWAS分析中曼哈顿图如何显示SNP信息
- chrome操作系统_如何报告问题或发送有关Chrome操作系统的反馈
- vue使用百度富文本编辑器(ueditor)
- cdoj1334郭大侠与Rabi-Ribi
- windows 映射samba Linux服务器,输入正确的账号密码却提示“ 指定的网络密码不正确
- 《超越平凡的平面设计: 版式设计原理与应用》目录 —导读
- 瑞星助手无法启动(小狮子)的解决方法
- 关于windows10自动升级 电脑重启 蓝屏
- 服务器进入系统进度条蓝屏,电脑开机读完进度条之后就蓝屏
- 在北京公司要多长时间才可以申请摇车牌号