Detect Back Button in Navigation Guards of Vue-Router
As stated by @Yuci, all the router hook callbacks are performed before popstate is updated (and therefore not helpful for this use case)
What you can do:
methods: {
navigate(location) {
this.internalNavigation = true;
this.$router.push(location, function () {
this.internalNavigation = false;
}.bind(this));
}
}
- Wrap 'router.push' with you own 'navigate' function
- Before calling router.push, set 'internalNavigation' flag to true
- Use vue router 'oncomplete' callback to set internalNavigation flag back to false
Now you can check the flag from within beforeEach callback and handle it accordingly.
router.beforeEach((to, from, next) => {
if ( this.internalNavigation ) {
//Do your stufff
}
next()
})
This is the only way that I've found:
We can listen for popstate, save it in a variable, and then check that variable
// This listener will execute before router.beforeEach only if registered
// before vue-router is registered with Vue.use(VueRouter)
window.popStateDetected = false
window.addEventListener('popstate', () => {
window.popStateDetected = true
})
router.beforeEach((to, from, next) => {
const IsItABackButton = window.popStateDetected
window.popStateDetected = false
if (IsItABackButton && from.meta.someLogica) {
next(false)
return ''
}
next()
})
Slight improvement to @yair-levy answer.
Wrapping push
to own navigate
method is not convenient because you usually want to call push
() from various places. Instead, router original methods can be patched in one place without changes in remaining code.
Following code is my Nuxt plugin to prevent navigation triggered by back/forward buttons (used in Electron app to avoid back caused by mouse additional "back" button, which makes mess in Electron app) Same principle can be used for vanilla Vue and to track common back button together with your custom handling.
export default ({ app }, inject) => {
// this is Nuxt stuff, in vanilla Vue use just your router intances
const { router } = app
let programmatic = false
;(['push', 'replace', 'go', 'back', 'forward']).forEach(methodName => {
const method = router[methodName]
router[methodName] = (...args) => {
programmatic = true
method.apply(router, args)
}
})
router.beforeEach((to, from, next) => {
// name is null for initial load or page reload
if (from.name === null || programmatic) {
// triggered bu router.push/go/... call
// route as usual
next()
} else {
// triggered by user back/forward
// do not route
next(false)
}
programmatic = false // clear flag
})
}