# 五.reroute方法
这个方法是整个
Single-SPA
中最核心的方法,当路由切换时也会执行该逻辑
# 1.获取对应状态的app
import {getAppChanges} from '../applications/apps';
export function reroute() {
const {
appsToLoad, // 获取要去加载的app
appsToMount, // 获取要被挂载的
appsToUnmount // 获取要被卸载的
} = getAppChanges();
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
export function getAppChanges(){
const appsToUnmount = [];
const appsToLoad = [];
const appsToMount = [];
apps.forEach(app => {
const appShouldBeActive = app.status !== SKIP_BECAUSE_BROKEN && shouldBeActive(app);
switch (app.status) { // toLoad
case STATUS.NOT_LOADED:
case STATUS.LOADING_SOURCE_CODE:
if(appShouldBeActive){
appsToLoad.push(app);
}
break;
case STATUS.NOT_BOOTSTRAPPED: // toMount
case STATUS.NOT_MOUNTED:
if(appShouldBeActive){
appsToMount.push(app);
}
break
case STATUS.MOUNTED: // toUnmount
if(!appShouldBeActive){
appsToUnmount.push(app);
}
}
});
return {appsToUnmount,appsToLoad,appsToMount}
}
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
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
根据状态筛选对应的应用
# 2. 预加载应用
当用户没有调用start
方法时,我们默认会先进行应用的加载
if(started){
return performAppChanges();
}else{
return loadApps();
}
async function performAppChanges(){
// 启动逻辑
}
async function loadApps(){
// 预加载应用
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
import {toLoadPromise} from '../lifecycles/load';
async function loadApps(){
// 预加载应用
await Promise.all(appsToLoad.map(toLoadPromise));
}
1
2
3
4
5
2
3
4
5
import { LOADING_SOURCE_CODE, NOT_BOOTSTRAPPED } from "../applications/app.helpers";
function flattenFnArray(fns) { // 将函数通过then链连接起来
fns = Array.isArray(fns) ? fns : [fns];
return function(props) {
return fns.reduce((p, fn) => p.then(() => fn(props)), Promise.resolve());
}
}
export async function toLoadPromise(app) {
app.status = LOADING_SOURCE_CODE;
let { bootstrap, mount, unmount } = await app.loadApp(app.customProps); // 调用load函数拿到接入协议
app.status = NOT_BOOTSTRAPPED;
app.bootstrap = flattenFnArray(bootstrap);
app.mount = flattenFnArray(mount);
app.unmount = flattenFnArray(unmount);
return app;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
用户load函数返回的
bootstrap
、mount
、unmount
可能是数组形式,我们将这些函数进行组合
# 3. app
运转逻辑
路由切换时卸载不需要的应用
import {toUnmountPromise} from '../lifecycles/unmount';
import {toUnloadPromise} from '../lifecycles/unload';
async function performAppChanges(){
// 卸载不需要的应用,挂载需要的应用
let unmountPromises = appsToUnmount.map(toUnmountPromise).map(unmountPromise=>unmountPromise.then(toUnloadPromise));
}
1
2
3
4
5
6
2
3
4
5
6
这里为了更加直观,我就采用最简单的方法来实现,调用钩子,并修改应用状态
import { UNMOUNTING, NOT_MOUNTED ,MOUNTED} from "../applications/app.helpers";
export async function toUnmountPromise(app){
if(app.status != MOUNTED){
return app;
}
app.status = UNMOUNTING;
await app.unmount(app);
app.status = NOT_MOUNTED;
return app;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
import { NOT_LOADED, UNLOADING } from "../applications/app.helpers";
const appsToUnload = {};
export async function toUnloadPromise(app){
if(!appsToUnload[app.name]){
return app;
}
app.status = UNLOADING;
delete app.bootstrap;
delete app.mount;
delete app.unmount;
app.status = NOT_LOADED;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
匹配到没有加载过的应用 (加载=> 启动 => 挂载)
const loadThenMountPromises = appsToLoad.map(async (app) => {
app = await toLoadPromise(app);
app = await toBootstrapPromise(app);
return toMountPromise(app);
});
1
2
3
4
5
2
3
4
5
这里需要注意一下,可能还有没加载完的应用这里不要进行重复加载
export async function toLoadPromise(app) {
if(app.loadPromise){
return app.loadPromise;
}
if (app.status !== NOT_LOADED) {
return app;
}
app.status = LOADING_SOURCE_CODE;
return (app.loadPromise = Promise.resolve().then(async ()=>{
let { bootstrap, mount, unmount } = await app.loadApp(app.customProps);
app.status = NOT_BOOTSTRAPPED;
app.bootstrap = flattenFnArray(bootstrap);
app.mount = flattenFnArray(mount);
app.unmount = flattenFnArray(unmount);
delete app.loadPromise;
return app;
}));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { BOOTSTRAPPING, NOT_MOUNTED,NOT_BOOTSTRAPPED } from "../applications/app.helpers.js";
export async function toBootstrapPromise(app) {
if(app.status !== NOT_BOOTSTRAPPED){
return app;
}
app.status = BOOTSTRAPPING;
await app.bootstrap(app.customProps);
app.status = NOT_MOUNTED;
return app;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
import { MOUNTED, MOUNTING,NOT_MOUNTED } from "../applications/app.helpers.js";
export async function toMountPromise(app) {
if (app.status !== NOT_MOUNTED) {
return app;
}
app.status = MOUNTING;
await app.mount();
app.status = MOUNTED;
return app;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
已经加载过了的应用 (启动 => 挂载)
const mountPromises = appsToMount.map(async (app) => {
app = await toBootstrapPromise(app);
return toMountPromise(app);
});
await Promise.all(unmountPromises); // 等待先卸载完成
await Promise.all([...loadThenMountPromises,...mountPromises]);
1
2
3
4
5
6
2
3
4
5
6