Vue项目在页面退出时弹窗确认 -vant组件Dialog弹窗在路由改变的时候不生效问题

2,682 阅读2分钟

需求:在离开某个页面时需要进行弹窗的二次确认。点击弹窗的确认按钮后离开,点取消不做任何离开操作。

实现方式:在beforeRouteLeave路由钩子中做拦截操作。

一:Dialog函数调用

  beforeRouteLeave(to, from, next) {
      // 下面这种方式,如果不加closeOnPopstate: false, 则会出现第一次生效。但是后面再进入页面再回退就不生效的问题。原因是路由改变,dialog弹窗自动关闭了导致用户无法确认,所以看vant官方文档发现 https://vant-contrib.gitee.io/vant/v2/#/zh-CN/dialog 常见问题有写到
    Dialog.confirm({
      title: "标题",
      message: "确定要离开此页面嘛",
      closeOnPopstate: false,
    })
      .then(() => {
        // on confirm
        next();
      })
      .catch(() => {
        // on cancel
        // cancelButton回调,把当前页路由推回
        // 不能使用this.$router进行路由操作,会触发beforeRouteLeave导致死循环
        window.history.pushState("", "", this.currentUrl);
      });
   
  }

二:Dialog组件调用

 //注意:一定要将closeOnPopstate属性设置为false,否则也不会生效。
  <van-dialog
      v-model="isShowDialog"
      title="标题"
      show-cancel-button
      @confirm="handleConfirm"
      :closeOnPopstate="false"
    >
      确定要离开此页面嘛
   </van-dialog>
 
 export default {
    data(){
     isConfirmClicked:false
    },
    methods:{
     handleConfirm() {
         //注意:下面2步一个都不能少。如果缺少变量,则点击确定后会触发beforeRouteLeave导致死循环,点击确认不停弹窗弹窗;如果缺少路由跳转逻辑,则会点击2次回退才会到上一级路由。第一次点击会弹窗,点击确定弹窗关闭,但是并不会到上一级路由;第二次点击浏览器回退,才会正常回到上一级路由。原因可以自己琢磨~~~
         this.isConfirmClicked = true
         this.$router.go(-1)
      },
    }
 }

 
  beforeRouteLeave(to, from, next) {
    if (this.isConfirmClicked) {
      next();
    } else {
      next(false);
      this.isShowDialog = true;
    }
  } 

上面2种方式亲测有效。关键在于closeOnPopstate属性的值一定要设置为true.否则离开了当前路由,弹窗就关闭了,导致无法再次弹窗。

一开始实现没有成功,以为是浏览器刷新机制的问题。所以也在页面写了个按钮测试,如下:

<van-cell @click="back">返回上一级</van-cell>
   //忘了这里为啥要这样写,好像是因为点击浏览器回退,路由变了,但是视图没有更新,还是在当前页面。没有再次复现。。。
   beforeRouteEnter(to, from, next) {
        next(() => {
          window.localStorage.setItem("lastRoute", from.path);
        });
   },

   back() {
       this.$router.go(-1)
      // this.$router.push(window.localStorage.getItem("lastRoute"));
    },

于是,想着改变浏览器默认的回退事件,像这样


 mounted() {
    if (window.history && window.history.pushState) {
      // 向历史记录中插入了当前页
      history.pushState(null, null, document.URL);
      window.addEventListener("popstate", this.goBack, false);
    }
  },
  destroyed() {
    //离开这个界面之后,删除事件
    window.removeEventListener("popstate", this.goBack, false);
  },
    goBack() {
      console.log("触发返回");
      //第一种方式:
      this.isShowDialog = true;
      
      //第二种方式:
      // Dialog.confirm({
      //   title: "标题",
      //   message: "确定要离开此页面嘛",
      // })
      //   .then(() => {
      //     // on confirm
      //     window.history.go(-1)
      //   })
      //   .catch(() => {
      //     // on cancel
      //     // cancelButton回调,把当前页路由推回
      //     // 不能使用this.$router进行路由操作,会触发beforeRouteLeave导致死循环
      //     window.history.pushState("", "", this.currentUrl);
      //   });
    },

下面这种方式哪怕不设置弹窗的closeOnPopstate属性也能生效。思考下是为什么把?

补充: vue监听浏览器原生返回按钮,进行路由转跳操作。注意不能直接在beforeRouteLeave中调用this.$router.psuh()方法,否则页面会进入死循环! www.yii666.com/blog/236119…