@umijs/plugins
Version:
165 lines (140 loc) • 5.44 kB
text/typescript
// @ts-nocheck
import { getPluginManager } from '@@/core/plugin';
import ReactDOM from 'react-dom';
import { ApplyPluginsType, __getRoot, history } from 'umi';
import { setModelState } from './qiankunModel';
const noop = () => {};
type Defer = {
promise: Promise<any>;
resolve(value?: any): void;
};
// @ts-ignore
const defer: Defer = {};
defer.promise = new Promise((resolve) => {
defer.resolve = resolve;
});
let render = noop;
let hasMountedAtLeastOnce = false;
export default () => defer.promise;
export const contextOptsStack: any[] = [];
function normalizeHistory(
history?: 'string' | Record<string, any>,
base?: string,
) {
let normalizedHistory: Record<string, any> = {};
if (base) normalizedHistory.basename = base;
if (history) {
if (typeof history === 'string') {
normalizedHistory.type = history;
} else {
normalizedHistory = history;
}
}
return normalizedHistory;
}
async function getSlaveRuntime() {
const config = await getPluginManager().applyPlugins({
key: 'qiankun',
type: ApplyPluginsType.modify,
initialValue: {},
async: true,
});
// 应用既是 master 又是 slave 的场景,运行时 slave 配置方式为 export const qiankun = { slave: {} }
const { slave } = config;
return slave || config;
}
export function genBootstrap(oldRender: typeof noop) {
return async (props: any) => {
const slaveRuntime = await getSlaveRuntime();
if (slaveRuntime.bootstrap) {
await slaveRuntime.bootstrap(props);
}
render = oldRender;
};
}
export function genMount(mountElementId: string) {
return async (props?: any) => {
// props 有值时说明应用是通过 lifecycle 被主应用唤醒的,而不是独立运行时自己 mount
if (typeof props !== 'undefined') {
setModelState(props);
const slaveRuntime = await getSlaveRuntime();
if (slaveRuntime.mount) {
await slaveRuntime.mount(props);
}
const { type, ...historyOpts } = normalizeHistory(
props?.history || {},
props?.base,
);
// 更新 clientRender 配置
const clientRenderOpts = {
callback: () => {
// 默认开启
// 如果需要手动控制 loading,通过主应用配置 props.autoSetLoading false 可以关闭
if (props.autoSetLoading && typeof props.setLoading === 'function') {
props.setLoading(false);
}
// 支持将子应用的 history 回传给父应用
if (typeof props?.onHistoryInit === 'function') {
props.onHistoryInit(history);
}
},
// 支持通过 props 注入 container 来限定子应用 mountElementId 的查找范围
// 避免多个子应用出现在同一主应用时出现 mount 冲突
rootElement:
props.container?.querySelector(`#${mountElementId}`) ||
document.getElementById(mountElementId),
basename: props.base,
// 支持 MicroAppWithMemoHistory 需要
historyType: type,
historyOpts: historyOpts,
// 当存在同一个 umi 子应用在同一个页面被多实例渲染的场景时(比如一个页面里,同时展示了这个子应用的多个路由页面)
// mount 钩子会被调用多次,但是具体什么时候对应的实例开始 render 则是不定的,即它调用 applyPlugins('modifyClientRenderOpts') 的时机是不确定的
// 为了保证每次 applyPlugins('modifyClientRenderOpts') 调用是生成正确的 history,我们需要这里通过闭包上下文维持 mount 调用时的一些配置信息
// FIXME 由于 umi history 是全局的,通过 import { history } from 'umi' 调用的永远都是最后一个调用 createHistory 产生的对象,所以这种场景下会存在子应用内部获取 history 时,获取到的是同一个 history 的问题。这种场景下就不能直接从 umi import history,而应该从组件的 props 中取
// getHistory() {
// // 动态改变 history
// const historyOptions = normalizeHistory(props.history, props.base);
// setCreateHistoryOptions(historyOptions);
//
// // FIXME 子应用嵌入模式下不支持热更
// return createHistory();
// },
};
contextOptsStack.push(clientRenderOpts);
}
// 第一次 mount defer 被 resolve 后umi 会自动触发 render,非第一次 mount 则需手动触发
if (hasMountedAtLeastOnce) {
render();
} else {
defer.resolve();
}
hasMountedAtLeastOnce = true;
};
}
export function genUpdate() {
return async (props: any) => {
setModelState(props);
const slaveRuntime = await getSlaveRuntime();
if (slaveRuntime.update) {
await slaveRuntime.update(props);
}
};
}
export function genUnmount(mountElementId: string) {
return async (props: any) => {
const root = __getRoot();
// support react 18 unmount
if (typeof root?.unmount === 'function') {
root.unmount();
} else {
const container = props?.container
? props.container.querySelector(`#${mountElementId}`)
: document.getElementById(mountElementId);
if (container) {
ReactDOM.unmountComponentAtNode(container);
}
}
const slaveRuntime = await getSlaveRuntime();
if (slaveRuntime.unmount) await slaveRuntime.unmount(props);
};
}