createSandboxContainer 创建沙箱
标签: 核心
# 目录
# createSandboxContainer
createSandboxContainer 函数创建沙箱,并且维护一些 patch 工作。
// src/sandbox/index.ts
/**
* 生成应用运行时沙箱
*
* 沙箱分两个类型:
* 1. app 环境沙箱
* app 环境沙箱是指应用初始化过之后,应用会在什么样的上下文环境运行。每个应用的环境沙箱只会初始化一次,因为子应用只会触发一次 bootstrap 。
* 子应用在切换时,实际上切换的是 app 环境沙箱。
* 2. render 沙箱
* 子应用在 app mount 开始前生成好的的沙箱。每次子应用切换过后,render 沙箱都会重现初始化。
*
* 这么设计的目的是为了保证每个子应用切换回来之后,还能运行在应用 bootstrap 之后的环境下。
*
* @param appName
* @param elementGetter
* @param scopedCSS
* @param useLooseSandbox
* @param excludeAssetFilter
* @param globalContext
*/
export function createSandboxContainer(
appName: string,
// 获取待挂载元素的方法
elementGetter: () => HTMLElement | ShadowRoot,
// 是否使用 scopedCSS
scopedCSS: boolean,
// 是否使用 lose mode sandbox
useLooseSandbox?: boolean,
// 沙箱资源白名单
excludeAssetFilter?: (url: string) => boolean,
// global env
globalContext?: typeof window,
) {
let sandbox: SandBox;
// 创建沙箱实例
if (window.Proxy) {
// lose mode 使用旧版沙箱,否则使用代理沙箱
sandbox = useLooseSandbox ? new LegacySandbox(appName, globalContext) : new ProxySandbox(appName, globalContext);
} else {
// 不支持 Proxy API 则使用快照沙箱,快照沙箱不支持多应用模式
sandbox = new SnapshotSandbox(appName);
}
// some side effect could be be invoked while bootstrapping, such as dynamic stylesheet injection with style-loader, especially during the development phase
// bootstrap 阶段的初始化工作,如动态样式表的插入,返回释放(恢复)副作用的数组
const bootstrappingFreers = patchAtBootstrapping(appName, elementGetter, sandbox, scopedCSS, excludeAssetFilter);
// mounting freers are one-off and should be re-init at every mounting time
let mountingFreers: Freer[] = [];
let sideEffectsRebuilders: Rebuilder[] = [];
return {
// sandbox 实例
instance: sandbox,
/**
* 沙箱被 mount
* 可能是从 bootstrap 状态进入的 mount
* 也可能是从 unmount 之后再次唤醒进入 mount
*/
async mount() {
/* ------------------------------------------ 因为有上下文依赖(window),以下代码执行顺序不能变 ------------------------------------------ */
/* ------------------------------------------ 1. 启动/恢复 沙箱------------------------------------------ */
sandbox.active();
// 分离出 bootstrapping 阶段和 mounting 阶段的副作用,能用 bootstrappingFreers.length 分离是因为加入 sideEffectsRebuilders 时把 bootstrappingFreers 放到了前面
const sideEffectsRebuildersAtBootstrapping = sideEffectsRebuilders.slice(0, bootstrappingFreers.length);
const sideEffectsRebuildersAtMounting = sideEffectsRebuilders.slice(bootstrappingFreers.length);
// must rebuild the side effects which added at bootstrapping firstly to recovery to nature state
// 执行 bootstrapping 阶段的副作用
if (sideEffectsRebuildersAtBootstrapping.length) {
sideEffectsRebuildersAtBootstrapping.forEach((rebuild) => rebuild());
}
/* ------------------------------------------ 2. 开启全局变量补丁 ------------------------------------------*/
// render 沙箱启动时开始劫持各类全局监听,尽量不要在应用初始化阶段有 事件监听/定时器 等副作用
// mount 阶段的 patch(初始化) 工作,如事件监听等,返回释放(恢复)副作用的数组
mountingFreers = patchAtMounting(appName, elementGetter, sandbox, scopedCSS, excludeAssetFilter);
/* ------------------------------------------ 3. 重置一些初始化时的副作用 ------------------------------------------*/
// 存在 rebuilder 则表明有些副作用需要重建
// 执行 mounting 阶段的副作用
if (sideEffectsRebuildersAtMounting.length) {
sideEffectsRebuildersAtMounting.forEach((rebuild) => rebuild());
}
// clean up rebuilders
// 所有 mounting 和 bootstrapping 阶段副作用执行完毕,清空 sideEffectsRebuilders
sideEffectsRebuilders = [];
},
/**
* 恢复 global 状态,使其能回到应用加载之前的状态
*/
async unmount() {
// record the rebuilders of window side effects (event listeners or timers)
// note that the frees of mounting phase are one-off as it will be re-init at next mounting
// 卸载时记录 bootstrapping 和 mounting 阶段的 rebuild 的副作用,以便在重新挂载时恢复到原始状态
// 注意:这里执行 rebuild 其实是 bootstrapping 和 mounting 阶段 patch 的 freer(恢复函数)。
// 注意:重新 mount 微应用时并不会重新执行 createSandboxContainer,因为创建的 sandbox 已经在闭包之中,但是 sandbox 的 mount 函数会被重新执行。
sideEffectsRebuilders = [...bootstrappingFreers, ...mountingFreers].map((free) => free());
// 关闭沙箱
sandbox.inactive();
},
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
这个函数的核心逻辑如下:
- 根据配置情况创建对应类型的沙箱:lose 模式创建 LegacySandbox,否则支持 Proxy API 的创建 ProxySandbox,不支持则创建 SnapshotSandbox。
- 返回对象中包含沙箱实例,沙箱的挂载函数,沙箱的卸载函数,这实际上是对不同类型的沙箱的封装,在外层维护了 patch 的副作用和副作用的恢复。
- 将挂载之前的阶段分成了 bootstrapping 和 mounting,这两个阶段中分别执行了一些 patch 工作,这部分之后详述。在 mount 时,将记录的所以的副作用执行,实际上是恢复上一次的 patch 的动作,在本次会重新 patch,并且在 unmount 时将 patch 记录下来。patch 的工作是一次性的。
编辑 (opens new window)
上次更新: 2022/09/06, 14:25:16