Skip to content

Commit ed0a108

Browse files
committed
refactor(guards): remove this access on composition guards
BREAKING CHANGE: `onBeforeRouteLeave` and `onBeforeRouteUpdate` used to have access to the component instance through `instance.proxy` but given that: 1. It has been marked as `internal` (vuejs/core#1849) 2. When using `setup`, all variables are accessible on the scope (and should be accessed that way because the code minimizes better) It has been removed to prevent wrong usage and lighten Vue Router
1 parent 14db2aa commit ed0a108

File tree

4 files changed

+18
-132
lines changed

4 files changed

+18
-132
lines changed

__tests__/guards/onBeforeRouteLeave.spec.ts

+1-49
Original file line numberDiff line numberDiff line change
@@ -13,58 +13,10 @@ const component = {
1313
}
1414

1515
describe('onBeforeRouteLeave', () => {
16-
it('invokes with the component context', async () => {
17-
expect.assertions(2)
18-
const spy = jest
19-
.fn()
20-
.mockImplementationOnce(function (this: any, to, from, next) {
21-
expect(typeof this.counter).toBe('number')
22-
next()
23-
})
24-
const WithLeave = defineComponent({
25-
template: `text`,
26-
// we use data to check if the context is the right one because saving `this` in a variable logs a few warnings
27-
data: () => ({ counter: 0 }),
28-
setup() {
29-
onBeforeRouteLeave(spy)
30-
},
31-
})
32-
33-
const router = createRouter({
34-
history: createMemoryHistory(),
35-
routes: [
36-
{ path: '/', component },
37-
{ path: '/leave', component: WithLeave as any },
38-
],
39-
})
40-
const app = createApp({
41-
template: `
42-
<router-view />
43-
`,
44-
})
45-
app.use(router)
46-
const rootEl = document.createElement('div')
47-
document.body.appendChild(rootEl)
48-
app.mount(rootEl)
49-
50-
await router.isReady()
51-
await router.push('/leave')
52-
await router.push('/')
53-
expect(spy).toHaveBeenCalledTimes(1)
54-
})
55-
5616
it('removes guards when leaving the route', async () => {
57-
expect.assertions(3)
58-
const spy = jest
59-
.fn()
60-
.mockImplementation(function (this: any, to, from, next) {
61-
expect(typeof this.counter).toBe('number')
62-
next()
63-
})
17+
const spy = jest.fn()
6418
const WithLeave = defineComponent({
6519
template: `text`,
66-
// we use data to check if the context is the right one because saving `this` in a variable logs a few warnings
67-
data: () => ({ counter: 0 }),
6820
setup() {
6921
onBeforeRouteLeave(spy)
7022
},

__tests__/guards/onBeforeRouteUpdate.spec.ts

+1-49
Original file line numberDiff line numberDiff line change
@@ -13,58 +13,10 @@ const component = {
1313
}
1414

1515
describe('onBeforeRouteUpdate', () => {
16-
it('invokes with the component context', async () => {
17-
expect.assertions(2)
18-
const spy = jest
19-
.fn()
20-
.mockImplementationOnce(function (this: any, to, from, next) {
21-
expect(typeof this.counter).toBe('number')
22-
next()
23-
})
24-
const WithLeave = defineComponent({
25-
template: `text`,
26-
// we use data to check if the context is the right one because saving `this` in a variable logs a few warnings
27-
data: () => ({ counter: 0 }),
28-
setup() {
29-
onBeforeRouteUpdate(spy)
30-
},
31-
})
32-
33-
const router = createRouter({
34-
history: createMemoryHistory(),
35-
routes: [
36-
{ path: '/', component },
37-
{ path: '/foo', component: WithLeave as any },
38-
],
39-
})
40-
const app = createApp({
41-
template: `
42-
<router-view />
43-
`,
44-
})
45-
app.use(router)
46-
const rootEl = document.createElement('div')
47-
document.body.appendChild(rootEl)
48-
app.mount(rootEl)
49-
50-
await router.isReady()
51-
await router.push('/foo')
52-
await router.push('/foo?q')
53-
expect(spy).toHaveBeenCalledTimes(1)
54-
})
55-
5616
it('removes update guards when leaving', async () => {
57-
expect.assertions(3)
58-
const spy = jest
59-
.fn()
60-
.mockImplementation(function (this: any, to, from, next) {
61-
expect(typeof this.counter).toBe('number')
62-
next()
63-
})
17+
const spy = jest.fn()
6418
const WithLeave = defineComponent({
6519
template: `text`,
66-
// we use data to check if the context is the right one because saving `this` in a variable logs a few warnings
67-
data: () => ({ counter: 0 }),
6820
setup() {
6921
onBeforeRouteUpdate(spy)
7022
},

e2e/multi-app/index.ts

+1-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createRouter, createWebHistory, onBeforeRouteUpdate } from '../../src'
1+
import { createRouter, createWebHistory } from '../../src'
22
import { RouteComponent } from '../../src/types'
33
import { createApp, ref, watchEffect, App, inject } from 'vue'
44

@@ -26,15 +26,6 @@ const User: RouteComponent = {
2626
setup() {
2727
const id = inject('id')!
2828

29-
if (id !== 1)
30-
onBeforeRouteUpdate(function (to, from, next) {
31-
// @ts-ignore
32-
console.log('update from ', id, this.id)
33-
// @ts-ignore
34-
// this.count++
35-
next()
36-
})
37-
3829
return { id }
3930
},
4031
}

src/navigationGuards.ts

+15-24
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ import { matchedRouteKey } from './injectionSymbols'
2323
import { RouteRecordNormalized } from './matcher/types'
2424
import { isESModule } from './utils'
2525

26+
function registerGuard(list: NavigationGuard[], guard: NavigationGuard) {
27+
onUnmounted(() => {
28+
const index = list.indexOf(guard)
29+
if (index > -1) list.splice(index, 1)
30+
})
31+
32+
list.push(guard)
33+
}
34+
2635
/**
2736
* Add a navigation guard that triggers whenever the current location is
2837
* left. Similarly to {@link beforeRouteLeave}, it has access to the
@@ -31,10 +40,8 @@ import { isESModule } from './utils'
3140
* @param leaveGuard - {@link NavigationGuard}
3241
*/
3342
export function onBeforeRouteLeave(leaveGuard: NavigationGuard) {
34-
const instance = getCurrentInstance()
35-
if (!instance) {
36-
__DEV__ &&
37-
warn('onBeforeRouteLeave must be called at the top of a setup function')
43+
if (__DEV__ && !getCurrentInstance()) {
44+
warn('onBeforeRouteLeave must be called at the top of a setup function')
3845
return
3946
}
4047

@@ -49,14 +56,7 @@ export function onBeforeRouteLeave(leaveGuard: NavigationGuard) {
4956
return
5057
}
5158

52-
// @ts-ignore do we even want to allow that? Passing the context in a composition api hook doesn't make sense
53-
const fn = leaveGuard.bind(instance.proxy)
54-
onUnmounted(() => {
55-
const index = activeRecord.leaveGuards.indexOf(fn)
56-
if (index > -1) activeRecord.leaveGuards.splice(index, 1)
57-
})
58-
59-
activeRecord.leaveGuards.push(fn)
59+
registerGuard(activeRecord.leaveGuards, leaveGuard)
6060
}
6161

6262
/**
@@ -67,10 +67,8 @@ export function onBeforeRouteLeave(leaveGuard: NavigationGuard) {
6767
* @param updateGuard - {@link NavigationGuard}
6868
*/
6969
export function onBeforeRouteUpdate(updateGuard: NavigationGuard) {
70-
const instance = getCurrentInstance()
71-
if (!instance) {
72-
__DEV__ &&
73-
warn('onBeforeRouteUpdate must be called at the top of a setup function')
70+
if (__DEV__ && !getCurrentInstance()) {
71+
warn('onBeforeRouteUpdate must be called at the top of a setup function')
7472
return
7573
}
7674

@@ -85,14 +83,7 @@ export function onBeforeRouteUpdate(updateGuard: NavigationGuard) {
8583
return
8684
}
8785

88-
// @ts-ignore do we even want to allow that? Passing the context in a composition api hook doesn't make sense
89-
const fn = updateGuard.bind(instance.proxy)
90-
onUnmounted(() => {
91-
const index = activeRecord.updateGuards.indexOf(fn)
92-
if (index > -1) activeRecord.updateGuards.splice(index, 1)
93-
})
94-
95-
activeRecord.updateGuards.push(fn)
86+
registerGuard(activeRecord.updateGuards, updateGuard)
9687
}
9788

9889
export function guardToPromiseFn(

0 commit comments

Comments
 (0)