import { getState } from "./utils";
let instance: BusInstance | null = null;
class BusInstance {
private queue: Function[] = [];
subscription = (cb: Function) => {
this.queue.push(cb);
};
unsubscription = (cb: Function) => {
this.queue = this.queue.filter((item) => item !== cb);
};
notify = (data: any) => {
this.queue.forEach((cb) => {
if ("[object Function]" === Object.prototype.toString.call(cb)) {
cb(data);
}
});
};
}
export function init(value: BusInstance) {
// 父工程将自己的 bus 实例,传递给子工程
// 子工程调用 init 函数,这样父子工程共享同一个实例
instance = value;
}
export default function Bus() {
// 使用单例模式
// 除了第一次返回的都是同一个实例
if (!instance) {
instance = new BusInstance();
}
return instance;
}
export function subscription(func: Function) {
const bus = Bus();
bus.subscription(func);
}
export function unsubscription(func: Function) {
const bus = Bus();
bus.unsubscription(func);
}
export function notify(data: { type: string; payload: any }) {
const bus = Bus();
// 使用 setTimeout 原因是,防止在 reducer 中调用 notify 函数,导致触发另外一个 reducers
setTimeout(() => {
bus.notify(data);
});
}
export function destory() {
instance = null;
}
export function initGlobal(global: any) {
setTimeout(() => {
const [state, store] = getState();
if (store) {
store.dispatch({ type: "global/initGlobal", payload: global() });
}
});
}
本质上使用 single-spa 用法一致,主要是需要共享实例。
import Bus from "@static/common/bus";
import { getState, cloneDeep } from "@static/common/utils";
export const qiankun = function () {
return Promise.resolve({
apps: window.SUB_APP_CONFIG.map(({ name, entry, base }) => ({
name,
entry,
base,
props: {
// 父工程生成实例传递给子工程
bus: Bus(),
global() {
const [state] = getState();
if (state) {
return cloneDeep(state.global);
}
return {};
},
},
})),
prefetch: true,
defer: true,
lifeCycles: {
beforeLoad() {
loadingFn(true);
},
beforeMount() {
loadingFn(false);
},
},
});
};
import { Effect, qiankunStart, Reducer } from "umi";
import { getSideOptions, getState } from "@static/common/utils";
import { notify, subscription } from "@static/common/bus";
import globalModel, { GlobalState } from "@static/models/global";
const { namespace, state, reducers, effects, subscriptions } = globalModel;
const newEffects: {
[index: string]: Effect;
} = {
...effects,
*authUserInfo(anyAction, effectsCommandMap) {
if (effects) {
yield effects.authUserInfo(anyAction, effectsCommandMap);
if (window.singleSpaNavigate) {
setTimeout(() => {
qiankunStart();
});
}
}
},
};
const newReduccers: {
[index: string]: Reducer<GlobalState | undefined>;
} = {
...reducers,
updateCollapsed(state, { payload }) {
if (!state) return state;
state.collapsed = payload;
// 发布消息
notify({ type: "global/updateCollapsed", payload });
if (payload) {
state.nav.openKeys = "";
} else {
const { openKeys } = getSideOptions(state.nav.data || []);
state.nav.openKeys = openKeys;
}
state.nav = { ...state.nav };
return { ...state };
},
};
export default {
namespace,
state,
reducers: newReduccers,
effects: newEffects,
subscriptions,
};
import {
initGlobal,
init,
destory,
subscription,
unsubscription,
} from "@static/common/bus";
import { getState } from "@static/common/utils";
function baseFunc(data: any) {
const [, store] = getState();
if (store) {
const { type, payload } = data;
if ("global/updateCollapsed" === type) {
store.dispatch({ type, payload });
}
}
}
export const qiankun = function () {
if (window.singleSpaNavigate) {
return {
async bootstrap(props: any) {
// 共享实例
init(props.bus);
// 订阅
subscription(baseFunc);
},
async mount(props: any) {
initGlobal(props.global);
},
async unmount() {
// 取消订阅
unsubscription(baseFunc);
destory();
},
};
}
return Promise.resolve();
};
注意:理论上只要共享了实例,可以是父子、兄弟等等之间互相发送消息