useHistoryBackTrap 阻止浏览器返回
阻止浏览器返回操作,可在用户编辑页面未保存时做出提醒,不依赖 Vue 原生 js 中也可以使用
代码实现
ts
export function useHistoryBackTrap(cb: () => boolean | void | Promise<boolean | void>) {
const name = 'use-history-back-trap';
const isTraping = () => window.history.state === name;
async function popstate(_event: PopStateEvent) {
if (isTraping()) return;
window.history.forward();
while (true) {
await new Promise(resolve => requestAnimationFrame(resolve));
if (isTraping()) break;
}
if (await cb()) {
await destroy();
window.history.back();
}
}
function start() {
if (!isTraping()) window.history.pushState(name, name, window.location.href);
window.removeEventListener('popstate', popstate);
window.addEventListener('popstate', popstate);
}
async function destroy() {
window.removeEventListener('popstate', popstate);
if (!isTraping()) return;
window.history.back();
while (true) {
await new Promise(resolve => requestAnimationFrame(resolve));
if (!isTraping()) break;
}
}
return { start, destroy };
}
使用示例
vue
<script lang="ts" setup>
import { Dialog } from 'vant';
import { ref } from 'vue-demi';
import { useHistoryBackTrap } from './use-history-back-trap';
const isModify = ref<boolean>(false)
const historyBackTrap = useHistoryBackTrap(() => {
// 若未修改内容,则不提示
if (!isModify.value) return true;
// 返回 true 取消拦截,返回 false 拦截本次返回
return Dialog.confirm({
message: '内容还没有提交,返回后您编辑的内容将失效',
cancelButtonText: '我再想想',
confirmButtonText: '确认返回'
})
.then(() => true)
.catch(() => false);
});
// 在页面无交互之前不要调用 `start()` 部分浏览器会阻止此操作
historyBackTrap.start();
// 跳转其他页面或不需要拦截时需调用 historyBackTrap.destroy()
function handleGoOtherPage() {
await historyBackTrap.destroy();
router.push('/other-page');
}
</script>