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>

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

相关推荐
寒雒11 分钟前
【Python】实战:实现GUI登录界面
开发语言·前端·python
独上归州18 分钟前
Vue与React的Suspense组件对比
前端·vue.js·react.js·suspense
Komorebi⁼25 分钟前
Vue核心特性解析(内含实践项目:设置购物车)
前端·javascript·vue.js·html·html5
明月清风徐徐26 分钟前
Vue实训---0-完成Vue开发环境的搭建
前端·javascript·vue.js
SameX29 分钟前
HarmonyOS Next 企业数据传输安全策略
前端·harmonyos
daopuyun29 分钟前
LoadRunner小贴士|开发Web-HTTP/HTML协议HTML5相关视频应用测试脚本的方法
前端·http·html
李先静32 分钟前
AWTK-WEB 快速入门(1) - C 语言应用程序
c语言·开发语言·前端
MR·Feng41 分钟前
使用Electron将vue2项目打包为桌面exe安装包
前端·javascript·electron
萧大侠jdeps1 小时前
图片生成视频-右进
前端·javascript·音视频
CSDN专家-赖老师(软件之家)1 小时前
养老院管理系统+小程序项目需求分析文档
vue.js·人工智能·小程序·mybatis·springboot