如何防止Vue中浏览器刷新、URL更改或路由导航

1,275 阅读1分钟

译文,原文作者Austin Gil,请确保网络能够访问Google

在Vue中,每隔一段时间,您就会希望防止用户改变路由或重新加载页面。例如,他们可能在没有保存的情况下对表单进行了更改。

这种行为我们避免不了,但通过技术阻止用户操作(例如刷新等)来说还是很容易实现的。

首先,我们需要一些条件来跟踪用户是否可以导航离开。但在这个例子中,我们会用一个布尔值来跟踪用户是否在编辑东西(下文的的例子在你的代码中实现可能不同,具体代码根据自身业务处理)

<script>
export default {
  data: () => ({
    isEditing: false
  })
</script>

防止URL更改和/或页面重新加载

接下来,我们需要添加一些逻辑,以防止用户跳转到新的URL,或在isEditing标志为true时重新加载页面。

幸运的是,浏览器有一个本地beforeunload事件。

把它添加到beforeMount钩子中,这样就知道我们不是在一个服务器渲染的环境中:

<script>
export default {
  // ...
  beforeMount() {
    window.addEventListener("beforeunload", event => {
      if (!this.isEditing) return
      event.preventDefault()
      // Chrome requires returnValue to be set.
      event.returnValue = ""
    })
  }
}
</script>

Vue在自动删除添加到模板中的任何事件处理程序方面做得很好,但是我们手工创建的任何事件处理程序都应该被清除,以避免任何内存泄漏。

为此,我们将匿名函数重构为一个命名方法,以便在beforeDestroy钩子中清理它:

<script>
export default {
  // ...
  beforeMount() {
    window.addEventListener("beforeunload", this.preventNav)
  },

  beforeDestroy() {
    window.removeEventListener("beforeunload", this.preventNav);
  },

  methods: {
    preventNav(event) {
      if (!this.isEditing) return
      event.preventDefault()
      event.returnValue = ""
    }
  }
}
</script>

如果愿意,还可以使用Vue的方法将事件侦听器逻辑组合在一起:

<script>
export default {
  // ...
  beforeMount() {
    window.addEventListener("beforeunload", this.preventNav)
    this.$once("hook:beforeDestroy", () => {
      window.removeEventListener("beforeunload", this.preventNav);
    })
  },

  methods: {
    preventNav(event) {
      if (!this.isEditing) return
      event.preventDefault()
      event.returnValue = ""
    }
  }
}
</script>

防止路由器跳转

太棒了!到目前为止,我们的组件将防止用户在浏览器发生更改时意外丢失更改,但您的路由更改实际上可能是由JavaScript处理的。如果是这种情况,还需要防止Vue路由器导航离开。

为此,我们可以方便地在beforeRouteLeave之前挂钩到组件内的导航保护(假设你正在使用)。

顾名思义,在您准备离开当前路由时运行beforeoutleave。它为我们提供了一些参数:

  • to:准备跳转(到)的路由对象
  • from:离开(当前)的路由对象
  • next:用于调用导航的函数。你也可以使用它来跳转到任何其他你喜欢的路由

就我们的目的而言,我们只对next参数感兴趣,我们可以将其与检查相结合,询问用户是否想要继续导航:

<script>
export default {
  // ...
  beforeRouteLeave(to, from, next) {
    if (this.isEditing) {
      if (!window.confirm("Leave without saving?")) {
        return;
      }
    }
    next();
  }
}
</script>

结束语

这样,我们就有了一个漂亮的小组件,它可以防止用户根据我们的逻辑进行导航。当然,我们实际上并没有实现任何逻辑,因为我将把它留给您来处理。

整个过程是这样的:

<script>
export default {
  data: () => ({
    isEditing: false
  }),

  beforeMount() {
    window.addEventListener("beforeunload", this.preventNav)
    this.$once("hook:beforeDestroy", () => {
      window.removeEventListener("beforeunload", this.preventNav);
    })
  },

  beforeRouteLeave(to, from, next) {
    if (this.isEditing) {
      if (!window.confirm("Leave without saving?")) {
        return;
      }
    }
    next();
  },

  methods: {
    preventNav(event) {
      if (!this.isEditing) return
      event.preventDefault()
      event.returnValue = ""
    },
  },
}
</script>

你可以在这里看到一个Demo示例(翻墙):

codesandbox.io/s/prevent-p…

如果需要,你可以将它做成mixin