qiankun子应用卸载切换时-setInterval被清除

397 阅读2分钟

问题

在乾坤中主应用有多个子应用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);
}

总结一下,在迫不得已的情况下可以这样处理,就得保证自己的代码不会有内存泄漏风险