为什么要隔离环境

  • 假如我们在其中一个子应用中设置全局变量

    1. window.a = 1
  • 如果我们没有做隔离环境,当我们切换到另一个子应用时,也可以获取到这个全局变量

  • 对于这样的情况,如果是公共的依赖变量是没有问题的,
  • 但是如果我们这个变量只在当前子应用应用,切换到另一个子应用时不需要这个变量。
  • 由于我们可以获取到,多多少少对我们变量管理会有些影响
  • 所以,我们当前子应用设置的变量只在当前使用,切换之后就销毁,如果有需要全局使用的公共变量,我们可以设置在主应用中,通过主应用来获取

6-3-4 运行环境隔离-快照沙箱、代理沙箱 - 图1

快照沙箱

  • 快照沙箱封装:main/micro/sandbox/snapshotSandBox.js

    1. // 快照沙箱
    2. // 针对给当前全局变量实现快照方式,来记录沙箱内容,子应用切换后,全局变量恢复初始值
    3. // 应用场景:比较老版本的浏览器
    4. export class SnapShotSandBox {
    5. constructor() {
    6. // 1. 代理对象
    7. this.proxy = window;
    8. this.active();
    9. }
    10. // 沙箱激活
    11. active() {
    12. this.snapshot = new Map(); // 创建 window 对象的沙箱快照
    13. // 遍历全局环境
    14. for (const key in window) {
    15. // eslint-disable-next-line no-prototype-builtins
    16. if (window.hasOwnProperty(key)) {
    17. // 将window上的属性进行拍照
    18. this.snapshot[key] = window[key];
    19. }
    20. }
    21. console.log(this.snapshot);
    22. }
    23. // 沙箱销毁
    24. inactive() {
    25. console.log(this.snapshot);
    26. for (const key in window) {
    27. // eslint-disable-next-line no-prototype-builtins
    28. if (window.hasOwnProperty(key)) {
    29. // 将上次快照的结果和本次window属性做对比
    30. if (key == 'a') {
    31. console.log('对比', key, '----', window[key], '----', this.snapshot[key]);
    32. }
    33. if (window[key] !== this.snapshot[key]) {
    34. // 还原window
    35. window[key] = this.snapshot[key];
    36. }
    37. }
    38. }
    39. }
    40. }
  • 执行子应用js脚本前,创建沙箱环境main/micro/sandbox/index.js

    1. export const sandbox = (app, script) => {
    2. // 创建沙箱环境
    3. const proxy = new SnapShotSandBox();
    4. // 判断当前应用有没有沙箱
    5. if (!app.proxy) {
    6. app.proxy = proxy;
    7. }
    8. ...
    9. }
  • 因为我们会加载多个子应用的js脚本,我们可以通过输出,可以看出,第一次的沙箱环境是纯环境的(没有自定义变量)

    • 加载子应用第一个js脚本

image.png

  • 加载子应用最后一个js脚本

image.png

  • 所以我们把第一个快照存储到子应用上,后面销毁的时候做对比,就不会带着当前子应用设置的变量了。

  • 切换子应用时,销毁上一个子应用以及对应的沙箱main/micro/lifeCycle.js/index.js

    1. // 有上一个子应用,销毁子应用
    2. if (prevApp && prevApp.unmount) {
    3. if (prevApp.proxy) {
    4. prevApp.proxy.inactive() // 将沙箱销毁
    5. }
    6. await destoryed(prevApp)
    7. }

    代理沙箱

  • 使用ES6中的Proxy可以更好地实现代理机制 ```javascript export const isFunction = (value) => typeof value === ‘function’;

let defaultValue = {}; // 代理沙箱 export class ProxySandBox { constructor() { this.proxy = null; this.active(); } active() { // 当我们操作window对象时,操作的是代理对象 // 设置的值放在defaultValue中 this.proxy = new Proxy(window, { get(target, propKey) { // console.log(‘propKey’, propKey); // 函数做特殊处理,this指向原对象 if (isFunction(target[propKey])) { return target[propKey].bind(target) } // 如果代理值查找不到就使用原值 return defaultValue[propKey] || target[propKey]; }, set(target, propKey, value) { defaultValue[propKey] = value return true } }); } inactive() { defaultValue = {}; console.log(‘关闭沙箱’); } } ```

  • 之前我们做的快照沙箱,在主应用中可以获取到子应用的全局变量
  • 使用代理沙箱后,子应用的全局变量只能在子应用中使用。