mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-04-06 03:59:53 +08:00
feat(plugin-qiankun): 支持微应用使用useModel('@@qiankunStateFromMain')获取主应用状态
This commit is contained in:
parent
d5c267f5d1
commit
8cb00db93c
@ -1,2 +1,4 @@
|
||||
export const defaultMainRootId = '#root-master';
|
||||
export const defaultHistoryType = 'hash';
|
||||
export const qiankunStateForMicroModelNamespace = '@@qiankunStateForMicro';
|
||||
export const qiankunStateFromMainModelNamespace = '@@qiankunStateFromMain';
|
||||
|
@ -23,6 +23,9 @@ export const MicroApp = defineComponent({
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
settings: Object,
|
||||
lifeCycles: Object,
|
||||
className: String,
|
||||
},
|
||||
setup(props) {
|
||||
const {
|
||||
@ -32,51 +35,110 @@ export const MicroApp = defineComponent({
|
||||
...globalSettings
|
||||
} = getMasterOptions();
|
||||
|
||||
const {
|
||||
name,
|
||||
settings: settingsFromProps = {},
|
||||
loader,
|
||||
lifeCycles,
|
||||
className,
|
||||
...propsFromParams
|
||||
} = props;
|
||||
|
||||
// 挂载节点
|
||||
const containerRef = ref(null);
|
||||
const microAppRef = ref();
|
||||
const updatingPromise = ref();
|
||||
const updatingTimestamp = ref(Date.now());
|
||||
const updatingPromiseRef = ref();
|
||||
const updatingTimestampRef = ref(Date.now());
|
||||
|
||||
const getAppConfig = () => {
|
||||
const appConfig = apps.find((app) => app.name === name);
|
||||
const appConfigRef = computed(() => {
|
||||
const appConfig = apps.find((app) => app.name === props.name);
|
||||
if (!appConfig) {
|
||||
throw new Error(
|
||||
`[@fesjs/plugin-qiankun]: Can not find the configuration of ${name} app!`
|
||||
`[@fesjs/plugin-qiankun]: Can not find the configuration of ${props.name} app!`
|
||||
);
|
||||
}
|
||||
return appConfig;
|
||||
};
|
||||
});
|
||||
|
||||
const propsFromConfigRef = computed(() => {
|
||||
const appConfig = appConfigRef.value;
|
||||
if (appConfig) {
|
||||
return appConfig.props;
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const propsFromParamsRef = computed(() => {
|
||||
const {
|
||||
name,
|
||||
settings,
|
||||
lifeCycles,
|
||||
className,
|
||||
...propsFromParams
|
||||
} = props;
|
||||
return propsFromParams || {};
|
||||
});
|
||||
|
||||
// 只有当name变化时才重新加载新的子应用
|
||||
const loadApp = () => {
|
||||
const appConfig = getAppConfig();
|
||||
const { name, entry, props: propsFromConfig = {} } = appConfig;
|
||||
const appConfig = appConfigRef.value;
|
||||
const { name, entry } = appConfig;
|
||||
// 加载新的
|
||||
microAppRef.value = loadMicroApp(
|
||||
{
|
||||
name: name,
|
||||
entry: entry,
|
||||
container: containerRef.value,
|
||||
props: { ...propsFromConfig, ...propsFromParams },
|
||||
props: {
|
||||
...propsFromConfigRef.value,
|
||||
...propsFromParamsRef.value,
|
||||
},
|
||||
},
|
||||
{
|
||||
...globalSettings,
|
||||
...settingsFromProps,
|
||||
...(props.settings || {}),
|
||||
},
|
||||
mergeWith({}, globalLifeCycles, lifeCycles, (v1, v2) =>
|
||||
mergeWith({}, globalLifeCycles || {}, props.lifeCycles || {}, (v1, v2) =>
|
||||
concat(v1 ?? [], v2 ?? [])
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// 当参数变化时,update子应用
|
||||
const updateApp = () => {
|
||||
const microApp = microAppRef.value;
|
||||
if (microApp) {
|
||||
if (!updatingPromiseRef.value) {
|
||||
// 初始化 updatingPromiseRef 为 microApp.mountPromise,从而确保后续更新是在应用 mount 完成之后
|
||||
updatingPromiseRef.value = microApp.mountPromise;
|
||||
} else {
|
||||
// 确保 microApp.update 调用是跟组件状态变更顺序一致的,且后一个微应用更新必须等待前一个更新完成
|
||||
updatingPromiseRef.value = updatingPromiseRef.value.then(
|
||||
() => {
|
||||
const canUpdate = (app) =>
|
||||
app?.update && app.getStatus() === "MOUNTED";
|
||||
if (canUpdate(microApp)) {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
if (
|
||||
Date.now() -
|
||||
updatingTimestampRef.value <
|
||||
200
|
||||
) {
|
||||
console.warn(
|
||||
`[@fesjs/plugin-qiankun] It seems like microApp ${props.name} is updating too many times in a short time(200ms), you may need to do some optimization to avoid the unnecessary re-rendering.`
|
||||
);
|
||||
}
|
||||
|
||||
console.info(
|
||||
`[@fesjs/plugin-qiankun] MicroApp ${props.name} is updating with props: `,
|
||||
props
|
||||
);
|
||||
updatingTimestampRef.value = Date.now();
|
||||
}
|
||||
|
||||
// 返回 microApp.update 形成链式调用
|
||||
return microApp.update({
|
||||
...propsFromConfigRef.value,
|
||||
...propsFromParamsRef.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadApp();
|
||||
});
|
||||
@ -85,54 +147,15 @@ export const MicroApp = defineComponent({
|
||||
unmountMicroApp(microAppRef.value);
|
||||
});
|
||||
|
||||
watch(props, () => {
|
||||
watch(appConfigRef, () => {
|
||||
unmountMicroApp(microAppRef.value);
|
||||
|
||||
loadApp();
|
||||
});
|
||||
|
||||
watch(microAppRef, () => {
|
||||
const microApp = microAppRef.value;
|
||||
const appConfig = getAppConfig();
|
||||
const { props: propsFromConfig = {} } = appConfig;
|
||||
if (microApp) {
|
||||
if (!updatingPromise.value) {
|
||||
// 初始化 updatingPromise 为 microApp.mountPromise,从而确保后续更新是在应用 mount 完成之后
|
||||
updatingPromise.value = microApp.mountPromise;
|
||||
} else {
|
||||
// 确保 microApp.update 调用是跟组件状态变更顺序一致的,且后一个微应用更新必须等待前一个更新完成
|
||||
updatingPromise.value = updatingPromise.value.then(() => {
|
||||
const canUpdate = (app) =>
|
||||
app?.update && app.getStatus() === "MOUNTED";
|
||||
if (canUpdate(microApp)) {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
if (
|
||||
Date.now() - updatingTimestamp.value <
|
||||
200
|
||||
) {
|
||||
console.warn(
|
||||
`[@fesjs/plugin-qiankun] It seems like microApp ${props.name} is updating too many times in a short time(200ms), you may need to do some optimization to avoid the unnecessary re-rendering.`
|
||||
);
|
||||
}
|
||||
|
||||
console.info(
|
||||
`[@fesjs/plugin-qiankun] MicroApp ${props.name} is updating with props: `,
|
||||
props
|
||||
);
|
||||
updatingTimestamp.value = Date.now();
|
||||
}
|
||||
|
||||
// 返回 microApp.update 形成链式调用
|
||||
return microApp.update({
|
||||
...propsFromConfig,
|
||||
...propsFromParams,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
watch([propsFromConfigRef, propsFromParamsRef], () => {
|
||||
updateApp();
|
||||
});
|
||||
|
||||
return () => <div ref={containerRef} className={className}></div>;
|
||||
return () => <div ref={containerRef} className={props.className}></div>;
|
||||
},
|
||||
});
|
||||
|
@ -0,0 +1,11 @@
|
||||
|
||||
import { reactive } from 'vue';
|
||||
|
||||
let initState;
|
||||
const setModelState = (val) => {
|
||||
initState = val;
|
||||
};
|
||||
|
||||
export default () => reactive(initState);
|
||||
|
||||
export { setModelState };
|
@ -3,6 +3,7 @@ import address from 'address';
|
||||
import { lodash } from '@umijs/utils';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { qiankunStateFromMainModelNamespace } from '../constants';
|
||||
|
||||
const namespace = 'plugin-qiankun/micro';
|
||||
|
||||
@ -15,6 +16,10 @@ export function isSlaveEnable(api) {
|
||||
}
|
||||
|
||||
export default function (api) {
|
||||
const {
|
||||
utils: { Mustache }
|
||||
} = api;
|
||||
|
||||
api.describe({
|
||||
enableBy: () => isSlaveEnable(api)
|
||||
});
|
||||
@ -84,11 +89,25 @@ export default function (api) {
|
||||
const absLifeclesPath = join(namespace, 'lifecycles.js');
|
||||
const absMicroOptionsPath = join(namespace, 'slaveOptions.js');
|
||||
const absPublicPath = join(namespace, 'publicPath.js');
|
||||
const absModelPath = join(namespace, 'qiankunModel.js');
|
||||
|
||||
// 更改public path
|
||||
api.addEntryImportsAhead(() => [{ source: `@@/${absPublicPath}` }]);
|
||||
|
||||
api.register({
|
||||
key: 'addExtraModels',
|
||||
fn: () => {
|
||||
const HAS_PLUGIN_MODEL = api.hasPlugins(['@fesjs/plugin-model']);
|
||||
return HAS_PLUGIN_MODEL ? [{
|
||||
absPath: `@@/${absModelPath}`,
|
||||
namespace: qiankunStateFromMainModelNamespace
|
||||
}] : [];
|
||||
}
|
||||
});
|
||||
|
||||
api.onGenerateFiles(() => {
|
||||
const HAS_PLUGIN_MODEL = api.hasPlugins(['@fesjs/plugin-model']);
|
||||
|
||||
api.writeTmpFile({
|
||||
path: absRuntimePath,
|
||||
content: readFileSync(
|
||||
@ -99,10 +118,12 @@ export default function (api) {
|
||||
|
||||
api.writeTmpFile({
|
||||
path: absLifeclesPath,
|
||||
content: readFileSync(
|
||||
content: Mustache.render(readFileSync(
|
||||
join(__dirname, 'runtime/lifecycles.tpl'),
|
||||
'utf-8'
|
||||
)
|
||||
), {
|
||||
HAS_PLUGIN_MODEL
|
||||
})
|
||||
});
|
||||
|
||||
api.writeTmpFile({
|
||||
@ -110,6 +131,7 @@ export default function (api) {
|
||||
content: `
|
||||
if (window.__POWERED_BY_QIANKUN__) {
|
||||
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
|
||||
window.public_path = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
|
||||
}
|
||||
`
|
||||
});
|
||||
@ -124,6 +146,13 @@ export default function (api) {
|
||||
export const setSlaveOptions = (newOpts) => options = ({ ...options, ...newOpts });
|
||||
`
|
||||
});
|
||||
|
||||
if (HAS_PLUGIN_MODEL) {
|
||||
api.writeTmpFile({
|
||||
path: absModelPath,
|
||||
content: readFileSync(join(__dirname, 'runtime/qiankunModel.tpl'), 'utf-8')
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
api.addEntryImports(() => ({
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { plugin, ApplyPluginsType } from '@@/core/coreExports';
|
||||
{{#HAS_PLUGIN_MODEL}}
|
||||
import { setModelState } from './qiankunModel';
|
||||
{{/HAS_PLUGIN_MODEL}}
|
||||
|
||||
const defer = {};
|
||||
defer.promise = new Promise((resolve) => {
|
||||
@ -47,6 +50,9 @@ export function genMount() {
|
||||
return async (props) => {
|
||||
// props 有值时说明应用是通过 lifecycle 被主应用唤醒的,而不是独立运行时自己 mount
|
||||
if (typeof props !== 'undefined') {
|
||||
{{#HAS_PLUGIN_MODEL}}
|
||||
setModelState(props);
|
||||
{{/HAS_PLUGIN_MODEL}}
|
||||
const slaveRuntime = getSlaveRuntime();
|
||||
if (slaveRuntime.mount) {
|
||||
await slaveRuntime.mount(props);
|
||||
@ -68,6 +74,9 @@ export function genMount() {
|
||||
|
||||
export function genUpdate() {
|
||||
return async (props) => {
|
||||
{{#HAS_PLUGIN_MODEL}}
|
||||
setModelState(props);
|
||||
{{/HAS_PLUGIN_MODEL}}
|
||||
const slaveRuntime = await getSlaveRuntime();
|
||||
if (slaveRuntime.update) {
|
||||
await slaveRuntime.update(props);
|
||||
|
@ -0,0 +1,11 @@
|
||||
|
||||
import { reactive } from 'vue';
|
||||
|
||||
let initState;
|
||||
const setModelState = (val) => {
|
||||
initState = val;
|
||||
};
|
||||
|
||||
export default () => reactive(initState);
|
||||
|
||||
export { setModelState };
|
Loading…
x
Reference in New Issue
Block a user