Skip to content

Feat/sandbox optimization #769

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

CunjieLee
Copy link

在使用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

@ClarkXia ClarkXia requested review from Copilot, ClarkXia and XXXMrG June 5, 2025 08:52
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces caching of global variables in the sandbox to reduce proxy overhead and improve script execution performance.

  • Added constant.ts to define and filter a list of globals for caching.
  • Prepended a destructuring statement in index.ts to cache these globals before executing the sandboxed script.

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/sandbox/src/index.ts Prepends destructured global variable declarations (cachedGlobals) before script execution.
packages/sandbox/src/constant.ts Defines globalsInES2015 and composes the cachedGlobals array of properties to cache.
Comments suppressed due to low confidence (3)

packages/sandbox/src/index.ts:196

  • [nitpick] The name scopedGlobalVariableDefinition is singular but contains multiple declarations. Consider renaming to scopedGlobalVariableDeclarations for clarity.
const scopedGlobalVariableDefinition = cachedGlobals.length ? `const {${cachedGlobals.join(',')}}=sandbox;` : '';

packages/sandbox/src/index.ts:195

  • [nitpick] Add a unit test to verify that sandbox execution with cached globals yields the same behavior as without caching, ensuring functional correctness.
// Concatenate scopedGlobalVariables into variable declarations to cache global variables, avoiding proxy traversal on every use.

packages/sandbox/src/constant.ts:55

  • Destructuring undefined as an identifier causes a syntax error since undefined is a reserved word. Remove it from cachedGlobals or cache it via a separate assignment (e.g., const undefinedVar = sandbox.undefined;).
'undefined',

@@ -191,6 +192,9 @@ export default class Sandbox {
this.createProxySandbox();
}
try {
// Concatenate scopedGlobalVariables into variable declarations to cache global variables, avoiding proxy traversal on every use.
const scopedGlobalVariableDefinition = cachedGlobals.length ? `const {${cachedGlobals.join(',')}}=sandbox;` : '';
script =scopedGlobalVariableDefinition +script
Copy link
Preview

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Add spaces around operators for better readability, e.g., script = scopedGlobalVariableDefinition + script;.

Suggested change
script =scopedGlobalVariableDefinition +script
script = scopedGlobalVariableDefinition + script;

Copilot uses AI. Check for mistakes.

@CunjieLee
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant