Skip to content

Commit 214eb30

Browse files
committed
docs: add composition api page
1 parent 706e84f commit 214eb30

File tree

7 files changed

+135
-46
lines changed

7 files changed

+135
-46
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
## Know issues
1111

12+
<!-- TODO: put a link to the documentation instead -->
13+
1214
### Breaking changes compared to [email protected]
1315

1416
- The `mode: 'history'` option has been replaced with a more flexible one named `history`:

docs/.vitepress/config.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,18 @@ const config = {
9898
text: 'Navigation guards',
9999
},
100100
{ link: '/guide/advanced/meta', text: 'Route Meta Fields' },
101-
{
102-
link: '/guide/advanced/transitions',
103-
text: 'Transitions',
104-
},
105101
{
106102
link: '/guide/advanced/data-fetching',
107103
text: 'Data Fetching',
108104
},
105+
{
106+
link: '/guide/advanced/composition-api',
107+
text: 'Composition API',
108+
},
109+
{
110+
link: '/guide/advanced/transitions',
111+
text: 'Transitions',
112+
},
109113
{
110114
link: '/guide/advanced/scroll-behavior',
111115
text: 'Scroll Behavior',
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Vue Router and the Composition API
2+
3+
The introduction of `setup` and Vue's [Composition API](https://v3.vuejs.org/guide/composition-api-introduction.html), open up new possibilities but to be able to get the full potential out of Vue Router, we will need to use a few new functions to replace access to `this` and in-component navigation guards.
4+
5+
## Accessing the Router and current Route inside `setup`
6+
7+
Because we don't have access to `this` inside of `setup`, we cannot directly access `this.$router` or `this.$route` anymore. Instead we use the `useRouter` function:
8+
9+
```js
10+
export default {
11+
setup() {
12+
const router = useRouter()
13+
const route = useRoute()
14+
15+
function pushWithQuery(query) {
16+
router.push({
17+
name: 'search',
18+
query: {
19+
...this.route.query,
20+
},
21+
})
22+
}
23+
},
24+
}
25+
```
26+
27+
The `route` object is a reactive object, so any of its properties can be watched and you should **avoid watching the whole `route`** object:
28+
29+
```js
30+
export default {
31+
setup() {
32+
const route = useRoute()
33+
const userData = ref()
34+
35+
// fetch the user information when params change
36+
watch(
37+
() => route.params,
38+
async params => {
39+
userData.value = await fetchUser(newParams.id)
40+
}
41+
)
42+
},
43+
}
44+
```
45+
46+
Note we still have access to `$router` and `$route` in templates, so there is no need to return `router` or `route` inside of `setup`.
47+
48+
## Navigation Guards
49+
50+
While you can still use in-component navigation guards with a `setup` function, Vue Router exposes update and leave guards as Composition API functions:
51+
52+
```js
53+
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
54+
55+
export default {
56+
setup() {
57+
// same as beforeRouteLeave option with no access to `this`
58+
onBeforeRouteLeave((to, from) => {
59+
const answer = window.confirm(
60+
'Do you really want to leave? you have unsaved changes!'
61+
)
62+
// cancel the navigation and stay on the same page
63+
if (!answer) return false
64+
})
65+
66+
const userData = ref()
67+
68+
// same as beforeRouteUpdate option with no access to `this`
69+
onBeforeRouteUpdate(async (to, from) => {
70+
// only fetch the user if the id changed as maybe only the query or the hash changed
71+
if (to.params.id !== from.params.id) {
72+
userData.value = await fetchUser(to.params.id)
73+
}
74+
})
75+
},
76+
}
77+
```
78+
79+
<!-- TODO: useLink -->

docs/guide/advanced/navigation-guards.md

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -225,24 +225,7 @@ beforeRouteLeave (to, from) {
225225

226226
### Using the composition API
227227

228-
If you are writing your component using the [composition API and a `setup` function](https://v3.vuejs.org/guide/composition-api-setup.html#setup), you can add update and leave guards:
229-
230-
```js
231-
import { onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router'
232-
233-
const UserDetails = {
234-
template: `...`,
235-
setup() {
236-
onBeforeRouteUpdate((to, from) => {
237-
// same as the beforeRouteUpdate option but with no access to `this`
238-
})
239-
240-
onBeforeRouteLeave((to, from) => {
241-
// same as the beforeRouteLeave option but with no access to `this`
242-
})
243-
},
244-
}
245-
```
228+
If you are writing your component using the [composition API and a `setup` function](https://v3.vuejs.org/guide/composition-api-setup.html#setup), you can add update and leave guards through `onBeforeRouteUpdate` and `onBeforeRouteLeave` respectively. Please refer to the [Composition API section](./composition-api.md#navigation-guards) for more details.
246229

247230
## The Full Navigation Resolution Flow
248231

docs/guide/index.md

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const About = { template: '<div>About</div>' }
5050
// We'll talk about nested routes later.
5151
const routes = [
5252
{ path: '/', component: Home },
53-
{ path: '/about', component: About }
53+
{ path: '/about', component: About },
5454
]
5555

5656
// 3. Create the router instance and pass the `routes` option
@@ -59,7 +59,7 @@ const routes = [
5959
const router = VueRouter.createRouter({
6060
// 4. Provide the history implementation to use. We are using the hash history for simplicity here.
6161
history: VueRouter.createWebHashHistory(),
62-
routes // short for `routes: routes`
62+
routes, // short for `routes: routes`
6363
})
6464

6565
// 4. Create and mount the root instance.
@@ -82,13 +82,17 @@ export default {
8282
username() {
8383
// We will see what `params` is shortly
8484
return this.$route.params.username
85-
}
85+
},
8686
},
8787
methods: {
88-
goBack() {
89-
window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
90-
}
91-
}
88+
goToDashboard() {
89+
if (isAuthenticated) {
90+
this.$router.push('/dashboard')
91+
} else {
92+
this.$router.push('/login')
93+
}
94+
},
95+
},
9296
}
9397
```
9498

@@ -105,13 +109,19 @@ export default {
105109
const route = useRoute()
106110

107111
const username = computed(() => route.params.username)
108-
function goBack() {
109-
window.history.length > 1 ? router.go(-1) : router.push('/')
112+
function goToDashboard() {
113+
if (isAuthenticated) {
114+
router.push('/dashboard')
115+
} else {
116+
router.push('/login')
117+
}
110118
}
111119

112-
return { username, goBack }
113-
}
120+
return { username, goToDashboard }
121+
},
114122
}
115123
```
116124

125+
We will learn more about this in [the Composition API](/guide/advanced/composition-api.md)
126+
117127
Throughout the docs, we will often use the `router` instance. Keep in mind that `this.$router` is exactly the same as directly using the `router` instance created through `createRouter`. The reason we use `this.$router` is because we don't want to import the router in every single component that needs to manipulate routing.

docs/guide/migration/index.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ Some of new features to keep an eye on in Vue Router 4 include:
99
<!-- TODO: links -->
1010

1111
- Dynamic Routing
12-
- Composition API
12+
- [Composition API](/guide/advanced/composition-api.md)
1313
- Custom History implementation
1414

15-
## Breaking Changes
16-
17-
### Improvements
15+
## Breaking Changes: Improvements
1816

1917
The following changes should not be a problem for you but they are technically breaking changes that will show a different behavior and might break parts of your application.
2018

@@ -99,7 +97,9 @@ Given any [normalized route ___location](#TODO):
9997

10098
**Reason**: This allows to easily copy existing properties of a ___location when calling `router.push()` and `router.resolve()`. Learn more about encoding [in the cookbook](/cookbook/encoding.md).
10199

102-
### Changed API
100+
## Breaking Changes: API Changes
101+
102+
The following changes will require you to adapt your code
103103

104104
### New `history` option to replace `mode`
105105

@@ -166,7 +166,7 @@ router.resolve({
166166
}).href // '/not/found'
167167
```
168168

169-
**Reason**: Vue Router doesn't use `path-to-regexp` anymore, instead it implements its own parsing system that allows route ranking and enables dynamic routing.
169+
**Reason**: Vue Router doesn't use `path-to-regexp` anymore, instead it implements its own parsing system that allows route ranking and enables dynamic routing. Since we usually add one single catch-all route per project, there is no big benefit in supporting a special syntax for `*`.
170170

171171
### Removal of `router.match` and changes to `router.resolve`
172172

@@ -194,10 +194,19 @@ The `append` prop has been removed from `<router-link>`. You can manually concat
194194
replace
195195
<router-link to="child-route" append>to relative child</router-link>
196196
with
197-
<router-link :to="$route.path + '/child-route'">to relative child</router-link>
197+
<router-link :to="append($route.path, 'child-route')">
198+
to relative child
199+
</router-link>
200+
```
201+
202+
You must define a global `append` function on your _App_ instance:
203+
204+
```js
205+
app.config.globalProperties.append = (path, pathToAppend) =>
206+
path + (path.endsWith('/') ? '' : '/') + pathToAppend
198207
```
199208

200-
**Reason**: `append` wasn't used very often and is easy to replicate in user land.
209+
**Reason**: `append` wasn't used very often, is easy to replicate in user land.
201210

202211
### Removal of `event` and `tag` props in `<router-link>`
203212

@@ -270,7 +279,7 @@ The `pathToRegexpOptions` and `caseSensitive` properties of route records have b
270279

271280
### TypeScript
272281

273-
To make typings more consistent and expressive, some types have been renamed.
282+
To make typings more consistent and expressive, some types have been renamed:
274283

275284
| `vue-router@3` | `vue-router@4` |
276285
| -------------- | ----------------------- |

src/RouterLink.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { assign } from './utils'
1818

1919
export interface RouterLinkOptions {
2020
/**
21-
* Location the link should navigate to when clicked on.
21+
* Route Location the link should navigate to when clicked on.
2222
*/
2323
to: RouteLocationRaw
2424
/**
@@ -30,7 +30,8 @@ export interface RouterLinkOptions {
3030

3131
export interface RouterLinkProps extends RouterLinkOptions {
3232
/**
33-
* Whether RouterLink should not wrap its content in an `a` tag.
33+
* Whether RouterLink should not wrap its content in an `a` tag. Useful when
34+
* using `v-slot` to create a custom RouterLink
3435
*/
3536
custom?: boolean
3637
/**
@@ -81,8 +82,9 @@ export function useLink(props: UseLinkOptions) {
8182
return (
8283
// we are dealing with nested routes
8384
length > 1 &&
84-
// if the have the same path, this link is referring to the empty child
85-
// are we currently are on a different child of the same parent
85+
// if the parent and matched route have the same path, this link is
86+
// referring to the empty child. Or we currently are on a different
87+
// child of the same parent
8688
getOriginalPath(routeMatched) === parentRecordPath &&
8789
// avoid comparing the child with its parent
8890
currentMatched[currentMatched.length - 1].path !== parentRecordPath

0 commit comments

Comments
 (0)