Skip to content

Commit

Permalink
chore: optimize code (#2925)
Browse files Browse the repository at this point in the history
  • Loading branch information
kuitos authored Mar 11, 2024
1 parent ea18ce6 commit 6d252c6
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 39 deletions.
5 changes: 5 additions & 0 deletions .changeset/smart-guests-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@qiankunjs/sandbox": patch
---

chore: optimize code
74 changes: 35 additions & 39 deletions packages/sandbox/src/patchers/windowListener.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable no-param-reassign */
/**
* @author Kuitos
* @since 2019-04-11
*/
import { type Free } from './types';

const rawAddEventListener = window.addEventListener;
const rawRemoveEventListener = window.removeEventListener;
Expand All @@ -15,67 +15,64 @@ type ListenerMapObject = {

const DEFAULT_OPTIONS: AddEventListenerOptions = { capture: false, once: false, passive: false };

// 移除cacheListener
const normalizeOptions = (rawOptions?: boolean | AddEventListenerOptions | null): AddEventListenerOptions => {
if (typeof rawOptions === 'object') {
return rawOptions ?? DEFAULT_OPTIONS;
}
return { capture: !!rawOptions, once: false, passive: false };
};

const findListenerIndex = (
listeners: ListenerMapObject[],
rawListener: EventListenerOrEventListenerObject,
options: AddEventListenerOptions,
): number =>
listeners.findIndex((item) => item.rawListener === rawListener && item.options.capture === options.capture);

const removeCacheListener = (
listenerMap: Map<string, ListenerMapObject[]>,
type: string,
rawListener: EventListenerOrEventListenerObject,
rawOptions?: boolean | AddEventListenerOptions,
): ListenerMapObject => {
// 如果 options 为 null、undefined,使用默认值
const opts = rawOptions ?? DEFAULT_OPTIONS;
// 处理 options,确保它是一个对象
const options = typeof opts === 'object' ? opts : { capture: !!opts };

const options = normalizeOptions(rawOptions);
const cachedTypeListeners = listenerMap.get(type) || [];
// listener和capture/useCapture都相同,认为是同一个监听
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
const findIndex = cachedTypeListeners.findIndex(
(item) => item.rawListener === rawListener && item.options.capture == options.capture,
);

const findIndex = findListenerIndex(cachedTypeListeners, rawListener, options);
if (findIndex > -1) {
const cacheListener = cachedTypeListeners[findIndex];
cachedTypeListeners.splice(findIndex, 1);
return cacheListener;
return cachedTypeListeners.splice(findIndex, 1)[0];
}

// 返回原始listener和options
return { listener: rawListener, rawListener, options };
};

// 添加监听构造一个cacheListener对象,考虑到多次添加同一个监听和once的情况
const addCacheListener = (
listenerMap: Map<string, ListenerMapObject[]>,
type: string,
rawListener: EventListenerOrEventListenerObject,
rawOptions?: boolean | AddEventListenerOptions,
): ListenerMapObject | undefined => {
// 如果 options 为 null、undefined,使用默认值
const opts = rawOptions ?? DEFAULT_OPTIONS;
// 处理 options,确保它是一个对象
const options = typeof opts === 'object' ? opts : { capture: !!opts };

const options = normalizeOptions(rawOptions);
const cachedTypeListeners = listenerMap.get(type) || [];
// listener和capture/useCapture都相同,认为是同一个监听
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
const findIndex = cachedTypeListeners.findIndex(
(item) => item.rawListener === rawListener && item.options.capture == options.capture,
);
// 如果事件已经添加到了target的event listeners 列表中,直接返回不需要添加第二次

const findIndex = findListenerIndex(cachedTypeListeners, rawListener, options);
// avoid duplicated listener in the listener list
if (findIndex > -1) return;

let listener: EventListenerOrEventListenerObject = rawListener;
if (options.once)
if (options.once) {
listener = (event: Event) => {
(rawListener as EventListener)(event);
removeCacheListener(listenerMap, type, rawListener, options);
};
}

const cacheListener = { listener, options, rawListener };
listenerMap.set(type, [...cachedTypeListeners, cacheListener]);
return cacheListener;
};

export default function patch(global: WindowProxy) {
export default function patch(global: WindowProxy): Free {
const listenerMap = new Map<string, ListenerMapObject[]>();

global.addEventListener = (
Expand All @@ -84,10 +81,9 @@ export default function patch(global: WindowProxy) {
rawOptions?: boolean | AddEventListenerOptions,
) => {
const addListener = addCacheListener(listenerMap, type, rawListener, rawOptions);
// 如果返回空,则代表事件已经添加过了,不需要重复添加

if (!addListener) return;
const { listener, options } = addListener;
return rawAddEventListener.call(window, type, listener, options);
return rawAddEventListener.call(global, type, addListener.listener, addListener.options);
};

global.removeEventListener = (
Expand All @@ -96,18 +92,18 @@ export default function patch(global: WindowProxy) {
rawOptions?: boolean | AddEventListenerOptions,
) => {
const { listener, options } = removeCacheListener(listenerMap, type, rawListener, rawOptions);
return rawRemoveEventListener.call(window, type, listener, options);
return rawRemoveEventListener.call(global, type, listener, options);
};

return function free() {
listenerMap.forEach((listeners, type) =>
[...listeners].forEach(({ rawListener, options }) => global.removeEventListener(type, rawListener, options)),
);
// 清空listenerMap,避免listenerMap中还存有listener导致内存泄漏
listenerMap.forEach((listeners, type) => {
listeners.forEach(({ rawListener, options }) => {
global.removeEventListener(type, rawListener, options);
});
});
listenerMap.clear();
global.addEventListener = rawAddEventListener;
global.removeEventListener = rawRemoveEventListener;

return () => Promise.resolve();
};
}

0 comments on commit 6d252c6

Please sign in to comment.