前言
项目中,产品经理往往会提这么一个需求:
-
我设计了一个表单表单,表单有各种输入项,这些内容非常重要,一定要让用户填写完整,如果用户没有填写完整,中途退出的话,一定要弹框提示用户是否不保留已输入的内容继续离开
-
我设计了一个考试系统,如果考生没有作答完成,或者提前关闭(刷新)浏览器,都需要提示考试这样会导致成绩作废
应对这些类似的需求,在Vue
中,Vue Router 的 beforeRouteLeave
导航守卫可以在路由发生变化前阻止并提示用户是否切换路由。
示例
1. 创建自定义提示弹框组件
首先,创建一个自定义提示弹框组件。
- BaseConfirm.vue
js
<template>
<div v-if="visible" alt="二次确认组件" :class="$options.name">
<div class="dialog-content">
<p>{{ message }}</p>
<button @click="stay">留在页面</button>
<button @click="leave">离开页面</button>
</div>
</div>
</template>
<script>
export default {
name: "BaseConfirm",
data() {
return {
visible: false,
message: '您确定要离开吗?'
};
},
methods: {
show() {
this.visible = true;
},
stay() {
this.visible = false;
},
leave() {
this.visible = false;
this.$emit('leave');
}
}
};
</script>
<style scoped>
.BaseConfirm {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.dialog-content {
background: white;
padding: 20px;
border-radius: 5px;
text-align: center;
}
button {
margin: 5px;
padding: 10px 20px;
cursor: pointer;
}
</style>
2. 在主组件中使用自定义提示弹框
在主组件中使用自定义提示弹框,并在 beforeRouteLeave
导航守卫中进行拦截。
- pages/index.vue
js
<template>
<div>
<BaseConfirm @leave="handleLeave" ref="customDialog" />
<h1>首页</h1>
<form @input="markFormAsDirty">
<input type="text" placeholder="输入内容" />
<button type="submit">提交</button>
</form>
</div>
</template>
<script>
import BaseConfirm from '~/components/BaseConfirm.vue';
export default {
components: {
BaseConfirm
},
data() {
return {
isFormDirty: false
};
},
methods: {
markFormAsDirty() {
this.isFormDirty = true;
},
handleLeave() {
this.isFormDirty = false;
this.$router.push(this.to); // 继续导航到目标路由
}
},
beforeRouteLeave(to, from, next) {
if (this.isFormDirty) {
this.to = to; // 保存目标路由
this.$refs.customDialog.show();
next(false); // 阻止路由导航
} else {
next(); // 允许路由导航
}
}
};
</script>
<style>
/* 页面样式 */
</style>
总结
-
BaseConfirm.vue:
- 创建一个自定义提示弹框组件,包含两个按钮:"留在页面"和"离开页面"。
- 提供
show
、stay
和leave
方法来控制弹框的显示和隐藏。
-
pages/index.vue:
- 引入并注册
BaseConfirm
组件。 - 在
data
中定义isFormDirty
来跟踪表单是否被修改。 - 在
methods
中定义markFormAsDirty
方法,当表单输入发生变化时,设置isFormDirty
为true
。 - 在
methods
中定义handleLeave
方法,当用户选择"离开页面"时,重置isFormDirty
并继续导航到目标路由。 - 在
beforeRouteLeave
导航守卫中,检查isFormDirty
是否为true
,如果是,则显示自定义提示弹框并阻止路由导航;否则,允许路由导航。
- 引入并注册
通过这种方式,你可以在用户尝试切换路由时显示一个自定义的提示弹框,并根据用户的操作决定是否继续导航。
局限性和解决方案
beforeRouteLeave
只能适用于路由发生改变,但是不关闭页面不重载页面的情况,如果用户主动重新载入页面或者关闭页面,导航守卫是捕获不到该事件的,这个时候就需要监听窗口的beforeunload
事件进行二次确认弹框处理
需要注意
使用方式
HTML规范建议 Event.preventDefault() 而非 Event.returnValue 的方式来提示用户。
因为兼容性的问题,两者需要同时使用
js
event.preventDefault();
event.returnValue = ''; // 防止默认对话框显示
returnValue返回值
当事件返回值不是null或者undefined时,用户将会被提醒是否离开页面。 (但测试后发现即便返回null或者undefined,弹出框也会被展示)。 如果不需要展示弹窗,不使用 Event.preventDefault(),Event.returnValue即可。 或者直接使用return的方式来兼容旧浏览器
js
function handleBeforeUnload(event) {
if (isFormDirty) {
const message = '您有未保存的更改,确定要离开吗?';
event.returnValue = message; // 标准做法
return message; // 兼容某些旧浏览器
}
}
returnValue自定义文本无效
Firefox 44
,Chrome 51
,Opera 38
,和Safari 9.1
以上不支持
在旧版本浏览器中,事件的返回值会被展示在对话框中。 但从Firefox 44,Chrome 51,Opera 38,和Safari 9.1以后,返回文本将不会被展示。
beforeunload
示例
js
<template>
<div id="app">
<BaseConfirm @leave="handleLeave" ref="customDialog" />
<!-- 其他组件内容 -->
</div>
</template>
<script>
import BaseConfirm from './components/BaseConfirm.vue';
export default {
components: {
BaseConfirm
},
data() {
return {
isFormDirty: false
};
},
created() {
// 监听表单变化,设置 isFormDirty 为 true
document.querySelector('form').addEventListener('input', () => {
this.isFormDirty = true;
});
// 监听 beforeunload 事件
window.addEventListener('beforeunload', this.handleBeforeUnload);
},
beforeDestroy() {
// 移除事件监听器
window.removeEventListener('beforeunload', this.handleBeforeUnload);
},
methods: {
handleBeforeUnload(event) {
if (this.isFormDirty) {
event.preventDefault();
event.returnValue = ''; // 防止默认对话框显示
this.$refs.customDialog.show();
}
},
handleLeave() {
this.isFormDirty = false;
window.location.reload(); // 重新加载页面
}
}
};
</script>
<style>
/* 全局样式 */
</style>
通过这种方式,你可以在用户尝试离开页面时显示一个自定义的提示弹框,并根据用户的操作决定是否真正离开页面。