Skip to content

Commit

Permalink
feat: onBeforeRouteUpdatee onBeforeRouteLeave
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Aug 22, 2022
1 parent 5bae10d commit 9861c55
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 24 deletions.
44 changes: 36 additions & 8 deletions examples/composables/app.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
import Vue, { defineComponent, watch, ref, onUnmounted } from 'vue'
import Vue, { defineComponent, watch, ref } from 'vue'
import VueRouter from 'vue-router'
import { useRoute, useRouter } from 'vue-router/composables'
import { onBeforeRouteLeave, onBeforeRouteUpdate } from '../../src/composables'

Vue.use(VueRouter)

const Foo = defineComponent({
setup () {
const route = useRoute()
onBeforeRouteUpdate((to, from, next) => {
console.log('Foo updating')
next()
})
onBeforeRouteLeave((to, from, next) => {
console.log('Foo leaving')
next()
})
return { route }
},
template: `
<div>
<h3>Foo</h3>
{{ route.fullPath }}
</div>
`
})

const Home = defineComponent({
setup () {
const route = useRoute()
Expand All @@ -12,19 +34,22 @@ const Home = defineComponent({
// should be /
const startRoute = route.fullPath

console.log('got to Home', startRoute)
onBeforeRouteUpdate((to, from, next) => {
console.log('Home updating')
next()
})

onBeforeRouteLeave((to, from, next) => {
console.log('Home leaving')
next()
})

const watchCount = ref(0)

watch(() => route.query.n, () => {
console.log('watched')
watchCount.value++
})

onUnmounted(() => {
console.log('unmounted')
})

function navigate () {
router.push({ query: { n: 1 + (Number(route.query.n) || 0) }})
}
Expand All @@ -37,8 +62,11 @@ const Home = defineComponent({
<p id='watch-count'>{{ watchCount }}</p>
<p id="fullpath">{{ route.fullPath }}</p>
<button id="nav" @click="navigate">Navigate</button>
<hr>
<Foo />
</div>
`
`,
components: { Foo }
})

const About = defineComponent({
Expand Down
72 changes: 56 additions & 16 deletions src/composables/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { getCurrentInstance, shallowReactive, effectScope, onUnmounted } from 'vue'
import {
getCurrentInstance,
shallowReactive,
effectScope,
onUnmounted
} from 'vue'

export function useRouter () {
const i = getCurrentInstance()
Expand All @@ -17,8 +22,8 @@ export function useRoute () {

const root = i.proxy.$root
if (!root._$route) {
const route = effectScope(true).run(
() => shallowReactive(Object.assign({}, root.$router.currentRoute))
const route = effectScope(true).run(() =>
shallowReactive(Object.assign({}, root.$router.currentRoute))
)
root._$route = route

Expand All @@ -39,33 +44,68 @@ export function onBeforeRouteUpdate (guard) {
throwNoCurrentInstance('onBeforeRouteUpdate')
}

return useFilteredGuard(guard, isUpdateNavigation)
}

function useFilteredGuard (guard, fn) {
const i = getCurrentInstance()
const router = useRouter()

let target = i.proxy
// find the nearest routerview to know the depth
while (target && target.$vnode && target.$vnode.data && target.$vnode.data.routerViewDepth == null) {
while (
target &&
target.$vnode &&
target.$vnode.data &&
target.$vnode.data.routerViewDepth == null
) {
target = target.$parent
}

const depth = target && target.$vnode && target.$vnode.data ? target.$vnode.data.routerViewDepth : null
const depth =
target && target.$vnode && target.$vnode.data
? target.$vnode.data.routerViewDepth
: null

console.log('found depth', depth)
if (depth != null) {
const removeGuard = router.beforeEach((to, from, next) => {
return fn(to, from, depth) ? guard(to, from, next) : next()
})

// TODO: allow multiple guards?
i.proxy.$options.beforeRouteUpdate = guard
onUnmounted(removeGuard)
return removeGuard
}

const removeGuard = router.beforeEach((to, from, next) => {
// TODO: check it's an update
return guard(to, from, next)
})
return noop
}

onUnmounted(removeGuard)
function isUpdateNavigation (to, from, depth) {
const toMatched = to.matched
const fromMatched = from.matched
return (
toMatched.length >= depth &&
toMatched
.slice(0, depth + 1)
.every((record, i) => record === fromMatched[i])
)
}

return removeGuard
function isLeaveNavigation (to, from, depth) {
const toMatched = to.matched
const fromMatched = from.matched
return toMatched.length < depth || toMatched[depth] !== fromMatched[depth]
}

// TODO:
// export function onBeforeRouteLeave () {}
const noop = () => {}

export function onBeforeRouteLeave (guard) {
const i = getCurrentInstance()
if (process.env.NODE_ENV !== 'production' && !i) {
throwNoCurrentInstance('onBeforeRouteLeave')
}

return useFilteredGuard(guard, isLeaveNavigation)
}

function throwNoCurrentInstance (method) {
throw new Error(
Expand Down

0 comments on commit 9861c55

Please sign in to comment.