Vue:如何在路由发生变化前阻止并提示用户

前言

项目中,产品经理往往会提这么一个需求:

  • 我设计了一个表单表单,表单有各种输入项,这些内容非常重要,一定要让用户填写完整,如果用户没有填写完整,中途退出的话,一定要弹框提示用户是否不保留已输入的内容继续离开

  • 我设计了一个考试系统,如果考生没有作答完成,或者提前关闭(刷新)浏览器,都需要提示考试这样会导致成绩作废

应对这些类似的需求,在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>

总结

  1. BaseConfirm.vue

    • 创建一个自定义提示弹框组件,包含两个按钮:"留在页面"和"离开页面"。
    • 提供 showstayleave 方法来控制弹框的显示和隐藏。
  2. pages/index.vue

    • 引入并注册 BaseConfirm 组件。
    • data 中定义 isFormDirty 来跟踪表单是否被修改。
    • methods 中定义 markFormAsDirty 方法,当表单输入发生变化时,设置 isFormDirtytrue
    • 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 44Chrome 51Opera 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>

通过这种方式,你可以在用户尝试离开页面时显示一个自定义的提示弹框,并根据用户的操作决定是否真正离开页面。

相关推荐
apcipot_rain36 分钟前
【应用密码学】实验五 公钥密码2——ECC
前端·数据库·python
ShallowLin42 分钟前
vue3学习——组合式 API:生命周期钩子
前端·javascript·vue.js
Nejosi_念旧1 小时前
Vue API 、element-plus自动导入插件
前端·javascript·vue.js
互联网搬砖老肖1 小时前
Web 架构之攻击应急方案
前端·架构
pixle02 小时前
Vue3 Echarts 3D饼图(3D环形图)实现讲解附带源码
前端·3d·echarts
麻芝汤圆2 小时前
MapReduce 入门实战:WordCount 程序
大数据·前端·javascript·ajax·spark·mapreduce
juruiyuan1114 小时前
FFmpeg3.4 libavcodec协议框架增加新的decode协议
前端
Peter 谭4 小时前
React Hooks 实现原理深度解析:从基础到源码级理解
前端·javascript·react.js·前端框架·ecmascript
LuckyLay6 小时前
React百日学习计划——Deepseek版
前端·学习·react.js
gxn_mmf6 小时前
典籍知识问答重新生成和消息修改Bug修改
前端·bug