diff --git a/packages/fes-plugin-qiankun/package.json b/packages/fes-plugin-qiankun/package.json index a97831e4..b9fe443c 100644 --- a/packages/fes-plugin-qiankun/package.json +++ b/packages/fes-plugin-qiankun/package.json @@ -29,7 +29,7 @@ "dependencies": { "@umijs/utils": "3.3.3", "address": "^1.1.2", - "path-to-regexp": "^6.2.0", + "lodash": "^4.17.15", "qiankun": "2.3.4" }, "peerDependencies": { diff --git a/packages/fes-plugin-qiankun/src/index.js b/packages/fes-plugin-qiankun/src/index.js index c404883a..33f82ba1 100644 --- a/packages/fes-plugin-qiankun/src/index.js +++ b/packages/fes-plugin-qiankun/src/index.js @@ -1,7 +1,3 @@ -// import { join } from 'path'; - -// const namespace = 'plugin-qiankun'; - export default (api) => { api.describe({ key: 'qiankun', @@ -21,8 +17,4 @@ export default (api) => { require.resolve('./main'), require.resolve('./micro') ]); - - // const absRuntimeFilePath = join(namespace, 'runtime.js'); - - // api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`); }; diff --git a/packages/fes-plugin-qiankun/src/main/index.js b/packages/fes-plugin-qiankun/src/main/index.js index 83161a81..78c7da2a 100644 --- a/packages/fes-plugin-qiankun/src/main/index.js +++ b/packages/fes-plugin-qiankun/src/main/index.js @@ -75,136 +75,4 @@ export default function (api) { source: absGetMicroAppRouteCompPath } ]); - - - // 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 - // } - // ]); } diff --git a/packages/fes-plugin-qiankun/src/main/runtime/MicroApp.tpl b/packages/fes-plugin-qiankun/src/main/runtime/MicroApp.tpl index 3c275925..08f8072e 100644 --- a/packages/fes-plugin-qiankun/src/main/runtime/MicroApp.tpl +++ b/packages/fes-plugin-qiankun/src/main/runtime/MicroApp.tpl @@ -4,14 +4,15 @@ import { watch, computed, onBeforeUnmount, - onMounted -} from 'vue'; -import { loadMicroApp } from 'qiankun'; + onMounted, +} from "vue"; +import { loadMicroApp } from "qiankun"; +import mergeWith from "lodash/mergeWith"; // eslint-disable-next-line import/extensions -import { getMasterOptions } from './masterOptions'; +import { getMasterOptions } from "./masterOptions"; function unmountMicroApp(microApp) { - if (microApp) { + if (microApp && microApp.mountPromise) { microApp.mountPromise.then(() => microApp.unmount()); } } @@ -20,8 +21,8 @@ export const MicroApp = defineComponent({ props: { name: { type: String, - required: true - } + required: true, + }, }, setup(props) { const { @@ -31,37 +32,48 @@ 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 appConfigRef = computed(() => { - const _appConfig = apps.find(app => app.name === props.name); - if (!_appConfig) { + const getAppConfig = () => { + const appConfig = apps.find((app) => app.name === name); + if (!appConfig) { throw new Error( - `[@fesjs/plugin-qiankun]: Can not find the configuration of ${props.name} app!` + `[@fesjs/plugin-qiankun]: Can not find the configuration of ${name} app!` ); } - return _appConfig; - }); + return appConfig; + }; const loadApp = () => { - const appConfig = appConfigRef.value; + const appConfig = getAppConfig(); + const { name, entry, props: propsFromConfig = {} } = appConfig; // 加载新的 microAppRef.value = loadMicroApp( { - name: appConfig.name, - entry: appConfig.entry, + name: name, + entry: entry, container: containerRef.value, - props: { ...appConfig.props } + props: { ...propsFromConfig, ...propsFromParams }, }, { ...globalSettings, - ...(props.settings || {}), - globalLifeCycles, - lifeCycles: props.lifeCycles - } + ...settingsFromProps, + }, + mergeWith({}, globalLifeCycles, lifeCycles, (v1, v2) => + concat(v1 ?? [], v2 ?? []) + ) ); }; @@ -73,7 +85,7 @@ export const MicroApp = defineComponent({ unmountMicroApp(microAppRef.value); }); - watch(appConfigRef, () => { + watch(props, () => { unmountMicroApp(microAppRef.value); loadApp(); @@ -81,7 +93,8 @@ export const MicroApp = defineComponent({ watch(microAppRef, () => { const microApp = microAppRef.value; - const appConfig = appConfigRef.value; + const appConfig = getAppConfig(); + const { props: propsFromConfig = {} } = appConfig; if (microApp) { if (!updatingPromise.value) { // 初始化 updatingPromise 为 microApp.mountPromise,从而确保后续更新是在应用 mount 完成之后 @@ -89,12 +102,13 @@ export const MicroApp = defineComponent({ } else { // 确保 microApp.update 调用是跟组件状态变更顺序一致的,且后一个微应用更新必须等待前一个更新完成 updatingPromise.value = updatingPromise.value.then(() => { - const canUpdate = app => app?.update && app.getStatus() === 'MOUNTED'; + const canUpdate = (app) => + app?.update && app.getStatus() === "MOUNTED"; if (canUpdate(microApp)) { - if (process.env.NODE_ENV === 'development') { + if (process.env.NODE_ENV === "development") { if ( - Date.now() - updatingTimestamp.value - < 200 + 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.` @@ -102,7 +116,7 @@ export const MicroApp = defineComponent({ } console.info( - `[@umijs/plugin-qiankun] MicroApp ${props.name} is updating with props: `, + `[@fesjs/plugin-qiankun] MicroApp ${props.name} is updating with props: `, props ); updatingTimestamp.value = Date.now(); @@ -110,7 +124,8 @@ export const MicroApp = defineComponent({ // 返回 microApp.update 形成链式调用 return microApp.update({ - ...appConfig.props + ...propsFromConfig, + ...propsFromParams, }); } }); @@ -118,6 +133,6 @@ export const MicroApp = defineComponent({ } }); - return () =>
; - } + return () => ; + }, }); diff --git a/packages/fes-plugin-qiankun/src/micro/index.js b/packages/fes-plugin-qiankun/src/micro/index.js index 38a00448..31c22d59 100644 --- a/packages/fes-plugin-qiankun/src/micro/index.js +++ b/packages/fes-plugin-qiankun/src/micro/index.js @@ -144,108 +144,4 @@ if (!window.__POWERED_BY_QIANKUN__) { } ` ); - - // 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); - // } - // ` - // ); }