## Know issues
+<!-- TODO: put a link to the documentation instead -->
+
### Breaking changes compared to vue-router@3.x
- The `mode: 'history'` option has been replaced with a more flexible one named `history`:
text: 'Navigation guards',
},
{ link: '/guide/advanced/meta', text: 'Route Meta Fields' },
- {
- link: '/guide/advanced/transitions',
- text: 'Transitions',
- },
{
link: '/guide/advanced/data-fetching',
text: 'Data Fetching',
},
+ {
+ link: '/guide/advanced/composition-api',
+ text: 'Composition API',
+ },
+ {
+ link: '/guide/advanced/transitions',
+ text: 'Transitions',
+ },
{
link: '/guide/advanced/scroll-behavior',
text: 'Scroll Behavior',
--- /dev/null
+# Vue Router and the Composition API
+
+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.
+
+## 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` function:
+
+```js
+export default {
+ setup() {
+ const router = useRouter()
+ const route = useRoute()
+
+ function pushWithQuery(query) {
+ router.push({
+ name: 'search',
+ query: {
+ ...this.route.query,
+ },
+ })
+ }
+ },
+}
+```
+
+The `route` object is a reactive object, so any of its properties can be watched and you should **avoid watching the whole `route`** object:
+
+```js
+export default {
+ setup() {
+ const route = useRoute()
+ const userData = ref()
+
+ // fetch the user information when params change
+ watch(
+ () => route.params,
+ async params => {
+ userData.value = await fetchUser(newParams.id)
+ }
+ )
+ },
+}
+```
+
+Note we still have access to `$router` and `$route` in templates, so there is no need to return `router` or `route` inside of `setup`.
+
+## 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:
+
+```js
+import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
+
+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)
+ }
+ })
+ },
+}
+```
+
+<!-- TODO: useLink -->
### Using the composition API
-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:
-
-```js
-import { onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router'
-
-const UserDetails = {
- template: `...`,
- setup() {
- onBeforeRouteUpdate((to, from) => {
- // same as the beforeRouteUpdate option but with no access to `this`
- })
-
- onBeforeRouteLeave((to, from) => {
- // same as the beforeRouteLeave option but with no access to `this`
- })
- },
-}
-```
+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.
## The Full Navigation Resolution Flow
// We'll talk about nested routes later.
const routes = [
{ path: '/', component: Home },
- { path: '/about', component: About }
+ { path: '/about', component: About },
]
// 3. Create the router instance and pass the `routes` option
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`
+ routes, // short for `routes: routes`
})
// 4. Create and mount the root instance.
username() {
// We will see what `params` is shortly
return this.$route.params.username
- }
+ },
},
methods: {
- goBack() {
- window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
- }
- }
+ goToDashboard() {
+ if (isAuthenticated) {
+ this.$router.push('/dashboard')
+ } else {
+ this.$router.push('/login')
+ }
+ },
+ },
}
```
const route = useRoute()
const username = computed(() => route.params.username)
- function goBack() {
- window.history.length > 1 ? router.go(-1) : router.push('/')
+ function goToDashboard() {
+ if (isAuthenticated) {
+ router.push('/dashboard')
+ } else {
+ router.push('/login')
+ }
}
- return { username, goBack }
- }
+ return { username, goToDashboard }
+ },
}
```
+We will learn more about this in [the Composition API](/guide/advanced/composition-api.md)
+
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.
<!-- TODO: links -->
- Dynamic Routing
-- Composition API
+- [Composition API](/guide/advanced/composition-api.md)
- Custom History implementation
-## Breaking Changes
-
-### Improvements
+## Breaking Changes: Improvements
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.
**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).
-### Changed API
+## Breaking Changes: API Changes
+
+The following changes will require you to adapt your code
### New `history` option to replace `mode`
}).href // '/not/found'
```
-**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.
+**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 `*`.
### Removal of `router.match` and changes to `router.resolve`
replace
<router-link to="child-route" append>to relative child</router-link>
with
-<router-link :to="$route.path + '/child-route'">to relative child</router-link>
+<router-link :to="append($route.path, 'child-route')">
+ to relative child
+</router-link>
+```
+
+You must define a global `append` function on your _App_ instance:
+
+```js
+app.config.globalProperties.append = (path, pathToAppend) =>
+ path + (path.endsWith('/') ? '' : '/') + pathToAppend
```
-**Reason**: `append` wasn't used very often and is easy to replicate in user land.
+**Reason**: `append` wasn't used very often, is easy to replicate in user land.
### Removal of `event` and `tag` props in `<router-link>`
### TypeScript
-To make typings more consistent and expressive, some types have been renamed.
+To make typings more consistent and expressive, some types have been renamed:
| `vue-router@3` | `vue-router@4` |
| -------------- | ----------------------- |
export interface RouterLinkOptions {
/**
- * Location the link should navigate to when clicked on.
+ * Route Location the link should navigate to when clicked on.
*/
to: RouteLocationRaw
/**
export interface RouterLinkProps extends RouterLinkOptions {
/**
- * Whether RouterLink should not wrap its content in an `a` tag.
+ * Whether RouterLink should not wrap its content in an `a` tag. Useful when
+ * using `v-slot` to create a custom RouterLink
*/
custom?: boolean
/**
return (
// we are dealing with nested routes
length > 1 &&
- // if the have the same path, this link is referring to the empty child
- // are we currently are on a different child of the same parent
+ // if the parent and matched route have the same path, this link is
+ // referring to the empty child. Or we currently are on a different
+ // child of the same parent
getOriginalPath(routeMatched) === parentRecordPath &&
// avoid comparing the child with its parent
currentMatched[currentMatched.length - 1].path !== parentRecordPath