icestark icon indicating copy to clipboard operation
icestark copied to clipboard

Feat/sandbox optimization

Open CunjieLee opened this issue 6 months ago • 1 comments

在使用icestarkk开启沙箱接入子应用的时,我们发现在遇到执行的js代码需要频繁访问沙箱时,会遇到明显的性能问题和卡顿。 针对此问题,笔者想到了一种解决方法: 在使用时将常用的变量进行缓存,这样相关的变量就只会访问一次沙箱,之后代码执行时就不再需要再多次重复访问沙箱。

在后续学习中发现另一个微前端框架qiankun也在沙箱中默认开启使用类似的缓存方法来对沙箱性能进行优化,pr中的缓存的变量以及写法参考了qiankun。

下面是一个将icestark的sandbox核心代码简化后的例子

// 简易沙箱
const sandbox = new Proxy(window, {
    get(target, prop, receiver) {
        if (prop === Symbol.unscopables) {
            return undefined;
        }
        console.log('访问了沙箱', prop)
        return target[prop]
    }
});

const start = performance.now();
const testScript = `
    // 执行的js代码,会频繁访问沙箱
    for (let i = 1; i <= 1000; i++) {
        const li = document.createElement('li');
        Math.abs(i)
        Math.random()
        new Array(3)
        let arr = new Array(3)
        Array.isArray(arr)
    }
        `;
const execScript = `with (sandbox) {;${testScript}\n}`;
const code = new Function('sandbox', execScript).bind(sandbox);
code(sandbox);
console.log('Time taken: ', performance.now() - start);

未进行缓存时候,以上代码中的Math等变量每执行一次都会访问沙箱造成不必要的性能开销,以上代码实际执行下来的时间大约在70ms。 如果简易沙箱开启缓存后,代码实际执行时间在0.8ms,性能差距非常明显。(图片在最后) 下面是简易沙箱开启缓存的示例代码:

const testScript = `
    // 下面是添加的缓存的代码
    const Math = sandbox.Math
    const Array = sandbox.Array
    const undefined = sandbox.undefined
    const Object = sandbox.Object
    const Promise = sandbox.Promise
    const setTimeout = sandbox.setTimeout
    const console = sandbox.console
    const document = sandbox.document
    // 执行的js代码,会频繁访问沙箱
    for (let i = 1; i <= 1000; i++) {
        const li = document.createElement('li');
        Math.abs(i)
        Math.random()
        new Array(3)
        let arr = new Array(3)
        Array.isArray(arr)
    }
        `;

在icestark沙箱中加上缓存后实际测试的效果: 测试示例代码未进行缓存前平均执行时间在54ms左右,开启缓存后的平均执行时间在19ms作用,速度提升了三倍左右 测试代码:

describe('sandbox: performance', () => {
  const testScript = `
    for (let i = 1; i <= 1000; i++) {
      const li = document.createElement('li');
      Math.abs(i);
      Math.random();
      new Array(3);
      let arr = new Array(3);
      Array.isArray(arr);
      Array.isArray([1,2,i]);
      new Date()
      requestAnimationFrame((i) => animate(i));
      JSON.stringify(i)
      new Map()
      const object1 = {
        a: "somestring",
        b: i,
        c: false,
      };
      Object.values(object1)
    }
  `;
  test('execution time', () => {

    // 测试沙盒执行时间
    const sandbox = new Sandbox();
    const sandboxStart = performance.now();
    sandbox.execScriptInSandbox(testScript);
    const sandboxTime = performance.now() - sandboxStart;
    console.log(`Sandbox execution time: ${sandboxTime.toFixed(2)}ms`);

    expect(sandboxTime).toBeGreaterThan(0);

    // 清理沙盒
    sandbox.clear();
  });
});

简易沙箱示例执行结果图片: icestarkpic1 icestarkpic2

CunjieLee avatar Jun 05 '25 08:06 CunjieLee

补充:已通过代码中全部sandbox测试用例

CunjieLee avatar Jun 06 '25 01:06 CunjieLee