- Most RFCs have been merged.
- All merged RFCs have been implemented.
- Vue CLI now has experimental support via vue-cli-plugin-vue-next.
- Check the playground or e2e tests for a usage example.
Breaking changes compared to [email protected]
-
The
mode: 'history'option has been replaced with a more flexible one namedhistory:import { createRouter, createWebHistory } from 'vue-router' // there is also createWebHashHistory and createMemoryHistory createRouter({ history: createWebHistory(), routes: [], })
-
baseoption is now passed as the first argument tocreateWebHistory(and other histories) -
Catch all routes (
/*) must now be defined using a parameter with a custom regex:/:catchAll(.*) -
router.matchandrouter.resolveare merged together intorouter.resolvewith a slightly different signature. Check its typing through autocomplete or Router'sresolvemethod -
router.getMatchedComponentsis now removed as they can be retrieved fromrouter.currentRoute.value.matched:router.currentRoute.value.matched.flatMap(record => Object.values(record.components) )
- The
appendargument has been removed. You can manually concatenate the value to an existingpathinstead.
- The
-
RouterLink -
If you use a
transition, you may need to wait for the router to be ready before mounting the app:app.use(router) // Note: on Server Side, you need to manually push the initial ___location router.isReady().then(() => app.mount('#app'))
Otherwise there will be an initial transition as if you provided the
appearprop totransitionbecause the router displays its initial ___location (nothing) and then displays the first ___location. This happens because navigations are all asynchronous now. If you have navigation guards upon the initial navigation, you might not want to block the app render until they are resolved. -
On SSR, you need to manually pass the appropriate history:
// router.js let history = isServer ? createMemoryHistory() : createWebHistory() let router = createRouter({ routes, history }) // somewhere in your server-entry.js router.push(req.url) // request url router.isReady().then(() => { // resolve the request })
-
The object returned in
scrollBehavioris now similar toScrollToOptions:xis renamed toleftandyis renamed totop. See RFC. -
transitionandkeep-alivemust now be used inside ofRouterViewvia thev-slotAPI:<router-view v-slot="{ Component }"> <transition> <keep-alive> <component :is="Component" /> </keep-alive> </transition> </router-view>
See more on the KeepAlive and the Transition examples. See RFC.
-
parentis removed from Route locations (this.$routeand object returned byrouter.resolve). You can still access it via thematchedarray:const parent = this.$route.matched[this.$route.matched.length - 2]
To make typings more consistent and expressive, some types have been renamed. Keep in mind these can change until stable release to ensure consistency. Some type properties might have changed as well.
vue-router@3 |
vue-router@4 |
|---|---|
| RouteConfig | RouteRecordRaw |
| Location | RouteLocation |
| Route | RouteLocationNormalized |
These are technically breaking changes but they fix an inconsistent behavior.
- Pushing or resolving a non existent named route throws an error instead of navigating to
/and displaying nothing. - resolving(
router.resolve) or pushing (router.push) a ___location with missing params no longer warns and produces an invalid URL (/), but explicitly throws an Error instead. - Empty children
pathdoes not append a trailing slash (/) anymore to make it consistent across all routes:- By default no route has a trailing slash but also works with a trailing slash
- Adding
strict: trueto a route record or to the router options (alongsideroutes) will disallow an optional trailing slash - Combining
strict: truewith a trailing slash in your routes allows you to enforce a trailing slash in your routes. In the case of nested routes, make sure to add the trailing slash to the parent and not the empty child:let routes = [ { path: '/parent/', children: [{ path: '' }, { path: 'child1/' }, { path: 'child2/' }], }, ]
- To redirect the user to trailing slash routes (or the opposite), you can setup a
beforeEachnavigation guard that ensures the presence of a trailing slash:router.beforeEach((to, from, next) => { if (to.path.endsWith('/')) next() else next({ path: to.path + '/', query: to.query, hash: to.hash }) })
- Because of the change above, relative children path
redirecton an empty path are not supported anymore. Use named routes instead:Note this will work if// replace let routes = [ { path: '/parent', children: [ // this would now redirect to `/home` instead of `/parent/home` { path: '', redirect: 'home' }, { path: 'home' }, ], }, ] // with let routes = [ { path: '/parent', children: [ { path: '', redirect: { name: 'home' } }, { path: 'home', name: 'home' }, ], }, ]
pathwas/parent/as the relative ___locationhometo/parent/is indeed/parent/homebut the relative ___location ofhometo/parentis/home - Encoding is now more consistent. The initial navigation should yield the same results are in-app navigations.
- Values in
path,fullPathare not decoded anymore. They will appear as provided by the browser (most browsers provide them encoded). params,queryandhashare now all decoded- When using
push,resolveandreplaceand providing astring___location or apathproperty in an object, it must be encoded.params,queryandhashmust be provided in its decoded version.
- Values in
See Contributing Guide.