mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-04-06 03:59:53 +08:00
Fix qiankun 内存泄漏问题 (#126)
* fix: 修复乾坤插件内存问题 * fix: 多页签时关闭页面需要清除缓存 * feat: 乾坤增加entry配置,用于区别同一name不同位置用法 * docs: 去掉文档 * fix: preset-build-in的route api 兼容 * fix: 修复问题 * docs: 更新w文档 * fix: 变更api
This commit is contained in:
parent
d37d6b480b
commit
c09dfeadde
@ -74,7 +74,6 @@ Fes.js 路由基于 [Vue Router 4.0](https://next.router.vuejs.org/introduction.
|
|||||||
返回当前 `router` 实例。
|
返回当前 `router` 实例。
|
||||||
```js
|
```js
|
||||||
import { getRouter } from "@fesjs/fes";
|
import { getRouter } from "@fesjs/fes";
|
||||||
|
|
||||||
const router = getRouter();
|
const router = getRouter();
|
||||||
router.push();
|
router.push();
|
||||||
```
|
```
|
||||||
|
@ -301,7 +301,7 @@ import { access, useAccess } from '@fesjs/fes';
|
|||||||
```js
|
```js
|
||||||
api.addCoreExports(() => [
|
api.addCoreExports(() => [
|
||||||
{
|
{
|
||||||
specifiers: ['getRoutes', 'getRouter', 'getHistory', 'destroyRouter'],
|
specifiers: ['getRoutes'],
|
||||||
source: absCoreFilePath
|
source: absCoreFilePath
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
@ -269,3 +269,24 @@ export default {
|
|||||||
|
|
||||||
- 主应用使用 props 的模式传递数据(参考主应用装载子应用配置一节)
|
- 主应用使用 props 的模式传递数据(参考主应用装载子应用配置一节)
|
||||||
- 子应用在生命周期钩子中获取 props 消费数据(参考子应用运行时配置一节)
|
- 子应用在生命周期钩子中获取 props 消费数据(参考子应用运行时配置一节)
|
||||||
|
|
||||||
|
|
||||||
|
### MicroApp
|
||||||
|
| 属性 | 说明 | 类型 | 默认值 |
|
||||||
|
| ---- | ----------- | ------------- | ---------- |
|
||||||
|
| name | 子应用名称,传入`qiankun.main.apps`配置中的`name` | String | - |
|
||||||
|
| settings | 子应用配置信息 | Object | {} |
|
||||||
|
| props | 传入子应用的参数 | Object | {} |
|
||||||
|
| lifeCycles | 子应用生命周期钩子 | Object | {} |
|
||||||
|
| cacheName | 子应用缓存名称,配置后根据`name`+`cacheName`缓存子应用实例 | Object | - |
|
||||||
|
|
||||||
|
|
||||||
|
### MicroAppWithMemoHistory
|
||||||
|
| 属性 | 说明 | 类型 | 默认值 |
|
||||||
|
| ---- | ----------- | ------------- | ---------- |
|
||||||
|
| name | 子应用名称,传入`qiankun.main.apps`配置中的`name` | String | - |
|
||||||
|
| settings | 子应用配置信息 | Object | {} |
|
||||||
|
| props | 传入子应用的参数 | Object | {} |
|
||||||
|
| lifeCycles | 子应用生命周期钩子 | Object | {} |
|
||||||
|
| cacheName | 子应用缓存名称,配置后根据`name`+`cacheName`缓存子应用实例 | Object | - |
|
||||||
|
| url | 子应用的路由地址 | String | - |
|
@ -31,8 +31,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</FTabs>
|
</FTabs>
|
||||||
<router-view v-slot="{ Component, route }">
|
<router-view v-slot="{ Component, route }">
|
||||||
<keep-alive>
|
<keep-alive :include="keepAlivePages">
|
||||||
<component :is="Component" :key="getPageKey(route)" />
|
<component
|
||||||
|
:is="getComponent(Component, route, true)"
|
||||||
|
:key="getPageKey(route)"
|
||||||
|
/>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</router-view>
|
</router-view>
|
||||||
</template>
|
</template>
|
||||||
@ -68,11 +71,12 @@ export default {
|
|||||||
return {
|
return {
|
||||||
path: _route.path,
|
path: _route.path,
|
||||||
route: _route,
|
route: _route,
|
||||||
name: _route.meta.name,
|
name: _route.meta.name ?? _route.name,
|
||||||
title: computed(() => transTitle(title)),
|
title: computed(() => transTitle(title)),
|
||||||
key: getKey()
|
key: getKey()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
const keepAlivePages = ref([]);
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -122,6 +126,12 @@ export default {
|
|||||||
}
|
}
|
||||||
list.splice(index, 1);
|
list.splice(index, 1);
|
||||||
pageList.value = list;
|
pageList.value = list;
|
||||||
|
const _keepAlivePages = [...keepAlivePages.value];
|
||||||
|
const keepIndex = _keepAlivePages.indexOf(selectedPage.name);
|
||||||
|
if (keepIndex !== -1) {
|
||||||
|
_keepAlivePages.splice(keepIndex, 1);
|
||||||
|
}
|
||||||
|
keepAlivePages.value = _keepAlivePages;
|
||||||
};
|
};
|
||||||
const reloadPage = (path) => {
|
const reloadPage = (path) => {
|
||||||
const selectedPage = findPage(path || unref(route.path));
|
const selectedPage = findPage(path || unref(route.path));
|
||||||
@ -132,6 +142,7 @@ export default {
|
|||||||
const closeOtherPage = (path) => {
|
const closeOtherPage = (path) => {
|
||||||
const selectedPage = findPage(path || unref(route.path));
|
const selectedPage = findPage(path || unref(route.path));
|
||||||
pageList.value = [selectedPage];
|
pageList.value = [selectedPage];
|
||||||
|
keepAlivePages.value = [selectedPage.name];
|
||||||
};
|
};
|
||||||
const getPageKey = (_route) => {
|
const getPageKey = (_route) => {
|
||||||
const selectedPage = findPage(_route.path);
|
const selectedPage = findPage(_route.path);
|
||||||
@ -151,10 +162,10 @@ export default {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const keepAlivePages = ref([]);
|
|
||||||
const getComponent = (Component, _route) => {
|
const getComponent = (Component, _route, isKeep = false) => {
|
||||||
if (_route.meta['keep-alive']) {
|
if (isKeep || _route.meta['keep-alive']) {
|
||||||
const name = _route.meta?.name || _route.name;
|
const name = _route.meta?.name ?? _route.name;
|
||||||
if (name) {
|
if (name) {
|
||||||
// 修改组件的 name
|
// 修改组件的 name
|
||||||
Component.type.name = name;
|
Component.type.name = name;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="haizekuo">
|
<div class="haizekuo">
|
||||||
app1 - index
|
app1 - index
|
||||||
|
<input />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<config>
|
<config>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="haizekuo">
|
<div class="haizekuo">
|
||||||
app1 - test
|
app1 - test
|
||||||
|
<input />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<config>
|
<config>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
main
|
main
|
||||||
|
<input />
|
||||||
<FTabs v-model="activeKey">
|
<FTabs v-model="activeKey">
|
||||||
<FTabPane name="Tab 1" value="1">
|
<FTabPane name="Tab 1" value="1">
|
||||||
<MicroAppWithMemoHistory key="1" name="app1" url="/app1" a="1" />
|
<MicroAppWithMemoHistory key="1" style="background: red" name="app1" url="/app1" a="1" />
|
||||||
</FTabPane>
|
</FTabPane>
|
||||||
<FTabPane name="Tab 2" value="2">
|
<FTabPane name="Tab 2" value="2">
|
||||||
<MicroAppWithMemoHistory key="2" name="app1" url="/app1/test" />
|
<MicroAppWithMemoHistory key="2" name="app1" url="/app1/test" />
|
||||||
|
@ -2,11 +2,11 @@ import { defaultHistoryType } from '../constants';
|
|||||||
|
|
||||||
function getMicroApp(options) {
|
function getMicroApp(options) {
|
||||||
const {
|
const {
|
||||||
key, microAppName, masterHistoryType, base, namespace, ...normalizedRouteProps
|
key, microAppName, cacheName, masterHistoryType, base, namespace, ...normalizedRouteProps
|
||||||
} = options;
|
} = options;
|
||||||
return `(() => {
|
return `(() => {
|
||||||
const { getMicroAppRouteComponent } = require('@@/${namespace}/getMicroAppRouteComponent');
|
const { getMicroAppRouteComponent } = require('@@/${namespace}/getMicroAppRouteComponent');
|
||||||
return getMicroAppRouteComponent({key: '${key}', appName: '${microAppName}', base: '${base}', masterHistoryType: '${masterHistoryType}', routeProps: ${JSON.stringify(normalizedRouteProps)} })
|
return getMicroAppRouteComponent({key: '${key}', appName: '${microAppName}', cacheName: '${cacheName}', base: '${base}', masterHistoryType: '${masterHistoryType}', routeProps: ${JSON.stringify(normalizedRouteProps)} })
|
||||||
})()`;
|
})()`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,6 +19,7 @@ function modifyRoutesWithAttachMode({
|
|||||||
if (route.meta && route.meta.microApp) {
|
if (route.meta && route.meta.microApp) {
|
||||||
route.component = getMicroApp({
|
route.component = getMicroApp({
|
||||||
key: route.path,
|
key: route.path,
|
||||||
|
cacheName: route.meta.cacheName ?? route.path,
|
||||||
microAppName: route.meta.microApp,
|
microAppName: route.meta.microApp,
|
||||||
masterHistoryType,
|
masterHistoryType,
|
||||||
base,
|
base,
|
||||||
|
@ -12,13 +12,13 @@ import {mergeWith} from "{{{LODASH_ES}}}";
|
|||||||
// eslint-disable-next-line import/extensions
|
// eslint-disable-next-line import/extensions
|
||||||
import { getMasterOptions } from "./masterOptions";
|
import { getMasterOptions } from "./masterOptions";
|
||||||
|
|
||||||
function unmountMicroApp(microApp) {
|
async function unmountMicroApp(microApp) {
|
||||||
if (!microApp) {
|
if (!microApp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const status = microApp.getStatus();
|
const status = microApp.getStatus();
|
||||||
if (status === 'MOUNTED') {
|
if (status === 'MOUNTED') {
|
||||||
microApp.unmount();
|
await microApp.unmount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,6 +28,7 @@ export const MicroApp = defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
cacheName: String,
|
||||||
settings: Object,
|
settings: Object,
|
||||||
props: Object,
|
props: Object,
|
||||||
lifeCycles: Object
|
lifeCycles: Object
|
||||||
@ -64,26 +65,26 @@ export const MicroApp = defineComponent({
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const propsConfigRef = computed(() => {
|
const propsConfigRef = computed(() => {
|
||||||
return {
|
return {
|
||||||
...propsFromConfigRef.value,
|
...propsFromConfigRef.value,
|
||||||
...props.props,
|
...props.props,
|
||||||
...attrs
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 只有当name变化时才重新加载新的子应用
|
// 只有当name变化时才重新加载新的子应用
|
||||||
const loadApp = () => {
|
const loadApp = () => {
|
||||||
const appConfig = appConfigRef.value;
|
const appConfig = appConfigRef.value;
|
||||||
const { name, entry } = appConfig;
|
const { name, cacheName } = appConfig;
|
||||||
// 加载新的
|
// 加载新的
|
||||||
microAppRef.value = loadMicroApp(
|
const app = loadMicroApp(
|
||||||
{
|
{
|
||||||
// 保证唯一
|
// 保证唯一
|
||||||
name: `${name}_${Date.now()}`,
|
name: `${name}_${props.cacheName || ''}`,
|
||||||
entry: entry,
|
entry: entry,
|
||||||
container: containerRef.value,
|
container: containerRef.value,
|
||||||
props: {...propsConfigRef.value}
|
props: {...propsConfigRef.value, ...attrs}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...globalSettings,
|
...globalSettings,
|
||||||
@ -96,6 +97,12 @@ export const MicroApp = defineComponent({
|
|||||||
(v1, v2) => concat(v1 ?? [], v2 ?? [])
|
(v1, v2) => concat(v1 ?? [], v2 ?? [])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
['loadPromise', 'bootstrapPromise', 'mountPromise', 'unmountPromise'].forEach((key)=>{
|
||||||
|
app[key].catch((e)=>{
|
||||||
|
console.warn("[@fesjs/plugin-qiankun]", e)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
microAppRef.value = app;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 当参数变化时,update子应用
|
// 当参数变化时,update子应用
|
||||||
@ -131,7 +138,7 @@ export const MicroApp = defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 返回 microApp.update 形成链式调用
|
// 返回 microApp.update 形成链式调用
|
||||||
return microApp.update({...propsConfigRef.value});
|
return microApp.update({...propsConfigRef.value, ...attrs});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -14,6 +14,7 @@ export const MicroAppWithMemoHistory = defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
cacheName: String,
|
||||||
settings: Object,
|
settings: Object,
|
||||||
props: Object,
|
props: Object,
|
||||||
lifeCycles: Object,
|
lifeCycles: Object,
|
||||||
|
@ -3,10 +3,11 @@ import { MicroApp } from './MicroApp';
|
|||||||
export function getMicroAppRouteComponent({
|
export function getMicroAppRouteComponent({
|
||||||
key,
|
key,
|
||||||
appName,
|
appName,
|
||||||
|
cacheName,
|
||||||
base,
|
base,
|
||||||
masterHistoryType,
|
masterHistoryType,
|
||||||
routeProps
|
routeProps
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
return <MicroApp key={key} base={base} masterHistoryType={masterHistoryType} name={appName} {...routeProps} />;
|
return <MicroApp key={key} base={base} masterHistoryType={masterHistoryType} name={appName} cacheName={cacheName} {...routeProps} />;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { plugin, ApplyPluginsType, getRouter, getHistory, destroyRouter } from '@@/core/coreExports';
|
import { plugin, ApplyPluginsType } from '@@/core/coreExports';
|
||||||
{{#HAS_PLUGIN_MODEL}}
|
{{#HAS_PLUGIN_MODEL}}
|
||||||
import { setModelState } from './qiankunModel';
|
import { setModelState } from './qiankunModel';
|
||||||
{{/HAS_PLUGIN_MODEL}}
|
{{/HAS_PLUGIN_MODEL}}
|
||||||
@ -18,6 +18,7 @@ function isPromise(obj) {
|
|||||||
let render = () => {};
|
let render = () => {};
|
||||||
let cacheAppPromise = null;
|
let cacheAppPromise = null;
|
||||||
let hasMountedAtLeastOnce = false;
|
let hasMountedAtLeastOnce = false;
|
||||||
|
const cache = {};
|
||||||
|
|
||||||
export default () => defer.promise;
|
export default () => defer.promise;
|
||||||
|
|
||||||
@ -91,6 +92,11 @@ export function genMount(mountElementId) {
|
|||||||
defer.resolve();
|
defer.resolve();
|
||||||
}
|
}
|
||||||
hasMountedAtLeastOnce = true;
|
hasMountedAtLeastOnce = true;
|
||||||
|
cacheAppPromise.then((app)=>{
|
||||||
|
if(!cache[props.name]) {
|
||||||
|
cache[props.name] = app;
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,9 +115,15 @@ export function genUpdate() {
|
|||||||
// 子应用生命周期钩子Unmount
|
// 子应用生命周期钩子Unmount
|
||||||
export function genUnmount() {
|
export function genUnmount() {
|
||||||
return async (props) => {
|
return async (props) => {
|
||||||
const history = getHistory();
|
if (cache[props.name]) {
|
||||||
history.destroy(); // 会触发app.unmount
|
setTimeout(() => {
|
||||||
destroyRouter();
|
const app = cache[props.name];
|
||||||
|
app.unmount();
|
||||||
|
app._container.innerHTML = '';
|
||||||
|
delete cache[props.name];
|
||||||
|
cacheAppPromise = null;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
const slaveRuntime = getSlaveRuntime();
|
const slaveRuntime = getSlaveRuntime();
|
||||||
if (slaveRuntime.unmount) {
|
if (slaveRuntime.unmount) {
|
||||||
await slaveRuntime.unmount(props);
|
await slaveRuntime.unmount(props);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createMemoryHistory, getHistory } from '@@/core/coreExports';
|
import { createMemoryHistory } from '@@/core/coreExports';
|
||||||
import qiankunRender, { clientRenderOptsStack, history } from './lifecycles';
|
import qiankunRender, { clientRenderOptsStack, history as historyConfig } from './lifecycles';
|
||||||
|
|
||||||
|
|
||||||
export const render = oldRender => qiankunRender().then(oldRender);
|
export const render = oldRender => qiankunRender().then(oldRender);
|
||||||
@ -15,19 +15,18 @@ export function modifyClientRenderOpts(memo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function modifyCreateHistory(memo) {
|
export function modifyCreateHistory(memo) {
|
||||||
if (history.url) {
|
if (historyConfig.url) {
|
||||||
return createMemoryHistory
|
return createMemoryHistory
|
||||||
}
|
}
|
||||||
return memo;
|
return memo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onRouterCreated({ router }) {
|
export function onRouterCreated({ router, history }) {
|
||||||
if(history.url) {
|
if(historyConfig.url) {
|
||||||
const memoryHistory = getHistory();
|
history.push(historyConfig.url)
|
||||||
memoryHistory.push(history.url)
|
|
||||||
}
|
}
|
||||||
if(history.onRouterInit){
|
if(historyConfig.onRouterInit){
|
||||||
history.onRouterInit(router)
|
historyConfig.onRouterInit(router)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ const beforeRender = async ({rootElement}) => {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
app.unmount();
|
app.unmount();
|
||||||
|
app._container.innerHTML = '';
|
||||||
}
|
}
|
||||||
return initialState;
|
return initialState;
|
||||||
};
|
};
|
||||||
|
@ -10,9 +10,6 @@ const ROUTER_BASE = '{{{ routerBase }}}';
|
|||||||
let router = null;
|
let router = null;
|
||||||
let history = null;
|
let history = null;
|
||||||
export const createRouter = (routes) => {
|
export const createRouter = (routes) => {
|
||||||
if (router) {
|
|
||||||
return router;
|
|
||||||
}
|
|
||||||
const createHistory = plugin.applyPlugins({
|
const createHistory = plugin.applyPlugins({
|
||||||
key: 'modifyCreateHistory',
|
key: 'modifyCreateHistory',
|
||||||
type: ApplyPluginsType.modify,
|
type: ApplyPluginsType.modify,
|
||||||
@ -36,7 +33,7 @@ export const createRouter = (routes) => {
|
|||||||
plugin.applyPlugins({
|
plugin.applyPlugins({
|
||||||
key: 'onRouterCreated',
|
key: 'onRouterCreated',
|
||||||
type: ApplyPluginsType.event,
|
type: ApplyPluginsType.event,
|
||||||
args: { router },
|
args: { router, history },
|
||||||
});
|
});
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user