<VueSchoolLink
href="https://vueschool.io/lessons/router-and-the-composition-api"
- title="Learn how to use Vue Router with the composition API"
+ title="Learn how to use Vue Router with the Composition API"
/>
-The introduction of `setup` and Vue's [Composition API](https://vuejs.org/guide/extras/composition-api-faq.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.
+The introduction of Vue's [Composition API](https://vuejs.org/guide/extras/composition-api-faq.html) opened 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.
## Accessing the Router and current Route inside `setup`
-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` and `useRoute` functions:
+Because we don't have access to `this` inside of `setup`, we cannot directly access `this.$router` or `this.$route`. Instead, we use the `useRouter` and `useRoute` composables:
-```js
+```vue
+<script setup>
import { useRouter, useRoute } from 'vue-router'
-export default {
- setup() {
- const router = useRouter()
- const route = useRoute()
-
- function pushWithQuery(query) {
- router.push({
- name: 'search',
- query: {
- ...route.query,
- ...query,
- },
- })
- }
- },
+const router = useRouter()
+const route = useRoute()
+
+function pushWithQuery(query) {
+ router.push({
+ name: 'search',
+ query: {
+ ...route.query,
+ ...query,
+ },
+ })
}
+</script>
```
-The `route` object is a reactive object, so any of its properties can be watched and you should **avoid watching the whole `route`** object. In most scenarios, you should directly watch the param you are expecting to change
+The `route` object is a reactive object. In most scenarios, you should **avoid watching the whole `route`** object. Instead, you can directly watch the properties you are expecting to change:
-```js
+```vue
+<script setup>
import { useRoute } from 'vue-router'
import { ref, watch } from 'vue'
-export default {
- setup() {
- const route = useRoute()
- const userData = ref()
-
- // fetch the user information when params change
- watch(
- () => route.params.id,
- async newId => {
- userData.value = await fetchUser(newId)
- }
- )
- },
-}
+const route = useRoute()
+const userData = ref()
+
+// fetch the user information when params change
+watch(
+ () => route.params.id,
+ async newId => {
+ userData.value = await fetchUser(newId)
+ }
+)
+</script>
```
-Note we still have access to `$router` and `$route` in templates, so there is no need to return `router` or `route` inside of `setup`.
+Note we still have access to `$router` and `$route` in templates, so there's no need to use `useRouter` or `useRoute` if we only need those object in the template.
## Navigation Guards
-While you can still use in-component navigation guards with a `setup` function, Vue Router exposes update and leave guards as Composition API functions:
+Vue Router exposes update and leave guards as Composition API functions:
-```js
+```vue
+<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'
-export default {
- setup() {
- // same as beforeRouteLeave option with no access to `this`
- onBeforeRouteLeave((to, from) => {
- const answer = window.confirm(
- 'Do you really want to leave? you have unsaved changes!'
- )
- // cancel the navigation and stay on the same page
- if (!answer) return false
- })
-
- const userData = ref()
-
- // same as beforeRouteUpdate option with no access to `this`
- onBeforeRouteUpdate(async (to, from) => {
- // only fetch the user if the id changed as maybe only the query or the hash changed
- if (to.params.id !== from.params.id) {
- userData.value = await fetchUser(to.params.id)
- }
- })
- },
-}
+// same as beforeRouteLeave option but with no access to `this`
+onBeforeRouteLeave((to, from) => {
+ const answer = window.confirm(
+ 'Do you really want to leave? you have unsaved changes!'
+ )
+ // cancel the navigation and stay on the same page
+ if (!answer) return false
+})
+
+const userData = ref()
+
+// same as beforeRouteUpdate option but with no access to `this`
+onBeforeRouteUpdate(async (to, from) => {
+ // only fetch the user if the id changed as maybe only the query or the hash changed
+ if (to.params.id !== from.params.id) {
+ userData.value = await fetchUser(to.params.id)
+ }
+})
+</script>
```
Composition API guards can also be used in any component rendered by `<router-view>`, they don't have to be used directly on the route component like in-component guards.
Vue Router exposes the internal behavior of RouterLink as a composable. It accepts a reactive object like the props of `RouterLink` and exposes low-level properties to build your own `RouterLink` component or generate custom links:
-```js
+```vue
+<script setup>
import { RouterLink, useLink } from 'vue-router'
import { computed } from 'vue'
-export default {
- name: 'AppLink',
-
- props: {
- // add @ts-ignore if using TypeScript
- ...RouterLink.props,
- inactiveClass: String,
- },
-
- setup(props) {
- const {
- // the resolved route object
- route,
- // the href to use in a link
- href,
- // boolean ref indicating if the link is active
- isActive,
- // boolean ref indicating if the link is exactly active
- isExactActive,
- // function to navigate to the link
- navigate
- } = useLink(props)
-
- const isExternalLink = computed(
- () => typeof props.to === 'string' && props.to.startsWith('http')
- )
-
- return { isExternalLink, href, navigate, isActive }
- },
-}
+const props = defineProps({
+ // add @ts-ignore if using TypeScript
+ ...RouterLink.props,
+ inactiveClass: String,
+})
+
+const {
+ // the resolved route object
+ route,
+ // the href to use in a link
+ href,
+ // boolean ref indicating if the link is active
+ isActive,
+ // boolean ref indicating if the link is exactly active
+ isExactActive,
+ // function to navigate to the link
+ navigate
+} = useLink(props)
+
+const isExternalLink = computed(
+ () => typeof props.to === 'string' && props.to.startsWith('http')
+)
+</script>
```
Note that the RouterLink's `v-slot` gives access to the same properties as the `useLink` composable.
## Fetching After Navigation
-When using this approach, we navigate and render the incoming component immediately, and fetch data in the component's `created` hook. It gives us the opportunity to display a loading state while the data is being fetched over the network, and we can also handle loading differently for each view.
+When using this approach, we navigate and render the incoming component immediately, and fetch data in the component itself. It gives us the opportunity to display a loading state while the data is being fetched over the network, and we can also handle loading differently for each view.
-Let's assume we have a `Post` component that needs to fetch the data for a post based on `$route.params.id`:
+Let's assume we have a `Post` component that needs to fetch the data for a post based on `route.params.id`:
-```html
+::: code-group
+
+```vue [Composition API]
<template>
<div class="post">
<div v-if="loading" class="loading">Loading...</div>
</div>
</div>
</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+import { useRoute } from 'vue-router'
+import { getPost } from './api.js'
+
+const route = useRoute()
+
+const loading = ref(false)
+const post = ref(null)
+const error = ref(null)
+
+// watch the params of the route to fetch the data again
+watch(() => route.params.id, fetchData, { immediate: true })
+
+async function fetchData(id) {
+ error.value = post.value = null
+ loading.value = true
+
+ try {
+ // replace `getPost` with your data fetching util / API wrapper
+ post.value = await getPost(id)
+ } catch (err) {
+ error.value = err.toString()
+ } finally {
+ loading.value = false
+ }
+}
+</script>
```
-```js
+```vue [Options API]
+<template>
+ <div class="post">
+ <div v-if="loading" class="loading">Loading...</div>
+
+ <div v-if="error" class="error">{{ error }}</div>
+
+ <div v-if="post" class="content">
+ <h2>{{ post.title }}</h2>
+ <p>{{ post.body }}</p>
+ </div>
+ </div>
+</template>
+
+<script>
+import { getPost } from './api.js'
+
export default {
data() {
return {
created() {
// watch the params of the route to fetch the data again
this.$watch(
- () => this.$route.params,
- () => {
- this.fetchData()
- },
+ () => this.$route.params.id,
+ this.fetchData,
// fetch the data when the view is created and the data is
// already being observed
{ immediate: true }
)
},
methods: {
- fetchData() {
+ async fetchData(id) {
this.error = this.post = null
this.loading = true
- // replace `getPost` with your data fetching util / API wrapper
- getPost(this.$route.params.id, (err, post) => {
+
+ try {
+ // replace `getPost` with your data fetching util / API wrapper
+ this.post = await getPost(id)
+ } catch (err) {
+ this.error = err.toString()
+ } finally {
this.loading = false
- if (err) {
- this.error = err.toString()
- } else {
- this.post = post
- }
- })
+ }
},
},
}
+</script>
```
+:::
+
## Fetching Before Navigation
-With this approach we fetch the data before actually navigating to the new
-route. We can perform the data fetching in the `beforeRouteEnter` guard in the incoming component, and only call `next` when the fetch is complete. The callback passed to `next` will be called **after the component is mounted**:
+With this approach we fetch the data before actually navigating to the new route. We can perform the data fetching in the `beforeRouteEnter` guard in the incoming component, and only call `next` when the fetch is complete. The callback passed to `next` will be called **after the component is mounted**:
```js
export default {
error: null,
}
},
- beforeRouteEnter(to, from, next) {
- getPost(to.params.id, (err, post) => {
- // `setData` is a method defined below
- next(vm => vm.setData(err, post))
- })
+ async beforeRouteEnter(to, from, next) {
+ try {
+ const post = await getPost(to.params.id)
+ // `setPost` is a method defined below
+ next(vm => vm.setPost(post))
+ } catch (err) {
+ // `setError` is a method defined below
+ next(vm => vm.setError(err))
+ }
},
// when route changes and this component is already rendered,
// the logic will be slightly different.
- async beforeRouteUpdate(to, from) {
+ beforeRouteUpdate(to, from) {
this.post = null
- try {
- this.post = await getPost(to.params.id)
- } catch (error) {
- this.error = error.toString()
- }
+ getPost(to.params.id).then(this.setPost).catch(this.setError)
},
methods: {
- setData(error, post) {
- if (error) {
- this.error = error
- } else {
- this.post = post
- }
+ setPost(post) {
+ this.post = post
+ },
+ setError(err) {
+ this.error = err.toString()
}
}
}
```js
router.addRoute({ path: '/about', component: About })
-// we could also use this.$route or route = useRoute() (inside a setup)
+// we could also use this.$route or useRoute()
router.replace(router.currentRoute.value.fullPath)
```
Let's extend RouterLink to handle external links as well and adding a custom `inactive-class` in an `AppLink.vue` file:
-```vue
+::: code-group
+
+```vue [Composition API]
+<script setup>
+import { computed } from 'vue'
+import { RouterLink } from 'vue-router'
+
+defineOptions({
+ inheritAttrs: false,
+})
+
+const props = defineProps({
+ // add @ts-ignore if using TypeScript
+ ...RouterLink.props,
+ inactiveClass: String,
+})
+
+const isExternalLink = computed(() => {
+ return typeof props.to === 'string' && props.to.startsWith('http')
+})
+</script>
+
<template>
<a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank">
<slot />
</a>
</router-link>
</template>
+```
+```vue [Options API]
<script>
import { RouterLink } from 'vue-router'
},
}
</script>
+
+<template>
+ <a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank">
+ <slot />
+ </a>
+ <router-link
+ v-else
+ v-bind="$props"
+ custom
+ v-slot="{ isActive, href, navigate }"
+ >
+ <a
+ v-bind="$attrs"
+ :href="href"
+ @click="navigate"
+ :class="isActive ? activeClass : inactiveClass"
+ >
+ <slot />
+ </a>
+ </router-link>
+</template>
```
+:::
+
If you prefer using a render function or create `computed` properties, you can use the `useLink` from the [Composition API](./composition-api.md):
```js
For example, with the above route config, the URL `/posts/new` will match both the parent route record (`path: '/posts'`) and the child route record (`path: 'new'`).
-All route records matched by a route are exposed on the `$route` object (and also route objects in navigation guards) as the `$route.matched` Array. We could loop through that array to check all `meta` fields, but Vue Router also provides you a `$route.meta` that is a non-recursive merge of **all `meta`** fields from parent to child. Meaning you can simply write
+All route records matched by a route are exposed on the `route` object (and also route objects in navigation guards) as the `route.matched` Array. We could loop through that array to check all `meta` fields, but Vue Router also provides you a `route.meta` that is a non-recursive merge of **all `meta`** fields from parent to child. Meaning you can simply write:
```js
router.beforeEach((to, from) => {
Finally, you can directly define route navigation guards inside route components (the ones passed to the router configuration)
-### Using the options API
+### Using the Options API
You can add the following options to route components:
- `beforeRouteUpdate`
- `beforeRouteLeave`
-```js
-const UserDetails = {
- template: `...`,
+```vue
+<script>
+export default {
beforeRouteEnter(to, from) {
// called before the route that renders this component is confirmed.
// does NOT have access to `this` component instance,
// As with `beforeRouteUpdate`, it has access to `this` component instance.
},
}
+</script>
```
The `beforeRouteEnter` guard does **NOT** have access to `this`, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet.
}
```
-### Using the composition API
+### Using the Composition API
-If you are writing your component using the [composition API and a `setup` function](https://vuejs.org/api/composition-api-setup.html), 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.
+If you are writing your component using the Composition API, 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.
## The Full Navigation Resolution Flow
Very often we will need to map routes with the given pattern to the same component. For example, we may have a `User` component which should be rendered for all users but with different user IDs. In Vue Router we can use a dynamic segment in the path to achieve that, we call that a _param_:
```js
-const User = {
- template: '<div>User</div>',
-}
+import User from './User.vue'
// these are passed to `createRouter`
const routes = [
Now URLs like `/users/johnny` and `/users/jolyne` will both map to the same route.
-A _param_ is denoted by a colon `:`. When a route is matched, the value of its _params_ will be exposed as `this.$route.params` in every component. Therefore, we can render the current user ID by updating `User`'s template to this:
+A _param_ is denoted by a colon `:`. When a route is matched, the value of its _params_ will be exposed as `route.params` in every component. Therefore, we can render the current user ID by updating `User`'s template to this:
-```js
-const User = {
- template: '<div>User {{ $route.params.id }}</div>',
-}
+```vue
+<template>
+ <div>
+ <!-- The current route is accessible as $route in the template -->
+ User {{ $route.params.id }}
+ </div>
+</template>
```
-You can have multiple _params_ in the same route, and they will map to corresponding fields on `$route.params`. Examples:
+You can have multiple _params_ in the same route, and they will map to corresponding fields on `route.params`. Examples:
-| pattern | matched path | \$route.params |
-| ------------------------------ | ------------------------ | ---------------------------------------- |
-| /users/:username | /users/eduardo | `{ username: 'eduardo' }` |
+| pattern | matched path | route.params |
+| ------------------------------ | ------------------------ | -------------------------------------- |
+| /users/:username | /users/eduardo | `{ username: 'eduardo' }` |
| /users/:username/posts/:postId | /users/eduardo/posts/123 | `{ username: 'eduardo', postId: '123' }` |
-In addition to `$route.params`, the `$route` object also exposes other useful information such as `$route.query` (if there is a query in the URL), `$route.hash`, etc. You can check out the full details in the [API Reference](../../api/interfaces/RouteLocationNormalized.md).
+In addition to `route.params`, the `route` object also exposes other useful information such as `route.query` (if there is a query in the URL), `route.hash`, etc. You can check out the full details in the [API Reference](../../api/interfaces/RouteLocationNormalized.md).
A working demo of this example can be found [here](https://codesandbox.io/s/route-params-vue-router-examples-mlb14?from-embed&initialpath=%2Fusers%2Feduardo%2Fposts%2F1).
One thing to note when using routes with params is that when the user navigates from `/users/johnny` to `/users/jolyne`, **the same component instance will be reused**. Since both routes render the same component, this is more efficient than destroying the old instance and then creating a new one. **However, this also means that the lifecycle hooks of the component will not be called**.
-To react to params changes in the same component, you can simply watch anything on the `$route` object, in this scenario, the `$route.params`:
+To react to params changes in the same component, you can simply watch anything on the `route` object, in this scenario, the `route.params`:
-```js
-const User = {
- template: '...',
+::: code-group
+
+```vue [Composition API]
+<script setup>
+import { watch } from 'vue'
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+
+watch(() => route.params.id, (newId, oldId) => {
+ // react to route changes...
+})
+</script>
+```
+
+```vue [Options API]
+<script>
+export default {
created() {
this.$watch(
- () => this.$route.params,
- (toParams, previousParams) => {
+ () => this.$route.params.id,
+ (newId, oldId) => {
// react to route changes...
}
)
},
}
+</script>
```
+:::
+
Or, use the `beforeRouteUpdate` [navigation guard](../advanced/navigation-guards.md), which also allows to cancel the navigation:
-```js
-const User = {
- template: '...',
+::: code-group
+
+```vue [Composition API]
+<script setup>
+import { onBeforeRouteUpdate } from 'vue-router'
+// ...
+
+onBeforeRouteUpdate(async (to, from) => {
+ // react to route changes...
+ userData.value = await fetchUser(to.params.id)
+})
+</script>
+```
+
+```vue [Options API]
+<script>
+export default {
async beforeRouteUpdate(to, from) {
// react to route changes...
this.userData = await fetchUser(to.params.id)
},
+ // ...
}
+</script>
```
+:::
+
## Catch all / 404 Not found Route
<VueSchoolLink
```js
const routes = [
- // will match everything and put it under `$route.params.pathMatch`
+ // will match everything and put it under `route.params.pathMatch`
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
- // will match anything starting with `/user-` and put it under `$route.params.afterUser`
+ // will match anything starting with `/user-` and put it under `route.params.afterUser`
{ path: '/user-:afterUser(.*)', component: UserGeneric },
]
```
In this specific scenario, we are using a [custom regexp](./route-matching-syntax.md#custom-regexp-in-params) between parentheses and marking the `pathMatch` param as [optionally repeatable](./route-matching-syntax.md#optional-parameters). This allows us to directly navigate to the route if we need to by splitting the `path` into an array:
```js
-this.$router.push({
+router.push({
name: 'NotFound',
// preserve current path and remove the first char to avoid the target URL starting with `//`
- params: { pathMatch: this.$route.path.substring(1).split('/') },
+ params: { pathMatch: route.path.substring(1).split('/') },
// preserve existing query and hash if any
- query: this.$route.query,
- hash: this.$route.hash,
+ query: route.query,
+ hash: route.hash,
})
```
Sometimes you need to display multiple views at the same time instead of nesting them, e.g. creating a layout with a `sidebar` view and a `main` view. This is where named views come in handy. Instead of having one single outlet in your view, you can have multiple and give each of them a name. A `router-view` without a name will be given `default` as its name.
```vue-html
-<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
-<router-view class="view main-content"></router-view>
-<router-view class="view right-sidebar" name="RightSidebar"></router-view>
+<router-view class="view left-sidebar" name="LeftSidebar" />
+<router-view class="view main-content" />
+<router-view class="view right-sidebar" name="RightSidebar" />
```
A view is rendered by using a component, therefore multiple views require
Given the app we created in the last chapter:
-```html
-<div id="app">
- <router-view></router-view>
-</div>
+```vue
+<!-- App.vue -->
+<template>
+ <router-view />
+</template>
+```
+
+```vue
+<!-- User.vue -->
+<template>
+ <div>
+ User {{ $route.params.id }}
+ </div>
+</template>
```
```js
-const User = {
- template: '<div>User {{ $route.params.id }}</div>',
-}
+import User from './User.vue'
// these are passed to `createRouter`
const routes = [{ path: '/user/:id', component: User }]
The `<router-view>` here is a top-level `router-view`. It renders the component matched by a top level route. Similarly, a rendered component can also contain its own, nested `<router-view>`. For example, if we add one inside the `User` component's template:
-```js
-const User = {
- template: `
- <div class="user">
- <h2>User {{ $route.params.id }}</h2>
- <router-view></router-view>
- </div>
- `,
-}
+```vue
+<!-- User.vue -->
+<template>
+ <div class="user">
+ <h2>User {{ $route.params.id }}</h2>
+ <router-view />
+ </div>
+</template>
```
To render components into this nested `router-view`, we need to use the `children` option in any of the routes:
title="Learn how to pass props to route components"
/>
-Using `$route` in your component creates a tight coupling with the route which limits the flexibility of the component as it can only be used on certain URLs. While this is not necessarily a bad thing, we can decouple this behavior with a `props` option:
+Using `$route` or `useRoute()` in your component creates a tight coupling with the route which limits the flexibility of the component as it can only be used on certain URLs. While this is not necessarily a bad thing, we can decouple this behavior with a `props` option.
-We can replace
+Let's return to our earlier example:
+
+```vue
+<!-- User.vue -->
+<template>
+ <div>
+ User {{ $route.params.id }}
+ </div>
+</template>
+```
+
+with:
```js
-const User = {
- template: '<div>User {{ $route.params.id }}</div>'
+import User from './User.vue'
+
+// these are passed to `createRouter`
+const routes = [
+ { path: '/users/:id', component: User },
+]
+```
+
+We can remove the direct dependency on `$route` in `User.vue` by declaring a prop instead:
+
+::: code-group
+
+```vue [Composition API]
+<!-- User.vue -->
+<script setup>
+defineProps({
+ id: String
+})
+</script>
+
+<template>
+ <div>
+ User {{ id }}
+ </div>
+</template>
+```
+
+```vue [Options API]
+<!-- User.vue -->
+<script>
+export default {
+ props: {
+ id: String
+ }
}
-const routes = [{ path: '/user/:id', component: User }]
+</script>
+
+<template>
+ <div>
+ User {{ id }}
+ </div>
+</template>
```
-with
+:::
+
+We can then configure the route to pass the `id` param as a prop by setting `props: true`:
```js
-const User = {
- // make sure to add a prop named exactly like the route param
- props: ['id'],
- template: '<div>User {{ id }}</div>'
-}
-const routes = [{ path: '/user/:id', component: User, props: true }]
+const routes = [
+ { path: '/user/:id', component: User, props: true }
+]
```
This allows you to use the component anywhere, which makes the component easier to reuse and test.
href="https://vueschool.io/courses/vue-router-4-for-everyone"
title="Learn how to build powerful Single Page Applications with the Vue Router on Vue School">Watch a Free Vue Router Video Course</VueSchoolLink>
-Creating a Single-page Application with Vue + Vue Router feels natural: with Vue.js, we are already composing our application with components. When adding Vue Router to the mix, all we need to do is map our components to the routes and let Vue Router know where to render them. Here's a basic example:
+Vue Router is the official client-side routing solution for Vue.
-## HTML
+Client-side routing is used by single-page applications (SPAs) to tie the browser URL to the content seen by the user. As users navigate around the application, the URL updates accordingly, but the page doesn't need to be reloaded from the server.
-```html
-<script src="https://unpkg.com/vue@3"></script>
-<script src="https://unpkg.com/vue-router@4"></script>
+Vue Router is built on Vue's component system. You configure **routes** to tell Vue Router which components to show for each URL path.
-<div id="app">
+::: tip Prerequisites
+This guide will assume that you are already familiar with Vue itself. You don't need to be a Vue expert, but you may occasionally need to refer back to [the core Vue documentation](https://vuejs.org/) for more information about certain features.
+:::
+
+## An example
+
+To introduce some of the main ideas, we're going to consider this example:
+
+- [Vue Playground example](https://play.vuejs.org/#eNqFVVtv2zYU/itn6gArmC05btEHTXXTFcWyYZeiLfYy7UGWji02EsmRlOPA8H/fIambnaRD4Fg61++c7yN9DJqc8eirDpKANVIoA0coFOYG30kJJ9gq0cBs3+Is412AEq1B1Xmi2L+ObpvX+3IpI5+b8aFqSJ+rjANErcbQp/v3RrTchLMXlDa7CuZBl07YUoONrCl/bQPT6np9i3UtbLPv0phenVm6L3rQRgm+W79vlULeIQaZmypJ484HxyN87xzRtq3rj+SE08mViX2dlOf7vuAnh/I3xu/AiDdZEGfB+mdBz3ArGkzj0f9sRr4hy5D2zr49ykvjvmdqeTmv9RfDe4i7uM6dxsNiaF9+l0+y+Ts2Qj3cMm3oa94Zfd0py4uBzYFPO6Br3ZPaGzpme9rtQGdxg2WUgOC6Y0PDG/jbjnL0vMAsnhEsQcU4UZaMbU/z8zC3x/PYsbcN/ueilaJW03nDoy1Y+VUkT+0nvHI9PVB6PJE8M44HN2iJ27yt+9q09ek+rFR1oZg0RM5FgmvboKlEqRP/BrATX4SDH171JgBD4CIvThXJVldhP7Y7J9DtxP4nxZKk+470cnFQVuseHh2TlTduWmMEh5uiZsUdSXPAcKlOH/hIZmfEjhODRtPaozNKjyiiGcqn75Ej0Pl3lMyHp2fFeMHnEB/SRia+ict6ep/GXBWV1UGHyGtgh5O1K0KvuC8T/duieoi6tLdvYUYg+rXTmKH3jLmeKoW0owLDI7h8IrnvfAKrIargxfQ/lA0LHjmr8w3W3X3w2dVMIGWchoH9ohEl1pFRrCE2fccsgCY/1Mh3piLjaknc+pujr3TOqedk0eSSrg/BiVU3WtY5dBYMks2CkRtrzoLKGKmTOG65vNtFtON4jLh5Fb2MlnFJJ2tijVA3i40S99rdV1ngNmtr31BQXOLeCFHrRS7Zcy0eBd68jl5H13HNNjFVjxkv8eBq94unMY0mQWzZ7mJIKwtWo/pTGkaCORs2p9+Z+1+dzagWB6BFhcXdE/av+uAhf1RI0+1xMpzJFWnOuz98/gMP9Dw4icW2puhvOD+hFnVrMfqwn1peEuxJnEP7i+OM8d0X/eFgkOt+KAt0FLIj8v03Rh/hvoxeTbaozUONOiq0/aGhX6w5aY1xn7cRqkSVwEoegMCyEl4sl8sf3d1H5RhfbATdKk0C10t5cHaZlyWBHSzUJeNUFtaQww/08Tenz65xSzf+NLJaTTuP5UcARVFMACSwpL9VVyE4/QesCg/V)
+
+Let's start by looking at the root component, `App.vue`.
+
+### App.vue
+
+```vue
+<template>
<h1>Hello App!</h1>
<p>
- <!-- use the router-link component for navigation. -->
- <!-- specify the link by passing the `to` prop. -->
- <!-- `<router-link>` will render an `<a>` tag with -->
- <!-- the correct `href` attribute -->
- <router-link to="/">Go to Home</router-link>
- <router-link to="/about">Go to About</router-link>
+ <strong>Current route path:</strong> {{ $route.fullPath }}
</p>
- <!-- route outlet -->
- <!-- component matched by the route will render here -->
- <router-view></router-view>
-</div>
+ <nav>
+ <RouterLink to="/">Go to Home</RouterLink>
+ <RouterLink to="/about">Go to About</RouterLink>
+ </nav>
+ <main>
+ <RouterView />
+ </main>
+</template>
```
-### `router-link`
+This template is using two components provided by Vue Router, `RouterLink` and `RouterView`.
-Note how instead of using regular `a` tags, we use a custom component `router-link` to create links. This allows Vue Router to change the URL without reloading the page, handle URL generation as well as its encoding. We will see later how to benefit from these features.
+Instead of using regular `<a>` tags, we use the custom component `RouterLink` to create links. This allows Vue Router to change the URL without reloading the page, handle URL generation, encoding, and various other features. We'll go into more detail about `RouterLink` in later sections of the guide.
-### `router-view`
+The `RouterView` component tells Vue Router where to render the current **route component**. That's the component that corresponds to the current URL path. It doesn't have to be in `App.vue`, you can put it anywhere to adapt it to your layout, but it does need to be included somewhere, otherwise Vue Router won't render anything.
-`router-view` will display the component that corresponds to the URL. You can put it anywhere to adapt it to your layout.
+The example above also uses <code v-pre>{{ $route.fullPath }}</code>. You can use `$route` in your component templates to access an object that represents the current route.
<VueMasteryLogoLink></VueMasteryLogoLink>
-## JavaScript
+### Creating the router instance
+
+The router instance is created by calling the function `createRouter()`:
```js
-// 1. Define route components.
-// These can be imported from other files
-const Home = { template: '<div>Home</div>' }
-const About = { template: '<div>About</div>' }
-
-// 2. Define some routes
-// Each route should map to a component.
-// We'll talk about nested routes later.
+import { createMemoryHistory, createRouter } from 'vue-router'
+
+import HomeView from './HomeView.vue'
+import AboutView from './AboutView.vue'
+
const routes = [
- { path: '/', component: Home },
- { path: '/about', component: About },
+ { path: '/', component: HomeView },
+ { path: '/about', component: AboutView },
]
-// 3. Create the router instance and pass the `routes` option
-// You can pass in additional options here, but let's
-// keep it simple for now.
-const router = VueRouter.createRouter({
- // 4. Provide the history implementation to use. We
- // are using the hash history for simplicity here.
- history: VueRouter.createWebHashHistory(),
- routes, // short for `routes: routes`
+const router = createRouter({
+ history: createMemoryHistory(),
+ routes,
})
+```
-// 5. Create and mount the root instance.
-const app = Vue.createApp({})
-// Make sure to _use_ the router instance to make the
-// whole app router-aware.
-app.use(router)
+The `routes` option defines the routes themselves, mapping URL paths to components. The component specified by the `component` option is the one that will be rendered by the `<RouterView>` in our earlier `App.vue`. These route components are sometimes referred to as _views_, though they are just normal Vue components.
-app.mount('#app')
+Routes support various other options that we'll see later in the guide, but for now we only need `path` and `component`.
+
+The `history` option controls how routes are mapped onto URLs and vice versa. For the Playground example we're using `createMemoryHistory()`, which ignores the browser URL entirely and uses its own internal URL instead. That works well for the Playground, but it's unlikely to be what you'd want in a real application. Typically, you'd want to use `createWebHistory()` instead, or perhaps `createWebHashHistory()`. We'll cover that topic in more detail in the guide to [History modes](./essentials/history-mode).
-// Now the app has started!
+### Registering the router plugin
+
+Once we've created our router instance, we need to register it as a plugin by calling `use()` on our application:
+
+```js
+createApp(App)
+ .use(router)
+ .mount('#app')
```
-By calling `app.use(router)`, we are triggering the initial navigation and giving access to `this.$router` as well as the current route as `this.$route` inside of any component:
+Or, equivalently:
+
+```js
+const app = createApp(App)
+app.use(router)
+app.mount('#app')
+```
+
+Like with most Vue plugins, the call to `use()` needs to happen before the call to `mount()`.
+
+If you're curious about what this plugin does, some of its responsibilities include:
+
+1. [Globally registering](https://vuejs.org/guide/components/registration.html#global-registration) the `RouterView` and `RouterLink` components.
+2. Adding the global `$router` and `$route` properties.
+3. Enabling the `useRouter()` and `useRoute()` composables.
+4. Triggering the router to resolve the initial route.
+
+### Accessing the router and current route
+
+You'll likely want to access the router from elsewhere in your application.
+
+If you're exporting the router instance from an ES module, you could import the router instance directly where you need it. In some cases this is the best approach, but we have other options if we're inside a component.
+
+In component templates, the router instance is exposed as `$router`. This is similar to the `$route` property we saw earlier, but note the extra `r` on the end.
+
+If we're using the Options API, we can access these same two properties as `this.$router` and `this.$route` in our JavaScript code. The `HomeView.vue` component in the Playground example accesses the router that way:
```js
-// Home.vue
export default {
- computed: {
- username() {
- // We will see what `params` is shortly
- return this.$route.params.username
- },
- },
methods: {
- goToDashboard() {
- if (isAuthenticated) {
- this.$router.push('/dashboard')
- } else {
- this.$router.push('/login')
- }
+ goToAbout() {
+ this.$router.push('/about')
},
},
}
```
-To access the router or the route inside the `setup` function, call the `useRouter` or `useRoute` functions. We will learn more about this in [the Composition API](./advanced/composition-api.md#Accessing-the-Router-and-current-Route-inside-setup)
+This method is calling `push()`, which is used for [programmatic navigation](./essentials/navigation). We'll learn more about that later.
+
+With the Composition API, we don't have access to the component instance via `this`, so Vue Router includes some composables that we can use instead. `AboutView.vue` in the Playground example is using that approach:
+
+```vue
+<script setup>
+import { computed } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+
+const router = useRouter()
+const route = useRoute()
+
+const search = computed({
+ get() {
+ return route.query.search ?? ''
+ },
+ set(search) {
+ router.replace({ query: { search } })
+ }
+})
+</script>
+```
+
+It's not necessary to understand all of that code right now. The key thing to notice is that the composables `useRouter()` and `useRoute()` are used to access the router instance and current route respectively.
+
+### Next steps
+
+If you'd like to see a complete example using Vite, you can use the [create-vue](https://github.com/vuejs/create-vue) scaffolding tool, which has the option to include Vue Router in its example project:
+
+::: code-group
+
+```bash [npm]
+npm create vue@latest
+```
+
+```bash [yarn]
+yarn create vue
+```
+
+```bash [pnpm]
+pnpm create vue
+```
+
+:::
+
+The example project created by create-vue uses similar features to the ones we've seen here. You may find that a useful starting point for exploring the features introduced in the next few pages of this guide.
+
+## Conventions in this guide
+
+### Single-File Components
+
+Vue Router is most commonly used in applications built using a bundler (e.g. Vite) and [SFCs](https://vuejs.org/guide/introduction.html#single-file-components) (i.e. `.vue` files). Most of the examples in this guide will be written in that style, but Vue Router itself doesn't require you to use build tools or SFCs.
+
+For example, if you're using the _global builds_ of [Vue](https://vuejs.org/guide/quick-start.html#using-vue-from-cdn) and [Vue Router](../installation#Direct-Download-CDN), the libraries are exposed via global objects, rather than imports:
+
+```js
+const { createApp } = Vue
+const { createRouter, createWebHistory } = VueRouter
+```
+
+### Component API style
+
+Vue Router can be used with both the Composition API and the Options API. Where relevant, the examples in this guide will show components written in both styles. Composition API examples will typically use `<script setup>`, rather than an explicit `setup` function.
+
+If you need a refresher about the two styles, see [Vue - API Styles](https://vuejs.org/guide/introduction.html#api-styles).
+
+### `router` and `route`
+
+Throughout the guide, we will often refer to the router instance as `router`. This is the object returned by `createRouter()`. How you access that object in your application will depend on the context. For example, in a component using the Composition API, it can be accessed by calling `useRouter()`. With the Options API, it can be accessed using `this.$router`.
+
+Similarly, the current route will be referred to as `route`. It can be accessed in components using `useRoute()` or `this.$route`, depending on which API the component is using.
+
+### `RouterView` and `RouterLink`
+
+The components `RouterView` and `RouterLink` are both [registered globally](https://vuejs.org/guide/components/registration.html#global-registration), so they don't need to be imported before using them in component templates. However, if you prefer, you can import them locally, e.g. `import { RouterLink } from 'vue-router'`.
+
+In templates, component names can be written in either PascalCase or kebab-case. Vue's template compiler supports either format, so `<RouterView>` and `<router-view>` are usually equivalent. You should follow whatever convention is used within your project.
-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.
+If you're using in-DOM templates then [the usual caveats](https://vuejs.org/guide/essentials/component-basics.html#in-dom-template-parsing-caveats) apply: component names must be written in kebab-case and self-closing tags are not supported. So rather than writing `<RouterView />`, you would need to use `<router-view></router-view>` instead.
\ No newline at end of file
<VueMasteryLogoLink></VueMasteryLogoLink>
-## Direct Download / CDN
-
-[https://unpkg.com/vue-router@4](https://unpkg.com/vue-router@4)
-
-<!--email_off-->
-
-[Unpkg.com](https://unpkg.com) provides npm-based CDN links. The above link will always point to the latest release on npm. You can also use a specific version/tag via URLs like `https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js`.
-
-<!--/email_off-->
-
-This will expose Vue Router via a global `VueRouter` object, e.g. `VueRouter.createRouter(...)`.
-
## Package managers
If you have an existing project that uses a JavaScript package manager, you can install Vue Router from the npm registry:
You'll be prompted with some questions about the kind of project you want to create. If you choose to install Vue Router, the example application will also demonstrate some of Vue Router's core features.
-Projects using package managers will typically use ES modules to access Vue Router, e.g. `import { createRouter } from 'vue-router'`.
\ No newline at end of file
+Projects using package managers will typically use ES modules to access Vue Router, e.g. `import { createRouter } from 'vue-router'`.
+
+## Direct Download / CDN
+
+[https://unpkg.com/vue-router@4](https://unpkg.com/vue-router@4)
+
+<!--email_off-->
+
+[Unpkg.com](https://unpkg.com) provides npm-based CDN links. The above link will always point to the latest release on npm. You can also use a specific version/tag via URLs like `https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js`.
+
+<!--/email_off-->
+
+This will expose Vue Router via a global `VueRouter` object, e.g. `VueRouter.createRouter(...)`.