目录

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>

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

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
坊钰几秒前
【MySQL 数据库】数据类型
java·开发语言·前端·数据库·学习·mysql·html
我是小路路呀18 分钟前
css 文字换行每一个字渐变
前端·css
谢小飞18 分钟前
Threejs全球坐标分布效果
前端·three.js
喝拿铁写前端18 分钟前
🚀从 0 到 1 构建字段推荐引擎:20+ 工具方法一文打尽!
前端
森叶23 分钟前
免费Deepseek-v3接口实现Browser-Use Web UI:浏览器自动化本地模拟抓取数据实录
前端·人工智能·自动化
拉不动的猪41 分钟前
刷刷题50(vue3)
前端·javascript·面试
胡八一1 小时前
使用 Less 实现 PC 和移动端样式适配
前端·css·less
加减法原则1 小时前
字节面试题之如何取消一个正在发送的请求
前端
ZSK61 小时前
【HTML】分享一个自己写的3*3拼图小游戏
前端·javascript·html
Yvette-W2 小时前
【JavaScript】原型链 prototype 和 this 关键字的练习(老虎机)
开发语言·前端·javascript·ecmascript·原型模式