无界沙箱运行
前言
在上一篇文章中,我们分析了无界的沙箱激活机制,沙箱激活过程中,将子应用的html模板渲染到webComponent中,但是距离子应用的运行还差一步,子应用的js还没有运行,本篇文章我们将一起分析沙箱的运行机制
运行js沙箱
沙箱的运行入口是start
函数,函数的定义如下:
class Wujie {
public async start(getExternalScripts: () => ScriptResultList): Promise<void> {
// debugger
this.execFlag = true;
// 执行脚本
const scriptResultList = await getExternalScripts();
// 假如已经被销毁了
if (!this.iframe) return;
const iframeWindow = this.iframe.contentWindow;
// 标志位,执行代码前设置
iframeWindow.__POWERED_BY_WUJIE__ = true;
// 同步代码
const syncScriptResultList: ScriptResultList = [];
scriptResultList.forEach((scriptResult) => {
// other code 处理defer和async
// 同步代码
syncScriptResultList.push(scriptResult);
});
// 同步代码
syncScriptResultList.forEach((scriptResult) => {
this.execQueue.push(() =>
scriptResult.contentPromise.then((content) =>
this.fiber
? requestIdleCallback(() => insertScriptToIframe({ ...scriptResult, content }, iframeWindow))
: insertScriptToIframe({ ...scriptResult, content }, iframeWindow)
)
);
});
//框架主动调用mount方法
this.execQueue.push(this.fiber ? () => requestIdleCallback(() => this.mount()) : () => this.mount());
// 执行队列
this.execQueue.shift()();
// 所有的execQueue队列执行完毕,start才算结束,保证串行的执行子应用
return new Promise((resolve) => {
this.execQueue.push(() => {
resolve();
this.execQueue.shift()?.();
});
});
}
}
上面的代码中,我们略作删减,留下一些关键代码,可以看到在start
函数中,最终是要将子应用的js代码插入到iframe中,然后调用mount
函数,实现的步骤如下:
- 向执行队列
execQueue
中添加子应用js代码 - 向执行队列
execQueue
中添加mount
函数,当子应用挂载时调用 - 依次执行队列
execQueue
中的函数
如下图所示:
如上所示使我们的队列中等待执行的函数,当我们调用 this.execQueue.shift()();
时,就会执行队列中的函数,并且会串行的执行所有的队列中的代码,那么这是怎么实现的呢?我们通过一个例子来了解下:
这张图片中,蓝色的为我们的子应用代码,红色的为我们的mount
函数,绿色的为我们的start
函数,当我们执行队列中函数时,遇到子应用代码则插入到iframe中,遇到mount
函数则调用mount
函数,当队列中的函数执行完毕时,start
函数才算执行完毕.
在执行子应用代码时,有一个特殊的处理,为了保证队列执行的延续性,在一个子应用代码执行完毕后,会像iframe中插入一段辅助代码"if(window.__WUJIE.execQueue && window.__WUJIE.execQueue.length){ window.__WUJIE.execQueue.shift()()}";
,这样就保证了队列中所有函数的的串行执行。如下图所示:
mount钩子函数
当我们的子应用代码执行完毕后,会调用mount
函数,mount
函数的定义如下:
class Wujie {
public mount(): void {
if (this.mountFlag) return;
if (isFunction(this.iframe.contentWindow.__WUJIE_MOUNT)) {
removeLoading(this.el);
this.lifecycles?.beforeMount?.(this.iframe.contentWindow);
this.iframe.contentWindow.__WUJIE_MOUNT();
this.lifecycles?.afterMount?.(this.iframe.contentWindow);
this.mountFlag = true;
}
this.execQueue.shift()?.();
}
}
mount
函数中,会调用子应用的__WUJIE_MOUNT
函数,这个函数是子应用中定义的,如下我们在子应用中定义:
window.__WUJIE_MOUNT = () => {
const router = new VueRouter({ base, routes });
instance = new Vue({ router, render: (h) => h(App) }).$mount("#app");
};
所以当我们调用完mount
函数后,子应用就会被挂载到我们的#app
中,这样就完成了子应用的挂载
总结
本篇文章我们分析了无界的沙箱运行机制,无界的沙箱运行机制是通过一个队列来实现的,
当我们调用start
函数时,会将子应用的js代码插入到iframe中,然后调用mount
函数,这其中也有一些特殊的处理,比如在子应用代码执行完毕后,会像iframe中插入一段辅助代码来保证队列继续往后执行
最后的最后
关于无界的沙箱运行机制我们用了7篇文章来分析,从无界的整体架构,到适配层,再到核心层,在核心层中,我们又分析了沙箱的创建,激活,运行,这其中涉及到了很多的知识点,比如webComponent
,pxoxy
等等,其中还有很多点我们没有深入分析,
但好在我们对整体的运行流程有了一个大概的了解,当我们以后想要分析某一块功能点时,也能够快速定位我们要分析的代码。
如果觉得本文有帮助 记得点赞三连哦 十分感谢!