产品有一个需求,在页面有文本框在编辑状态时,用户关闭、刷新、返回、页面跳转时要拦截用户操作,明确告知用户会丢失数据。考虑到这是一个常用功能,于是封装一个通用的组合函数来在项目中使用。
src/composable/browser-events/index.ts
/**
* 监听浏览器自带刷新、前进、回退、关闭标签页、切换标签页
*/
const WINDOW_EVENTS = [
/** 监听浏览器前进回退 */
'popstate',
/** 监听标签页关闭和刷新 */
'beforeunload',
/** 文档卸载 */
'unload',
] as const;
const DOCUMENT_EVENTS = [
/** 监听浏览器切换标签页 */
'visibilitychange',
] as const;
// 提取联合类型
type WindowEventName = (typeof WINDOW_EVENTS)[number];
type DocumentEventName = (typeof DOCUMENT_EVENTS)[number];
type BrowserEventName = WindowEventName | DocumentEventName;
// 所有监听函数类型定义
type BrowserEventHandlers = Partial<{
[K in BrowserEventName]: (event: Event) => void;
}>;
export const useBrowserEvents = (options: BrowserEventHandlers) => {
const eventNames = Object.keys(options) as BrowserEventName[];
/**
* 初始化
*/
const init = () => {
eventNames.forEach(eventName => {
const fn = options[eventName];
const isFunction = typeof fn === 'function';
if (isFunction) {
if (WINDOW_EVENTS.includes(eventName as WindowEventName)) {
window.addEventListener(eventName, fn);
}
if (DOCUMENT_EVENTS.includes(eventName as DocumentEventName)) {
document.addEventListener(eventName, fn);
}
}
});
};
/**
* 销毁
*/
const destroy = () => {
eventNames.forEach(eventName => {
const fn = options[eventName];
const isFunction = typeof fn === 'function';
if (isFunction) {
if (WINDOW_EVENTS.includes(eventName as WindowEventName)) {
window.removeEventListener(eventName, fn);
}
if (DOCUMENT_EVENTS.includes(eventName as DocumentEventName)) {
document.removeEventListener(eventName, fn);
}
}
});
};
return {
init,
destroy,
};
};
export default useBrowserEvents;
使用方法
使用说明文档
概述
useBrowserEvents
是一个用于监听浏览器事件的自定义钩子。该钩子可以帮助你轻松地添加和移除对浏览器内置事件(如刷新、前进、回退、关闭标签页、切换标签页等)的监听。
导出常量
WINDOW_EVENTS
WINDOW_EVENTS
是一个常量数组,包含了需要在 window
对象上监听的事件类型:
'popstate'
:监听浏览器前进和回退事件。'beforeunload'
:监听标签页关闭和刷新事件。'unload'
:监听文档卸载事件。
DOCUMENT_EVENTS
DOCUMENT_EVENTS
是一个常量数组,包含了需要在 document
对象上监听的事件类型:
'visibilitychange'
:监听浏览器标签页切换事件。
使用方法
引入模块
import useBrowserEvents from '@/composable/browser-events';
调用钩子
const browserEvents = useBrowserEvents({
popstate: () => {
console.log('Browser history navigation occurred');
},
beforeunload: (event) => {
event.preventDefault();
event.returnValue = '';
console.log('Browser is about to refresh or close');
},
unload: () => {
console.log('Document is being unloaded');
},
visibilitychange: () => {
if (document.hidden) {
console.log('Tab is not visible');
} else {
console.log('Tab is visible');
}
}
});
初始化事件监听
browserEvents.init();
移除事件监听
browserEvents.destroy();
API 说明
useBrowserEvents
参数
options
(Object): 一个包含事件处理函数的对象,键名为事件名称,值为对应的事件处理函数。
返回值
init
(Function): 初始化事件监听,将指定的事件处理函数添加到相应的事件上。destroy
(Function): 移除事件监听,将指定的事件处理函数从相应的事件上移除。
事件类型和说明
WINDOW_EVENTS
'popstate'
:当活动历史记录条目更改时触发。'beforeunload'
:在窗口、文档及其资源将要被卸载时触发。'unload'
:在窗口、文档及其资源即将被卸载时触发。
DOCUMENT_EVENTS
'visibilitychange'
:当文档的可见性状态发生变化时触发。
示例
以下是一个完整的使用示例:
import useBrowserEvents from './use-browser-events'';
const browserEvents = useBrowserEvents({
popstate: () => {
console.log('Browser history navigation occurred');
},
beforeunload: (event) => {
event.preventDefault();
event.returnValue = '';
console.log('Browser is about to refresh or close');
},
unload: () => {
console.log('Document is being unloaded');
},
visibilitychange: () => {
if (document.hidden) {
console.log('Tab is not visible');
} else {
console.log('Tab is visible');
}
}
});
browserEvents.init();
// 当不再需要监听这些事件时
// browserEvents.destroy();
在这个示例中,我们定义了多个事件处理函数,并通过 useBrowserEvents
钩子将它们绑定到相应的事件上。通过调用 init
方法初始化事件监听,当不再需要时可以调用 destroy
方法移除这些监听。
案例
import useBrowserEvents from '@/composable/browser-events'
const popstateHandle = (e: Event) => {
const event = e as PopStateEvent;
alert('location: ' + document.location + ', state: ' + JSON.stringify(event.state));
};
const beforeunloadHandle = (e: Event) => {
const event = e as BeforeUnloadEvent;
// 判断是否有未保存的数据
if (text.value === '') {
return;
}
/** 如果有未保存的数据,就拦截浏览器默认关闭、跳转的行为 */
if (text.value) {
// 按照标准的规定,需要取消该事件。如果你希望阻止事件的默认行为(例如 submit、click 等),你需要调用 event.preventDefault() 或其他等效方式
event.preventDefault();
// Chrome 要求设置 returnValue 属性。在 Chrome 浏览器中,为了兼容某些老旧行为,必须设置 event.returnValue = false,否则事件可能不会被正确取消。这通常出现在对 beforeunload 或老版本浏览器的兼容处理代码中。
event.returnValue = '';
}
};
const { init, destroy } = useBrowserEvents({
/** 浏览器地址变化 */
popstate: popstateHandle,
/** 拦截关闭浏览器事件 */
beforeunload: beforeunloadHandle
})
/** 挂载时初始化 **/
onMounted(() => {
init()
})
/** 卸载时销毁事件 */
onBeforeUnmount(() => {
destroy()
})
参考文档
developer.mozilla.org/zh-CN/docs/… developer.mozilla.org/zh-CN/docs/…