UNPKG

@umijs/plugins

Version:
165 lines (140 loc) 5.44 kB
// @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); }; }