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>

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

相关推荐
编程猪猪侠17 分钟前
Tailwind CSS 自定义工具类与主题配置指南
前端·css
qhd吴飞21 分钟前
mybatis 差异更新法
java·前端·mybatis
YGY Webgis糕手之路42 分钟前
OpenLayers 快速入门(九)Extent 介绍
前端·经验分享·笔记·vue·web
患得患失9491 小时前
【前端】【vueDevTools】使用 vueDevTools 插件并修改默认打开编辑器
前端·编辑器
ReturnTrue8681 小时前
Vue路由状态持久化方案,优雅实现记住表单历史搜索记录!
前端·vue.js
UncleKyrie1 小时前
一个浏览器插件帮你查看Figma设计稿代码图片和转码
前端
遂心_1 小时前
深入解析前后端分离中的 /api 设计:从路由到代理的完整指南
前端·javascript·api
你听得到111 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构
风清云淡_A1 小时前
【REACT18.x】CRA+TS+ANTD5.X封装自定义的hooks复用业务功能
前端·react.js
@大迁世界1 小时前
第7章 React性能优化核心
前端·javascript·react.js·性能优化·前端框架