mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-04-06 03:59:53 +08:00
feat: plugin-qiankun完成子应用挂载逻辑、主应用提供<MicroApp />加载子应用
This commit is contained in:
parent
4484bbd8fa
commit
170877dcb9
@ -33,7 +33,7 @@
|
|||||||
"qiankun": "2.3.4"
|
"qiankun": "2.3.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@webank/fes": "^2.0.0-alpha.0",
|
"@webank/fes": "^2.0.0-rc.0",
|
||||||
"vue": "^3.0.5"
|
"vue": "^3.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
packages/fes-plugin-qiankun/src/constants.js
Normal file
2
packages/fes-plugin-qiankun/src/constants.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export const defaultMainRootId = '#root-master';
|
||||||
|
export const defaultHistoryType = 'hash';
|
@ -1,6 +1,6 @@
|
|||||||
import { join } from 'path';
|
// import { join } from 'path';
|
||||||
|
|
||||||
const namespace = 'plugin-qiankun';
|
// const namespace = 'plugin-qiankun';
|
||||||
|
|
||||||
export default (api) => {
|
export default (api) => {
|
||||||
api.describe({
|
api.describe({
|
||||||
@ -15,12 +15,14 @@ export default (api) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
api.addRuntimePluginKey(() => 'qiankun');
|
||||||
|
|
||||||
api.registerPlugins([
|
api.registerPlugins([
|
||||||
require.resolve('./main'),
|
require.resolve('./main'),
|
||||||
require.resolve('./mirco')
|
require.resolve('./mirco')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const absRuntimeFilePath = join(namespace, 'runtime.js');
|
// const absRuntimeFilePath = join(namespace, 'runtime.js');
|
||||||
|
|
||||||
api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
|
// api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
|
||||||
};
|
};
|
||||||
|
@ -1,141 +1,195 @@
|
|||||||
import { existsSync, readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import {
|
import { defaultMainRootId, defaultHistoryType } from '../constants';
|
||||||
defaultHistoryMode,
|
|
||||||
defaultMainRootId,
|
|
||||||
testPathWithPrefix,
|
|
||||||
toArray
|
|
||||||
} from '../common';
|
|
||||||
|
|
||||||
export default function (api, options) {
|
const namespace = 'plugin-qiankun/main';
|
||||||
const { registerRuntimeKeyInIndex = false } = options || {};
|
|
||||||
|
|
||||||
api.addRuntimePlugin(() => require.resolve('./runtime'));
|
export function isMasterEnable(api) {
|
||||||
|
return (
|
||||||
if (!registerRuntimeKeyInIndex) {
|
!!api.userConfig?.qiankun?.main
|
||||||
api.addRuntimePluginKey(() => 'qiankun');
|
|| !!process.env.INITIAL_QIANKUN_MAIN_OPTIONS
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function (api) {
|
||||||
|
api.describe({
|
||||||
|
enableBy: () => isMasterEnable(api)
|
||||||
|
});
|
||||||
|
|
||||||
api.modifyDefaultConfig(config => ({
|
api.modifyDefaultConfig(config => ({
|
||||||
...config,
|
...config,
|
||||||
mountElementId: defaultMainRootId,
|
mountElementId: defaultMainRootId
|
||||||
disableGlobalVariables: true
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// apps 可能在构建期为空
|
const absMicroAppPath = join(namespace, 'MicroApp.js');
|
||||||
const { apps = [] } = options || {};
|
const absRuntimePath = join(namespace, 'runtime.js');
|
||||||
if (apps.length) {
|
const absMasterOptionsPath = join(namespace, 'masterOptions.js');
|
||||||
// 获取一组路由中以 basePath 为前缀的路由
|
|
||||||
const findRouteWithPrefix = (routes, basePath) => {
|
|
||||||
for (const route of routes) {
|
|
||||||
if (route.path && testPathWithPrefix(basePath, route.path)) { return route; }
|
|
||||||
|
|
||||||
if (route.routes && route.routes.length) {
|
|
||||||
return findRouteWithPrefix(route.routes, basePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const modifyAppRoutes = () => {
|
|
||||||
// TODO: fes缺少修改路由API
|
|
||||||
api.modifyRoutes((routes) => {
|
|
||||||
const {
|
|
||||||
config: { history: mainHistory = defaultHistoryMode }
|
|
||||||
} = api;
|
|
||||||
|
|
||||||
const newRoutes = routes.map((route) => {
|
|
||||||
if (route.path === '/' && route.routes && route.routes.length) {
|
|
||||||
apps.forEach(({ history: slaveHistory = 'history', base }) => {
|
|
||||||
// 当子应用的 history mode 跟主应用一致时,为避免出现 404 手动为主应用创建一个 path 为 子应用 rule 的空 div 路由组件
|
|
||||||
if (slaveHistory === mainHistory) {
|
|
||||||
const baseConfig = toArray(base);
|
|
||||||
|
|
||||||
baseConfig.forEach((basePath) => {
|
|
||||||
const routeWithPrefix = findRouteWithPrefix(routes, basePath);
|
|
||||||
|
|
||||||
// 应用没有自己配置过 basePath 相关路由,则自动加入 mock 的路由
|
|
||||||
if (!routeWithPrefix) {
|
|
||||||
route.routes.unshift({
|
|
||||||
path: basePath,
|
|
||||||
exact: false,
|
|
||||||
component: `() => {
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
console.log('${basePath} 404 mock rendered');
|
|
||||||
}
|
|
||||||
|
|
||||||
return React.createElement('div');
|
|
||||||
}`
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 若用户已配置过跟应用 base 重名的路由,则强制将该路由 exact 设置为 false,目的是兼容之前遗留的错误用法的场景
|
|
||||||
routeWithPrefix.exact = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return route;
|
|
||||||
});
|
|
||||||
|
|
||||||
return newRoutes;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
modifyAppRoutes();
|
|
||||||
}
|
|
||||||
|
|
||||||
const rootExportsFile = join(api.paths.absSrcPath, 'rootExports.js');
|
|
||||||
|
|
||||||
api.addTmpGenerateWatcherPaths(() => rootExportsFile);
|
|
||||||
|
|
||||||
const namespace = 'plugin-qiankun';
|
|
||||||
const absCoreFilePath = join(namespace, 'qiankunDefer.js');
|
|
||||||
|
|
||||||
api.onGenerateFiles(() => {
|
api.onGenerateFiles(() => {
|
||||||
const {
|
|
||||||
config: { history = defaultHistoryMode }
|
|
||||||
} = api;
|
|
||||||
const rootExports = `window.g_rootExports = ${existsSync(rootExportsFile) ? 'require(\'@/rootExports\')' : '{}'};`.trim();
|
|
||||||
|
|
||||||
api.writeTmpFile({
|
api.writeTmpFile({
|
||||||
path: `${namespace}/qiankunRootExports.js`,
|
path: absMicroAppPath,
|
||||||
content: rootExports
|
content: readFileSync(join(__dirname, 'runtime/MicroApp.tpl'), 'utf-8')
|
||||||
});
|
});
|
||||||
|
|
||||||
api.writeTmpFile({
|
api.writeTmpFile({
|
||||||
path: `${namespace}/subAppsConfig.json`,
|
path: absRuntimePath,
|
||||||
content: JSON.stringify({
|
content: readFileSync(join(__dirname, 'runtime/runtime.tpl'), 'utf-8')
|
||||||
mainHistory: history,
|
|
||||||
...options
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const { main: options } = api.config?.qiankun || {};
|
||||||
|
const masterHistoryType = api.config?.router?.mode || defaultHistoryType;
|
||||||
|
const base = api.config.base || '/';
|
||||||
api.writeTmpFile({
|
api.writeTmpFile({
|
||||||
path: `${namespace}/qiankunDefer.js`,
|
path: absMasterOptionsPath,
|
||||||
content: `
|
content: `
|
||||||
class Deferred {
|
let options = ${JSON.stringify({
|
||||||
constructor() {
|
masterHistoryType,
|
||||||
this.promise = new Promise(resolve => this.resolve = resolve);
|
base,
|
||||||
}
|
...options
|
||||||
}
|
})};
|
||||||
export const deferred = new Deferred();
|
export const getMasterOptions = () => options;
|
||||||
export const qiankunStart = deferred.resolve;
|
export const setMasterOptions = (newOpts) => options = ({ ...options, ...newOpts });
|
||||||
`.trim()
|
`
|
||||||
});
|
|
||||||
|
|
||||||
api.writeTmpFile({
|
|
||||||
path: `${namespace}/runtime.js`,
|
|
||||||
content: readFileSync(join(__dirname, 'runtime.js'), 'utf-8')
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
api.addPluginExports(() => [
|
api.addPluginExports(() => [
|
||||||
{
|
{
|
||||||
specifiers: ['qiankunStart'],
|
specifiers: ['MicroApp'],
|
||||||
source: absCoreFilePath
|
source: absMicroAppPath
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
// const { registerRuntimeKeyInIndex = false } = options || {};
|
||||||
|
|
||||||
|
// api.addRuntimePlugin(() => require.resolve('./runtime'));
|
||||||
|
|
||||||
|
// if (!registerRuntimeKeyInIndex) {
|
||||||
|
// api.addRuntimePluginKey(() => 'qiankun');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// api.modifyDefaultConfig(config => ({
|
||||||
|
// ...config,
|
||||||
|
// mountElementId: defaultMainRootId,
|
||||||
|
// disableGlobalVariables: true
|
||||||
|
// }));
|
||||||
|
|
||||||
|
// // apps 可能在构建期为空
|
||||||
|
// const { apps = [] } = options || {};
|
||||||
|
// if (apps.length) {
|
||||||
|
// // 获取一组路由中以 basePath 为前缀的路由
|
||||||
|
// const findRouteWithPrefix = (routes, basePath) => {
|
||||||
|
// for (const route of routes) {
|
||||||
|
// if (route.path && testPathWithPrefix(basePath, route.path)) { return route; }
|
||||||
|
|
||||||
|
// if (route.routes && route.routes.length) {
|
||||||
|
// return findRouteWithPrefix(route.routes, basePath);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return null;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const modifyAppRoutes = () => {
|
||||||
|
// // TODO: fes缺少修改路由API
|
||||||
|
// api.modifyRoutes((routes) => {
|
||||||
|
// const {
|
||||||
|
// config: { history: mainHistory = defaultHistoryMode }
|
||||||
|
// } = api;
|
||||||
|
|
||||||
|
// const newRoutes = routes.map((route) => {
|
||||||
|
// if (route.path === '/' && route.routes && route.routes.length) {
|
||||||
|
// apps.forEach(({ history: slaveHistory = 'history', base }) => {
|
||||||
|
// // 当子应用的 history mode 跟主应用一致时,为避免出现 404 手动为主应用创建一个 path 为 子应用 rule 的空 div 路由组件
|
||||||
|
// if (slaveHistory === mainHistory) {
|
||||||
|
// const baseConfig = toArray(base);
|
||||||
|
|
||||||
|
// baseConfig.forEach((basePath) => {
|
||||||
|
// const routeWithPrefix = findRouteWithPrefix(routes, basePath);
|
||||||
|
|
||||||
|
// // 应用没有自己配置过 basePath 相关路由,则自动加入 mock 的路由
|
||||||
|
// if (!routeWithPrefix) {
|
||||||
|
// route.routes.unshift({
|
||||||
|
// path: basePath,
|
||||||
|
// exact: false,
|
||||||
|
// component: `() => {
|
||||||
|
// if (process.env.NODE_ENV === 'development') {
|
||||||
|
// console.log('${basePath} 404 mock rendered');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return React.createElement('div');
|
||||||
|
// }`
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// // 若用户已配置过跟应用 base 重名的路由,则强制将该路由 exact 设置为 false,目的是兼容之前遗留的错误用法的场景
|
||||||
|
// routeWithPrefix.exact = false;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return route;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return newRoutes;
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// modifyAppRoutes();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const rootExportsFile = join(api.paths.absSrcPath, 'rootExports.js');
|
||||||
|
|
||||||
|
// api.addTmpGenerateWatcherPaths(() => rootExportsFile);
|
||||||
|
|
||||||
|
// const namespace = 'plugin-qiankun';
|
||||||
|
// const absCoreFilePath = join(namespace, 'qiankunDefer.js');
|
||||||
|
|
||||||
|
// api.onGenerateFiles(() => {
|
||||||
|
// const {
|
||||||
|
// config: { history = defaultHistoryMode }
|
||||||
|
// } = api;
|
||||||
|
// const rootExports = `window.g_rootExports = ${existsSync(rootExportsFile) ? 'require(\'@/rootExports\')' : '{}'};`.trim();
|
||||||
|
|
||||||
|
// api.writeTmpFile({
|
||||||
|
// path: `${namespace}/qiankunRootExports.js`,
|
||||||
|
// content: rootExports
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.writeTmpFile({
|
||||||
|
// path: `${namespace}/subAppsConfig.json`,
|
||||||
|
// content: JSON.stringify({
|
||||||
|
// mainHistory: history,
|
||||||
|
// ...options
|
||||||
|
// })
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.writeTmpFile({
|
||||||
|
// path: `${namespace}/qiankunDefer.js`,
|
||||||
|
// content: `
|
||||||
|
// class Deferred {
|
||||||
|
// constructor() {
|
||||||
|
// this.promise = new Promise(resolve => this.resolve = resolve);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// export const deferred = new Deferred();
|
||||||
|
// export const qiankunStart = deferred.resolve;
|
||||||
|
// `.trim()
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.writeTmpFile({
|
||||||
|
// path: `${namespace}/runtime.js`,
|
||||||
|
// content: readFileSync(join(__dirname, 'runtime.js'), 'utf-8')
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.addPluginExports(() => [
|
||||||
|
// {
|
||||||
|
// specifiers: ['qiankunStart'],
|
||||||
|
// source: absCoreFilePath
|
||||||
|
// }
|
||||||
|
// ]);
|
||||||
}
|
}
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
import { deferred } from '@@/plugin-qiankun/qiankunDefer.js';
|
|
||||||
import '@@/plugin-qiankun/qiankunRootExports.js';
|
|
||||||
import subAppConfig from '@@/plugin-qiankun/subAppsConfig.json';
|
|
||||||
import { registerMicroApps, start } from 'qiankun';
|
|
||||||
import { createApp, h } from 'vue';
|
|
||||||
import { plugin, ApplyPluginsType } from '@@/core/coreExports';
|
|
||||||
import { defaultMountContainerId, testPathWithPrefix, toArray } from '../common';
|
|
||||||
|
|
||||||
async function getMasterRuntime() {
|
|
||||||
const config = plugin.applyPlugins({
|
|
||||||
key: 'qiankun',
|
|
||||||
type: ApplyPluginsType.modify,
|
|
||||||
initialValue: {},
|
|
||||||
async: true
|
|
||||||
});
|
|
||||||
const { master } = config;
|
|
||||||
return master || config;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function render(oldRender) {
|
|
||||||
oldRender();
|
|
||||||
function isAppActive(location, history, base) {
|
|
||||||
const baseConfig = toArray(base);
|
|
||||||
switch (history.type || history) {
|
|
||||||
case 'hash':
|
|
||||||
return baseConfig.some(pathPrefix => testPathWithPrefix(`#${pathPrefix}`, location.hash));
|
|
||||||
case 'browser':
|
|
||||||
return baseConfig.some(pathPrefix => testPathWithPrefix(pathPrefix, location.pathname));
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const runtimeConfig = await getMasterRuntime();
|
|
||||||
const {
|
|
||||||
apps, jsSandbox = false, prefetch = true, defer = false, lifeCycles, masterHistory, ...otherConfigs
|
|
||||||
} = {
|
|
||||||
...subAppConfig,
|
|
||||||
...runtimeConfig
|
|
||||||
};
|
|
||||||
|
|
||||||
assert(apps && apps.length, 'sub apps must be config when using fes-plugin-qiankun');
|
|
||||||
|
|
||||||
registerMicroApps(apps.map(({
|
|
||||||
name, entry, base, history = masterHistory, mountElementId = defaultMountContainerId, props
|
|
||||||
}) => ({
|
|
||||||
name,
|
|
||||||
entry,
|
|
||||||
activeRule: location => isAppActive(location, history, base),
|
|
||||||
render: ({ appContent, loading }) => {
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
console.info(`app ${name} loading ${loading}`);
|
|
||||||
}
|
|
||||||
if (mountElementId) {
|
|
||||||
const container = document.getElementById(mountElementId);
|
|
||||||
if (container) {
|
|
||||||
const subApp = {
|
|
||||||
setup() {
|
|
||||||
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
h('div', {
|
|
||||||
dangerouslySetInnerHTML: {
|
|
||||||
__html: appContent
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const app = createApp();
|
|
||||||
app.mount(subApp, container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
base,
|
|
||||||
history,
|
|
||||||
...props
|
|
||||||
}
|
|
||||||
})), lifeCycles);
|
|
||||||
|
|
||||||
if (defer) {
|
|
||||||
await deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
start({ jsSandbox, prefetch, ...otherConfigs });
|
|
||||||
}
|
|
123
packages/fes-plugin-qiankun/src/main/runtime/MicroApp.tpl
Normal file
123
packages/fes-plugin-qiankun/src/main/runtime/MicroApp.tpl
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import {
|
||||||
|
defineComponent,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
computed,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onMounted
|
||||||
|
} from 'vue';
|
||||||
|
import { loadMicroApp } from 'qiankun';
|
||||||
|
// eslint-disable-next-line import/extensions
|
||||||
|
import { getMasterOptions } from './masterOptions';
|
||||||
|
|
||||||
|
function unmountMicroApp(microApp) {
|
||||||
|
if (microApp) {
|
||||||
|
microApp.mountPromise.then(() => microApp.unmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MicroApp = defineComponent({
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const {
|
||||||
|
masterHistoryType,
|
||||||
|
apps = [],
|
||||||
|
lifeCycles: globalLifeCycles,
|
||||||
|
...globalSettings
|
||||||
|
} = getMasterOptions();
|
||||||
|
|
||||||
|
const containerRef = ref(null);
|
||||||
|
const microAppRef = ref();
|
||||||
|
const updatingPromise = ref();
|
||||||
|
const updatingTimestamp = ref(Date.now());
|
||||||
|
|
||||||
|
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 ${props.name} app!`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _appConfig;
|
||||||
|
});
|
||||||
|
|
||||||
|
const loadApp = () => {
|
||||||
|
const appConfig = appConfigRef.value;
|
||||||
|
// 加载新的
|
||||||
|
microAppRef.value = loadMicroApp(
|
||||||
|
{
|
||||||
|
name: appConfig.name,
|
||||||
|
entry: appConfig.entry,
|
||||||
|
container: containerRef.value,
|
||||||
|
props: { ...appConfig.props }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...globalSettings,
|
||||||
|
...(props.settings || {}),
|
||||||
|
globalLifeCycles,
|
||||||
|
lifeCycles: props.lifeCycles
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadApp();
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
unmountMicroApp(microAppRef.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(appConfigRef, () => {
|
||||||
|
unmountMicroApp(microAppRef.value);
|
||||||
|
|
||||||
|
loadApp();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(microAppRef, () => {
|
||||||
|
const microApp = microAppRef.value;
|
||||||
|
const appConfig = appConfigRef.value;
|
||||||
|
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(
|
||||||
|
`[@umijs/plugin-qiankun] MicroApp ${props.name} is updating with props: `,
|
||||||
|
props
|
||||||
|
);
|
||||||
|
updatingTimestamp.value = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回 microApp.update 形成链式调用
|
||||||
|
return microApp.update({
|
||||||
|
...appConfig.props
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => <div ref={containerRef} className={props.className}></div>;
|
||||||
|
}
|
||||||
|
});
|
85
packages/fes-plugin-qiankun/src/main/runtime/runtime.tpl
Normal file
85
packages/fes-plugin-qiankun/src/main/runtime/runtime.tpl
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// import { deferred } from '@@/plugin-qiankun/qiankunDefer.js';
|
||||||
|
// import '@@/plugin-qiankun/qiankunRootExports.js';
|
||||||
|
// import subAppConfig from '@@/plugin-qiankun/subAppsConfig.json';
|
||||||
|
// import { registerMicroApps, start } from 'qiankun';
|
||||||
|
// import { createApp, h } from 'vue';
|
||||||
|
// import { plugin, ApplyPluginsType } from '@@/core/coreExports';
|
||||||
|
// import { defaultMountContainerId, testPathWithPrefix, toArray } from '../common';
|
||||||
|
|
||||||
|
// async function getMasterRuntime() {
|
||||||
|
// const config = plugin.applyPlugins({
|
||||||
|
// key: 'qiankun',
|
||||||
|
// type: ApplyPluginsType.modify,
|
||||||
|
// initialValue: {},
|
||||||
|
// async: true
|
||||||
|
// });
|
||||||
|
// const { master } = config;
|
||||||
|
// return master || config;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export async function render(oldRender) {
|
||||||
|
// oldRender();
|
||||||
|
// function isAppActive(location, history, base) {
|
||||||
|
// const baseConfig = toArray(base);
|
||||||
|
// switch (history.type || history) {
|
||||||
|
// case 'hash':
|
||||||
|
// return baseConfig.some(pathPrefix => testPathWithPrefix(`#${pathPrefix}`, location.hash));
|
||||||
|
// case 'browser':
|
||||||
|
// return baseConfig.some(pathPrefix => testPathWithPrefix(pathPrefix, location.pathname));
|
||||||
|
// default:
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// const runtimeConfig = await getMasterRuntime();
|
||||||
|
// const {
|
||||||
|
// apps, jsSandbox = false, prefetch = true, defer = false, lifeCycles, masterHistory, ...otherConfigs
|
||||||
|
// } = {
|
||||||
|
// ...subAppConfig,
|
||||||
|
// ...runtimeConfig
|
||||||
|
// };
|
||||||
|
|
||||||
|
// assert(apps && apps.length, 'sub apps must be config when using fes-plugin-qiankun');
|
||||||
|
|
||||||
|
// registerMicroApps(apps.map(({
|
||||||
|
// name, entry, base, history = masterHistory, mountElementId = defaultMountContainerId, props
|
||||||
|
// }) => ({
|
||||||
|
// name,
|
||||||
|
// entry,
|
||||||
|
// activeRule: location => isAppActive(location, history, base),
|
||||||
|
// render: ({ appContent, loading }) => {
|
||||||
|
// if (process.env.NODE_ENV === 'development') {
|
||||||
|
// console.info(`app ${name} loading ${loading}`);
|
||||||
|
// }
|
||||||
|
// if (mountElementId) {
|
||||||
|
// const container = document.getElementById(mountElementId);
|
||||||
|
// if (container) {
|
||||||
|
// const subApp = {
|
||||||
|
// setup() {
|
||||||
|
|
||||||
|
// },
|
||||||
|
// render() {
|
||||||
|
// h('div', {
|
||||||
|
// dangerouslySetInnerHTML: {
|
||||||
|
// __html: appContent
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// const app = createApp();
|
||||||
|
// app.mount(subApp, container);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// props: {
|
||||||
|
// base,
|
||||||
|
// history,
|
||||||
|
// ...props
|
||||||
|
// }
|
||||||
|
// })), lifeCycles);
|
||||||
|
|
||||||
|
// if (defer) {
|
||||||
|
// await deferred.promise;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// start({ jsSandbox, prefetch, ...otherConfigs });
|
||||||
|
// }
|
@ -1,103 +1,187 @@
|
|||||||
|
import assert from 'assert';
|
||||||
|
import { lodash } from '@umijs/utils';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { defaultMircoRootId } from '../common';
|
// import { defaultMircoRootId } from '../common';
|
||||||
|
|
||||||
export default function (api, options) {
|
const namespace = 'plugin-qiankun/mirco';
|
||||||
const { registerRuntimeKeyInIndex = false } = options || {};
|
|
||||||
|
|
||||||
api.addRuntimePlugin(() => require.resolve('./runtime'));
|
export function isSlaveEnable(api) {
|
||||||
|
return (
|
||||||
if (!registerRuntimeKeyInIndex) {
|
!!api.userConfig?.qiankun?.mirco
|
||||||
api.addRuntimePluginKey(() => 'qiankun');
|
|| lodash.isEqual(api.userConfig?.qiankun, {})
|
||||||
|
|| !!process.env.INITIAL_QIANKUN_MIRCO_OPTIONS
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lifecyclePath = require.resolve('./lifecycles');
|
export default function (api) {
|
||||||
const { name: pkgName } = require(join(api.cwd, 'package.json'));
|
api.describe({
|
||||||
|
enableBy: () => isSlaveEnable(api)
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: fes缺少修改默认配置API
|
api.modifyDefaultConfig(memo => ({
|
||||||
api.modifyDefaultConfig(memo => (Object.assign(Object.assign({}, memo), {
|
...memo,
|
||||||
disableGlobalVariables: true,
|
|
||||||
base: `/${pkgName}`,
|
|
||||||
mountElementId: defaultMircoRootId,
|
|
||||||
// 默认开启 runtimePublicPath,避免出现 dynamic import 场景子应用资源地址出问题
|
|
||||||
runtimePublicPath: true
|
runtimePublicPath: true
|
||||||
})));
|
}));
|
||||||
|
|
||||||
if (api.service.userConfig.runtimePublicPath !== false) {
|
api.modifyPublicPathStr((publicPathStr) => {
|
||||||
// TODO: fes缺少修改 publicPath API
|
const { runtimePublicPath } = api.config;
|
||||||
api.modifyPublicPathStr(() => `window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ || "${
|
const qiankunConfig = api.config.qiankun || {};
|
||||||
// 开发阶段 publicPath 配置无效,默认为 /
|
if (!qiankunConfig || !qiankunConfig.mirco) {
|
||||||
process.env.NODE_ENV !== 'development'
|
return publicPathStr;
|
||||||
? api.config.publicPath || '/'
|
|
||||||
: '/'}"`);
|
|
||||||
}
|
}
|
||||||
|
const { shouldNotModifyRuntimePublicPath } = qiankunConfig;
|
||||||
|
|
||||||
|
if (runtimePublicPath === true && !shouldNotModifyRuntimePublicPath) {
|
||||||
|
return `window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ || "${
|
||||||
|
api.config.publicPath || '/'
|
||||||
|
}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicPathStr;
|
||||||
|
});
|
||||||
|
|
||||||
api.chainWebpack((config) => {
|
api.chainWebpack((config) => {
|
||||||
assert(api.pkg.name, 'You should have name in package.json');
|
assert(api.pkg.name, 'You should have name in package.json');
|
||||||
config.output
|
config.output.libraryTarget('umd').library(`${api.pkg.name}-[name]`);
|
||||||
.libraryTarget('umd')
|
return config;
|
||||||
.library(`${api.pkg.name}-[name]`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// bundle 添加 entry 标记
|
const absRuntimePath = join(namespace, 'runtime.js');
|
||||||
// TODO: fes缺少修改HTML API
|
const absLifeclesPath = join(namespace, 'lifecycles.js');
|
||||||
api.modifyHTML(($) => {
|
|
||||||
$('script').each((_, el) => {
|
|
||||||
const scriptEl = $(el);
|
|
||||||
const umiEntryJs = /\/?umi(\.\w+)?\.js$/g;
|
|
||||||
const scriptElSrc = scriptEl.attr('src');
|
|
||||||
|
|
||||||
if (umiEntryJs.test((scriptElSrc) !== null && scriptElSrc !== 0 ? scriptElSrc : '')) {
|
|
||||||
scriptEl.attr('entry', '');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return $;
|
|
||||||
});
|
|
||||||
|
|
||||||
const namespace = 'plugin-qiankun';
|
|
||||||
|
|
||||||
api.onGenerateFiles(() => {
|
api.onGenerateFiles(() => {
|
||||||
api.writeTmpFile({
|
api.writeTmpFile({
|
||||||
path: `${namespace}/qiankunContext.js`,
|
path: absRuntimePath,
|
||||||
content: `
|
content: readFileSync(join(__dirname, 'runtime/runtime.tpl'), 'utf-8')
|
||||||
import { createApp, h } from 'vue';
|
|
||||||
export const Context = createContext(null);
|
|
||||||
export function useRootExports() {
|
|
||||||
return useContext(Context);
|
|
||||||
};
|
|
||||||
`.trim()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
api.writeTmpFile({
|
api.writeTmpFile({
|
||||||
path: `${namespace}/runtime.js`,
|
path: absLifeclesPath,
|
||||||
content: readFileSync(join(__dirname, 'runtime.js'), 'utf-8')
|
content: readFileSync(join(__dirname, 'runtime/lifecycles.tpl'), 'utf-8')
|
||||||
});
|
|
||||||
|
|
||||||
api.writeTmpFile({
|
|
||||||
path: `${namespace}/lifecycles.js`,
|
|
||||||
content: readFileSync(join(__dirname, 'lifecycles.js'), 'utf-8')
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
api.addPluginExports(() => [
|
|
||||||
{
|
|
||||||
specifiers: ['useRootExports'],
|
|
||||||
source: `${namespace}/qiankunContext.js`
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
api.addEntryImports(() => ({
|
api.addEntryImports(() => ({
|
||||||
source: lifecyclePath,
|
source: `@@/${absLifeclesPath}`,
|
||||||
specifier: '{ genMount as qiankun_genMount, genBootstrap as qiankun_genBootstrap, genUnmount as qiankun_genUnmount }'
|
specifier:
|
||||||
|
'{ genMount as qiankun_genMount, genBootstrap as qiankun_genBootstrap, genUnmount as qiankun_genUnmount, genUpdate as qiankun_genUpdate }'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
api.addEntryCode(() => `
|
api.addEntryCode(
|
||||||
export const bootstrap = qiankun_genBootstrap(Promise.resolve(), clientRender);
|
() => `
|
||||||
export const mount = qiankun_genMount();
|
export const bootstrap = qiankun_genBootstrap(completeClientRender, app);
|
||||||
|
export const mount = qiankun_genMount('${api.config.mountElementId}');
|
||||||
export const unmount = qiankun_genUnmount('${api.config.mountElementId}');
|
export const unmount = qiankun_genUnmount('${api.config.mountElementId}');
|
||||||
|
export const update = qiankun_genUpdate();
|
||||||
|
|
||||||
if (!window.__POWERED_BY_QIANKUN__) {
|
if (!window.__POWERED_BY_QIANKUN__) {
|
||||||
bootstrap().then(mount);
|
bootstrap().then(mount);
|
||||||
}
|
}
|
||||||
`);
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
// const { registerRuntimeKeyInIndex = false } = options || {};
|
||||||
|
|
||||||
|
// api.addRuntimePlugin(() => require.resolve('./runtime'));
|
||||||
|
|
||||||
|
// // if (!registerRuntimeKeyInIndex) {
|
||||||
|
// // api.addRuntimePluginKey(() => 'qiankun');
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// const lifecyclePath = require.resolve('./lifecycles');
|
||||||
|
// const { name: pkgName } = require(join(api.cwd, 'package.json'));
|
||||||
|
|
||||||
|
// // TODO: fes缺少修改默认配置API
|
||||||
|
// api.modifyDefaultConfig(memo => Object.assign(Object.assign({}, memo), {
|
||||||
|
// disableGlobalVariables: true,
|
||||||
|
// base: `/${pkgName}`,
|
||||||
|
// mountElementId: defaultMircoRootId,
|
||||||
|
// // 默认开启 runtimePublicPath,避免出现 dynamic import 场景子应用资源地址出问题
|
||||||
|
// runtimePublicPath: true
|
||||||
|
// }));
|
||||||
|
|
||||||
|
// if (api.service.userConfig.runtimePublicPath !== false) {
|
||||||
|
// // TODO: fes缺少修改 publicPath API
|
||||||
|
// api.modifyPublicPathStr(
|
||||||
|
// () => `window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ || "${
|
||||||
|
// // 开发阶段 publicPath 配置无效,默认为 /
|
||||||
|
// process.env.NODE_ENV !== 'development'
|
||||||
|
// ? api.config.publicPath || '/'
|
||||||
|
// : '/'
|
||||||
|
// }"`
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// api.chainWebpack((config) => {
|
||||||
|
// assert(api.pkg.name, 'You should have name in package.json');
|
||||||
|
// config.output.libraryTarget('umd').library(`${api.pkg.name}-[name]`);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // bundle 添加 entry 标记
|
||||||
|
// // TODO: fes缺少修改HTML API
|
||||||
|
// api.modifyHTML(($) => {
|
||||||
|
// $('script').each((_, el) => {
|
||||||
|
// const scriptEl = $(el);
|
||||||
|
// const umiEntryJs = /\/?umi(\.\w+)?\.js$/g;
|
||||||
|
// const scriptElSrc = scriptEl.attr('src');
|
||||||
|
|
||||||
|
// if (
|
||||||
|
// umiEntryJs.test(
|
||||||
|
// scriptElSrc !== null && scriptElSrc !== 0 ? scriptElSrc : ''
|
||||||
|
// )
|
||||||
|
// ) {
|
||||||
|
// scriptEl.attr('entry', '');
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// return $;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.onGenerateFiles(() => {
|
||||||
|
// api.writeTmpFile({
|
||||||
|
// path: `${namespace}/qiankunContext.js`,
|
||||||
|
// content: `
|
||||||
|
// import { createApp, h } from 'vue';
|
||||||
|
// export const Context = createContext(null);
|
||||||
|
// export function useRootExports() {
|
||||||
|
// return useContext(Context);
|
||||||
|
// };
|
||||||
|
// `.trim()
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.writeTmpFile({
|
||||||
|
// path: `${namespace}/runtime.js`,
|
||||||
|
// content: readFileSync(join(__dirname, 'runtime.js'), 'utf-8')
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.writeTmpFile({
|
||||||
|
// path: `${namespace}/lifecycles.js`,
|
||||||
|
// content: readFileSync(join(__dirname, 'lifecycles.js'), 'utf-8')
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// api.addPluginExports(() => [
|
||||||
|
// {
|
||||||
|
// specifiers: ['useRootExports'],
|
||||||
|
// source: `${namespace}/qiankunContext.js`
|
||||||
|
// }
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// api.addEntryImports(() => ({
|
||||||
|
// source: lifecyclePath,
|
||||||
|
// specifier:
|
||||||
|
// '{ genMount as qiankun_genMount, genBootstrap as qiankun_genBootstrap, genUnmount as qiankun_genUnmount }'
|
||||||
|
// }));
|
||||||
|
|
||||||
|
// api.addEntryCode(
|
||||||
|
// () => `
|
||||||
|
// export const bootstrap = qiankun_genBootstrap(Promise.resolve(), clientRender);
|
||||||
|
// export const mount = qiankun_genMount();
|
||||||
|
// export const unmount = qiankun_genUnmount('${api.config.mountElementId}');
|
||||||
|
|
||||||
|
// if (!window.__POWERED_BY_QIANKUN__) {
|
||||||
|
// bootstrap().then(mount);
|
||||||
|
// }
|
||||||
|
// `
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
import { plugin, ApplyPluginsType } from '@@/core/coreExports';
|
|
||||||
|
|
||||||
const defer = {};
|
|
||||||
defer.promise = new Promise((resolve) => {
|
|
||||||
defer.resolve = resolve;
|
|
||||||
});
|
|
||||||
|
|
||||||
let render = () => { };
|
|
||||||
let hasMountedAtLeastOnce = false;
|
|
||||||
|
|
||||||
export default () => defer.promise;
|
|
||||||
|
|
||||||
function getSlaveRuntime() {
|
|
||||||
const config = plugin.applyPlugins({
|
|
||||||
key: 'qiankun',
|
|
||||||
type: ApplyPluginsType.modify,
|
|
||||||
initialValue: {}
|
|
||||||
});
|
|
||||||
const { slave } = config;
|
|
||||||
return slave || config;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 子应用生命周期钩子Bootstrap
|
|
||||||
export function genBootstrap(promise, oldRender) {
|
|
||||||
return async (...args) => {
|
|
||||||
const slaveRuntime = getSlaveRuntime();
|
|
||||||
if (slaveRuntime.bootstrap) { await slaveRuntime.bootstrap(...args); }
|
|
||||||
render = () => promise.then(oldRender).catch((e) => {
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
console.error('Render failed', e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 子应用生命周期钩子Mount
|
|
||||||
export function genMount() {
|
|
||||||
return async (...args) => {
|
|
||||||
defer.resolve();
|
|
||||||
const slaveRuntime = getSlaveRuntime();
|
|
||||||
if (slaveRuntime.mount) { await slaveRuntime.mount(...args); }
|
|
||||||
// 第一次 mount 会自动触发 render,非第一次 mount 则需手动触发
|
|
||||||
if (hasMountedAtLeastOnce) {
|
|
||||||
render();
|
|
||||||
}
|
|
||||||
hasMountedAtLeastOnce = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 子应用生命周期钩子Unmount
|
|
||||||
export function genUnmount(mountElementId, app) {
|
|
||||||
return async (...args) => {
|
|
||||||
const container = document.getElementById(mountElementId);
|
|
||||||
if (container) {
|
|
||||||
app.unmount(container);
|
|
||||||
}
|
|
||||||
const slaveRuntime = getSlaveRuntime();
|
|
||||||
if (slaveRuntime.unmount) { await slaveRuntime.unmount(...args); }
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
import { h } from 'vue';
|
|
||||||
import qiankunRender from './lifecycles';
|
|
||||||
|
|
||||||
export function rootContainer(container) {
|
|
||||||
const value = window.g_rootExports;
|
|
||||||
const { Context } = require('@@/plugin-qiankun/qiankunContext');
|
|
||||||
return h(Context.Provider, { value }, container);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const render = oldRender => qiankunRender().then(() => {
|
|
||||||
oldRender();
|
|
||||||
});
|
|
92
packages/fes-plugin-qiankun/src/mirco/runtime/lifecycles.tpl
Normal file
92
packages/fes-plugin-qiankun/src/mirco/runtime/lifecycles.tpl
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { plugin, ApplyPluginsType } from '@@/core/coreExports';
|
||||||
|
|
||||||
|
const defer = {};
|
||||||
|
defer.promise = new Promise((resolve) => {
|
||||||
|
defer.resolve = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
|
function isPromise(obj) {
|
||||||
|
return !!obj // 有实际含义的变量才执行方法,变量null,undefined和''空串都为false
|
||||||
|
&& (typeof obj === 'object' || typeof obj === 'function') // 初始promise 或 promise.then返回的
|
||||||
|
&& typeof obj.then === 'function';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let render = () => {};
|
||||||
|
let app = null;
|
||||||
|
let hasMountedAtLeastOnce = false;
|
||||||
|
|
||||||
|
export default () => defer.promise;
|
||||||
|
|
||||||
|
function getSlaveRuntime() {
|
||||||
|
const config = plugin.applyPlugins({
|
||||||
|
key: 'qiankun',
|
||||||
|
type: ApplyPluginsType.modify,
|
||||||
|
initialValue: {}
|
||||||
|
});
|
||||||
|
const { slave } = config;
|
||||||
|
return slave || config;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子应用生命周期钩子Bootstrap
|
||||||
|
export function genBootstrap(oldRender, appPromise) {
|
||||||
|
return async (props) => {
|
||||||
|
const slaveRuntime = getSlaveRuntime();
|
||||||
|
if (slaveRuntime.bootstrap) {
|
||||||
|
await slaveRuntime.bootstrap(props);
|
||||||
|
}
|
||||||
|
render = oldRender;
|
||||||
|
if (isPromise(appPromise)) {
|
||||||
|
app = await appPromise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子应用生命周期钩子Mount
|
||||||
|
export function genMount() {
|
||||||
|
return async (props) => {
|
||||||
|
// props 有值时说明应用是通过 lifecycle 被主应用唤醒的,而不是独立运行时自己 mount
|
||||||
|
if (typeof props !== 'undefined') {
|
||||||
|
const slaveRuntime = getSlaveRuntime();
|
||||||
|
if (slaveRuntime.mount) {
|
||||||
|
await slaveRuntime.mount(props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第一次 mount 会自动触发 render,非第一次 mount 则需手动触发
|
||||||
|
if (hasMountedAtLeastOnce) {
|
||||||
|
const appPromise = render();
|
||||||
|
if (isPromise(appPromise)) {
|
||||||
|
app = await appPromise;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defer.resolve();
|
||||||
|
}
|
||||||
|
hasMountedAtLeastOnce = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genUpdate() {
|
||||||
|
return async (props) => {
|
||||||
|
const slaveRuntime = await getSlaveRuntime();
|
||||||
|
if (slaveRuntime.update) {
|
||||||
|
await slaveRuntime.update(props);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子应用生命周期钩子Unmount
|
||||||
|
export function genUnmount(mountElementId) {
|
||||||
|
return async (props) => {
|
||||||
|
const container = props?.container
|
||||||
|
? props.container.querySelector(`#${mountElementId}`)
|
||||||
|
: document.getElementById(mountElementId);
|
||||||
|
if (container && app) {
|
||||||
|
app.unmount(container);
|
||||||
|
}
|
||||||
|
const slaveRuntime = getSlaveRuntime();
|
||||||
|
if (slaveRuntime.unmount) {
|
||||||
|
await slaveRuntime.unmount(props);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
12
packages/fes-plugin-qiankun/src/mirco/runtime/runtime.tpl
Normal file
12
packages/fes-plugin-qiankun/src/mirco/runtime/runtime.tpl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// import { h } from 'vue';
|
||||||
|
// import qiankunRender from './lifecycles';
|
||||||
|
|
||||||
|
// export function rootContainer(container) {
|
||||||
|
// const value = typeof window !== 'undefined' ? window.g_rootExports : {};
|
||||||
|
// const { Context } = require('@@/plugin-qiankun/qiankunContext');
|
||||||
|
// return h(Context.Provider, { value }, container);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export const render = oldRender => qiankunRender().then(() => {
|
||||||
|
// oldRender();
|
||||||
|
// });
|
@ -55,7 +55,16 @@ export async function getBundleAndConfigs({
|
|||||||
type: api.ApplyPluginsType.add,
|
type: api.ApplyPluginsType.add,
|
||||||
initialState: []
|
initialState: []
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
publicPath: await api.applyPlugins({
|
||||||
|
key: 'modifyPublicPathStr',
|
||||||
|
type: api.ApplyPluginsType.modify,
|
||||||
|
initialValue: api.config.publicPath || '',
|
||||||
|
args: {
|
||||||
|
// route: args.route
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,8 @@ export default async function getConfig({
|
|||||||
modifyBabelOpts,
|
modifyBabelOpts,
|
||||||
modifyBabelPresetOpts,
|
modifyBabelPresetOpts,
|
||||||
chainWebpack,
|
chainWebpack,
|
||||||
headScripts
|
headScripts,
|
||||||
|
publicPath
|
||||||
}) {
|
}) {
|
||||||
const isDev = env === 'development';
|
const isDev = env === 'development';
|
||||||
const isProd = env === 'production';
|
const isProd = env === 'production';
|
||||||
@ -93,7 +94,7 @@ export default async function getConfig({
|
|||||||
// --------------- output -----------
|
// --------------- output -----------
|
||||||
webpackConfig.output
|
webpackConfig.output
|
||||||
.path(absoluteOutput)
|
.path(absoluteOutput)
|
||||||
.publicPath(config.publicPath || '')
|
.publicPath(publicPath)
|
||||||
.filename('[name].[contenthash:8].js')
|
.filename('[name].[contenthash:8].js')
|
||||||
.chunkFilename('[name].[contenthash:8].chunk.js');
|
.chunkFilename('[name].[contenthash:8].chunk.js');
|
||||||
|
|
||||||
|
@ -62,8 +62,7 @@ const getClientRender = (args = {}) => plugin.applyPlugins({
|
|||||||
args,
|
args,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const beforeRender = async () => {
|
||||||
|
|
||||||
const beforeRenderConfig = plugin.applyPlugins({
|
const beforeRenderConfig = plugin.applyPlugins({
|
||||||
key: "beforeRender",
|
key: "beforeRender",
|
||||||
type: ApplyPluginsType.modify,
|
type: ApplyPluginsType.modify,
|
||||||
@ -72,8 +71,6 @@ const beforeRenderConfig = plugin.applyPlugins({
|
|||||||
action: null
|
action: null
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const beforeRender = async () => {
|
|
||||||
let initialState = {};
|
let initialState = {};
|
||||||
if (typeof beforeRenderConfig.action === "function") {
|
if (typeof beforeRenderConfig.action === "function") {
|
||||||
const app = createApp(beforeRenderConfig.loading);
|
const app = createApp(beforeRenderConfig.loading);
|
||||||
@ -89,13 +86,16 @@ const beforeRender = async () => {
|
|||||||
return initialState;
|
return initialState;
|
||||||
};
|
};
|
||||||
|
|
||||||
const render = async () => {
|
const completeClientRender = async () => {
|
||||||
const initialState = await beforeRender();
|
const initialState = await beforeRender();
|
||||||
const clientRender = getClientRender({initialState});
|
const clientRender = getClientRender({initialState});
|
||||||
clientRender();
|
const app = clientRender();
|
||||||
|
return app;
|
||||||
};
|
};
|
||||||
|
|
||||||
render();
|
const app = completeClientRender();
|
||||||
|
|
||||||
|
export default app;
|
||||||
|
|
||||||
|
|
||||||
{{{ entryCode }}}
|
{{{ entryCode }}}
|
||||||
|
@ -29,8 +29,7 @@ export default function (api) {
|
|||||||
'modifyBabelPresetOpts',
|
'modifyBabelPresetOpts',
|
||||||
'chainWebpack',
|
'chainWebpack',
|
||||||
'addTmpGenerateWatcherPaths',
|
'addTmpGenerateWatcherPaths',
|
||||||
'modifyPublicPathStr',
|
'modifyPublicPathStr'
|
||||||
'modifyHTML',
|
|
||||||
].forEach((name) => {
|
].forEach((name) => {
|
||||||
api.registerMethod({ name });
|
api.registerMethod({ name });
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user