# 五.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
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. 预加载应用

当用户没有调用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
import {toLoadPromise} from '../lifecycles/load';
async function loadApps(){
	// 预加载应用
	await Promise.all(appsToLoad.map(toLoadPromise));
}
1
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

用户load函数返回的bootstrapmountunmount可能是数组形式,我们将这些函数进行组合

# 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

这里为了更加直观,我就采用最简单的方法来实现,调用钩子,并修改应用状态

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
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

匹配到没有加载过的应用 (加载=> 启动 => 挂载)

const loadThenMountPromises = appsToLoad.map(async (app) => {
    app = await toLoadPromise(app);
    app = await toBootstrapPromise(app);
    return toMountPromise(app);
});
1
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
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
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

已经加载过了的应用 (启动 => 挂载)

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
上次更新: 2021/1/6 上午11:20:04