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>

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

相关推荐
蟾宫曲2 小时前
在 Vue3 项目中实现计时器组件的使用(Vite+Vue3+Node+npm+Element-plus,附测试代码)
前端·npm·vue3·vite·element-plus·计时器
秋雨凉人心2 小时前
简单发布一个npm包
前端·javascript·webpack·npm·node.js
liuxin334455662 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
qq13267029402 小时前
运行Zr.Admin项目(前端)
前端·vue2·zradmin前端·zradmin vue·运行zradmin·vue2版本zradmin
LCG元3 小时前
Vue.js组件开发-使用vue-pdf显示PDF
vue.js
魏时烟4 小时前
css文字折行以及双端对齐实现方式
前端·css
哥谭居民00014 小时前
将一个组件的propName属性与父组件中的variable变量进行双向绑定的vue3(组件传值)
javascript·vue.js·typescript·npm·node.js·css3
烟波人长安吖~5 小时前
【目标跟踪+人流计数+人流热图(Web界面)】基于YOLOV11+Vue+SpringBoot+Flask+MySQL
vue.js·pytorch·spring boot·深度学习·yolo·目标跟踪
2401_882726485 小时前
低代码配置式组态软件-BY组态
前端·物联网·低代码·前端框架·编辑器·web
web130933203985 小时前
ctfshow-web入门-文件包含(web82-web86)条件竞争实现session会话文件包含
前端·github