问题
在乾坤中主应用有多个子应用abc。当在子应用中使用了setInterval实现一些轮训,或者全局的toast延时关闭。乾坤切换子应用时会清除这些interval,导致子应用的一些回调无法调用。
比如
-
- 业务中有一个强依赖 interval 的业务组件,用于发送心跳;当子应用切换时, interval 会被默认清理掉。
- 我们的业务组件中的 toast 设定了 3 秒自动消失,内部使用的setInterval实现,如果 qiankun 自动帮使用者清副作用,会导致提示框无法消失。😓
定位问题
找到qiankun中关于代理setInterval的代码:src/sandbox/patchers/interval.ts
/* eslint-disable no-param-reassign */
/**
* @author Kuitos
* @since 2019-04-11
*/
import { noop } from 'lodash';
const rawWindowInterval = window.setInterval;
const rawWindowClearInterval = window.clearInterval;
export default function patch(global: Window) {
let intervals: number[] = [];
global.clearInterval = (intervalId: number) => {
intervals = intervals.filter((id) => id !== intervalId);
return rawWindowClearInterval.call(window, intervalId as any);
};
global.setInterval = (handler: CallableFunction, timeout?: number, ...args: any[]) => {
const intervalId = rawWindowInterval(handler, timeout, ...args);
intervals = [...intervals, intervalId];
return intervalId;
};
return function free() {
intervals.forEach((id) => global.clearInterval(id));
global.setInterval = rawWindowInterval;
global.clearInterval = rawWindowClearInterval;
return noop;
};
}
src/sandbox/index.ts
我们可以发现,只要子应用中使用代理后的setInterval,qiankun用了一个intervals数组,收集了子应用中的所有intervalId,并且在子应用卸载的时候执行free中把所有intervals都clear了!确实保证了不会内存泄漏~
所以找到了关键点,使用代理的 setInterval,intervalId 就会被收集起来,卸载的时候统一 清除。
那就简单了,我们在子应用中不用qiankun重写的 setInterval, 使用 原生setInterval 不就行了吗?
解决
主应用中
把主应用window当作props传给子应用,以便子应用使用原生setInterval
registerMicroApps(
[
{
name: 'app1',
entry: '//localhost:8080',
container: '#container',
activeRule: '/react',
props: {
name: 'kuitos',
mainAppWindow:window,
},
},
]);
子应用中
前后打印window.setInterval 会发现是两个函数,第一个是乾坤重写的,第二个就是原生setInterval了
export async function mount(props) {
console.log('micro fe props from main framework', props);
const {mainAppWindow} = props || {};
/*
* @Description
* 被qiankun代理的setInterval,会将Interval-id收集,
* 被qiankun代理的clearInterval,在子应用切换时会清除所有Interval-id
* 将子应用被qiankun代理的setInterval改回原生setInterval,子应用Interval-id就不会被收集
* Interval-id不会被qiankun收集,那么在切换子应用时,Interval不会被清除
*/
// window.clearInterval = mainAppWindow.clearInterval;
console.log(window.setInterval, 'window.setInterval');
if (mainAppWindow) {
window.setInterval = mainAppWindow.setInterval;
}
console.log(window.setInterval, 'window.setInterval');
actions = props?.actions;
render(props);
}