Skip to content

Commit 8c6f3a7

Browse files
committed
docs: migration guide [skip ci]
1 parent ac97473 commit 8c6f3a7

File tree

1 file changed

+216
-6
lines changed

1 file changed

+216
-6
lines changed

docs/guide/migration/index.md

Lines changed: 216 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Some of new features to keep an eye on in Vue Router 4 include:
1212
- Composition API
1313
- Custom History implementation
1414

15-
## Breaking
15+
## Breaking Changes
1616

1717
### Improvements
1818

@@ -45,9 +45,9 @@ router.resolve({ name: 'users' })
4545

4646
**Reason**: Same as above.
4747

48-
### Empty `path` in children no longer produces a trailing slash
48+
### Named children routes with an empty `path` no longer appends a slash
4949

50-
Given any nested routes with an empty `path`:
50+
Given any nested named route with an empty `path`:
5151

5252
```js
5353
const routes = [
@@ -62,8 +62,218 @@ const routes = [
6262
]
6363
```
6464

65-
Navigating to the named route `dashboard` will now produce a URL **without a trailing slash**.
65+
Navigating or resolving to the named route `dashboard` will now produce a URL **without a trailing slash**:
6666

67-
Both `/dashboard` and `/dashboard/` will access the named route _dashboard_ and not _dashboard-parent_
67+
```js
68+
router.resolve({ name: 'dashboard' }).href // '/dashboard'
69+
```
70+
71+
This has an important side effect about children `redirect` records like these:
72+
73+
```js
74+
const routes = [
75+
{
76+
path: '/parent',
77+
children: [
78+
// this would now redirect to `/home` instead of `/parent/home`
79+
{ path: '', redirect: 'home' },
80+
{ path: 'home' },
81+
],
82+
},
83+
]
84+
```
85+
86+
Note this will work if `path` was `/parent/` as the relative ___location `home` to `/parent/` is indeed `/parent/home` but the relative ___location of `home` to `/parent` is `/home`. Learn more about relative links [in the cookbook](/cookbook/relative-links.md).
87+
88+
**Reason**: This is to make trailing slash behavior consistent: by default all routes allow a trailing slash. [It can be disabled](/cookbook/trailing-slashes.md).
89+
90+
### `$route` properties Encoding
91+
92+
Decoded values are now consistent no matter where the navigation is initiated (older browsers will still produce unencoded `path` and `fullPath`). The initial navigation should yield the same results as in-app navigations.
93+
94+
Given any [normalized route ___location](#TODO):
95+
96+
- Values in `path`, `fullPath` are not decoded anymore. They will appear as provided by the browser (modern browsers provide them encoded). e.g. directly writing on the address bar `https://example.com/hello world` will yield the encoded version: `https://example.com/hello%20world` and both `path` and `fullPath` will be `/hello%20world`.
97+
- `hash` is now decoded, that way it can be copied over: `router.push({ hash: $route.hash })`.
98+
- When using `push`, `resolve` and `replace` and providing a `string` ___location or a `path` property in an object, **it must be encoded**. On the other hand, `params`, `query` and `hash` must be provided in its unencoded version.
99+
100+
**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).
101+
102+
### Changed API
103+
104+
### New `history` option to replace `mode`
105+
106+
The `mode: 'history'` option has been replaced with a more flexible one named `history`:
107+
108+
```js
109+
import { createRouter, createWebHistory } from 'vue-router'
110+
// there is also createWebHashHistory and createMemoryHistory
111+
112+
createRouter({
113+
history: createWebHistory(),
114+
routes: [],
115+
})
116+
```
117+
118+
On SSR, you need to manually pass the appropriate history:
119+
120+
```js
121+
// router.js
122+
let history = isServer ? createMemoryHistory() : createWebHistory()
123+
let router = createRouter({ routes, history })
124+
// somewhere in your server-entry.js
125+
router.push(req.url) // request url
126+
router.isReady().then(() => {
127+
// resolve the request
128+
})
129+
```
130+
131+
**Reason**: enable tree shaking of non used histories as well as implementing custom histories for advanced use cases like native solutions.
132+
133+
### Moved the `base` option
134+
135+
The `base` option is now passed as the first argument to `createWebHistory` (and other histories):
136+
137+
```js
138+
import { createRouter, createWebHistory } from 'vue-router'
139+
createRouter({
140+
history: createWebHistory('/base-directory/'),
141+
routes: [],
142+
})
143+
```
144+
145+
### Removed `*` (star or catch all) routes
146+
147+
Catch all routes (`*`, `/*`) must now be defined using a parameter with a custom regex:
148+
149+
```js
150+
const routes = [
151+
// pathMatch is the name of the param, e.g., going to /not/found yields
152+
// { params: { pathMatch: ['not', 'found'] }}
153+
{ path: '/:pathMatch(.*)*', name: 'not-found' },
154+
// if you omit the last `*`, the `/` character in params will be encoded when resolving or pushing
155+
{ path: '/:pathMatch(.*)', name: 'bad-not-found' },
156+
]
157+
// bad example:
158+
router.resolve({
159+
name: 'bad-not-found',
160+
params: { pathMatch: 'not/found' },
161+
}).href // '/not%2Ffound'
162+
// good example:
163+
router.resolve({
164+
name: 'not-found',
165+
params: { pathMatch: ['not', 'found'] },
166+
}).href // '/not/found'
167+
```
168+
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.
170+
171+
### Removal of `router.match` and changes to `router.resolve`
172+
173+
Both `router.match`, and `router.resolve` have been merged together into `router.resolve` with a slightly different signature. [Refer to the API](#TODO) for more details.
174+
175+
**Reason**: Uniting multiple methods that were use for the same purpose.
176+
177+
### Removal of `router.getMatchedComponents()`
178+
179+
The method `router.getMatchedComponents` is now removed as matched components can be retrieved from `router.currentRoute.value.matched`:
180+
181+
```js
182+
router.currentRoute.value.matched.flatMap(record =>
183+
Object.values(record.components)
184+
)
185+
```
186+
187+
**Reason**: This method was only used during SSR and is a one liner that can be done by the user.
188+
189+
### Removal of `append` prop in `<router-link>`
190+
191+
The `append` prop has been removed from `<router-link>`. You can manually concatenate the value to an existing `path` instead:
192+
193+
```html
194+
replace
195+
<router-link to="child-route" append>to relative child</router-link>
196+
with
197+
<router-link :to="$route.path + '/child-route'">to relative child</router-link>
198+
```
199+
200+
**Reason**: `append` wasn't used very often and is easy to replicate in user land.
201+
202+
### Removal of `event` and `tag` props in `<router-link>`
203+
204+
Both `event`, and `tag` props have been removed from `<router-link>`. You can use the [`v-slot` API](#TODO) to fully customize `<router-link>`:
205+
206+
```html
207+
replace
208+
<router-link to="/about" tag="span" event="dblclick">About Us</router-link>
209+
with
210+
<router-link to="/about" custom v-slot="{ navigate }">
211+
<span @dblclick="navigate" role="link">About Us</span>
212+
</router-link>
213+
```
214+
215+
**Reason**: These props were often used together to use something different from an `<a>` tag but were introduced before the `v-slot` API and are not used enough to justify adding to the bundle size for everybody.
216+
217+
### Removal of the `exact` prop in `<router-link>`
218+
219+
The `exact` prop has been removed because the caveat it was fixing is no longer present. See the [RFC about active matching](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0028-router-active-link.md) changes for more details.
220+
221+
### **All** navigations are now always asynchronous
222+
223+
All navigations, including the first one, are now asynchronous, meaning that, if you use a `transition`, you may need to wait for the router to be _ready_ before mounting the app:
224+
225+
```js
226+
app.use(router)
227+
// Note: on Server Side, you need to manually push the initial ___location
228+
router.isReady().then(() => app.mount('#app'))
229+
```
230+
231+
Otherwise there will be an initial transition as if you provided the `appear` prop to `transition` because the router displays its initial ___location (nothing) and then displays the first ___location.
232+
233+
Note that **if you have navigation guards upon the initial navigation**, you might not want to block the app render until they are resolved unless you are doing Server Side Rendering.
234+
235+
### `scrollBehavior` changes
236+
237+
The object returned in `scrollBehavior` is now similar to [`ScrollToOptions`](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions): `x` is renamed to `left` and `y` is renamed to `top`. See [RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0035-router-scroll-position.md).
238+
239+
**Reason**: making the object similar to `ScrollToOptions` to make it feel more familiar with native JS APIs and potentially enable future new options.
240+
241+
### `<router-view>`, `<keep-alive>`, and `<transition>`
242+
243+
`transition` and `keep-alive` must now be used **inside** of `RouterView` via the `v-slot` API:
244+
245+
```vue
246+
<router-view v-slot="{ Component }">
247+
<transition>
248+
<keep-alive>
249+
<component :is="Component" />
250+
</keep-alive>
251+
</transition>
252+
</router-view>
253+
```
254+
255+
**Reason**: This is was a necessary change. See the [related RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0034-router-view-keep-alive-transitions.md).
256+
257+
### Removal of `parent` from route locations
258+
259+
The `parent` property has been removed from normalized route locations (`this.$route` and object returned by `router.resolve`). You can still access it via the `matched` array:
260+
261+
```js
262+
const parent = this.$route.matched[this.$route.matched.length - 2]
263+
```
264+
265+
**Reason**: Having `parent` and `children` creates unnecessary circular references while the properties could be retrieved already through `matched`.
266+
267+
### Removal of `pathToRegexpOptions`
268+
269+
The `pathToRegexpOptions` and `caseSensitive` properties of route records have been replaced with `sensitive` and `strict` options for `createRouter()`. They can now also be directly passed when creating the router with `createRouter()`. Any other option specific to `path-to-regexp` has been removed as `path-to-regexp` is no longer used to parse paths.
270+
271+
### TypeScript
272+
273+
To make typings more consistent and expressive, some types have been renamed.
68274

69-
**Reason**: This is to make things consistent
275+
| `vue-router@3` | `vue-router@4` |
276+
| -------------- | ----------------------- |
277+
| RouteConfig | RouteRecordRaw |
278+
| Location | RouteLocation |
279+
| Route | RouteLocationNormalized |

0 commit comments

Comments
 (0)