Skip to content

docs: add navigation failures #3220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 74 additions & 57 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,39 @@ module.exports = {
description: 'Vue.js 공식 라우터'
},
'/fr/': {
lang: 'fr',
title: 'Vue Router',
description: 'Routeur officiel pour Vue.Js'
lang: 'fr',
title: 'Vue Router',
description: 'Routeur officiel pour Vue.Js'
}
},
head: [
['link', { rel: 'icon', href: `/logo.png` }],
['link', { rel: 'apple-touch-icon', href: `/icons/apple-touch-icon-152x152.png` }],
['link', { rel: 'mask-icon', href: '/icons/safari-pinned-tab.svg', color: '#3eaf7c' }],
['meta', { name: 'msapplication-TileImage', content: '/icons/msapplication-icon-144x144.png' }],
[
'link',
{ rel: 'apple-touch-icon', href: `/icons/apple-touch-icon-152x152.png` }
],
[
'link',
{
rel: 'mask-icon',
href: '/icons/safari-pinned-tab.svg',
color: '#3eaf7c'
}
],
[
'meta',
{
name: 'msapplication-TileImage',
content: '/icons/msapplication-icon-144x144.png'
}
]
],
serviceWorker: true,
theme: 'vue',
themeConfig: {
algolia: {
apiKey: 'f854bb46d3de7eeb921a3b9173bd0d4c',
indexName: 'vue-router',
indexName: 'vue-router'
},
repo: 'vuejs/vue-router',
docsDir: 'docs',
Expand Down Expand Up @@ -92,7 +108,8 @@ module.exports = {
'/guide/advanced/transitions.md',
'/guide/advanced/data-fetching.md',
'/guide/advanced/scroll-behavior.md',
'/guide/advanced/lazy-loading.md'
'/guide/advanced/lazy-loading.md',
'/guide/advanced/navigation-failures.md'
]
}
]
Expand Down Expand Up @@ -298,55 +315,55 @@ module.exports = {
]
},
'/fr/': {
label: 'Français',
selectText: 'Langues',
editLinkText: 'Editer cette page sur Github',
nav: [
{
text: 'Guide',
link: '/fr/guide/'
},
{
text: 'API',
link: '/fr/api/'
},
{
text: 'Notes de version',
link: 'https://github.com/vuejs/vue-router/releases'
}
],
sidebar: [
'/fr/installation.md',
'/fr/',
{
title: 'Essentiels',
collapsable: false,
children: [
'/fr/guide/',
'/fr/guide/essentials/dynamic-matching.md',
'/fr/guide/essentials/nested-routes.md',
'/fr/guide/essentials/navigation.md',
'/fr/guide/essentials/named-routes.md',
'/fr/guide/essentials/named-views.md',
'/fr/guide/essentials/redirect-and-alias.md',
'/fr/guide/essentials/passing-props.md',
'/fr/guide/essentials/history-mode.md'
]
},
{
title: 'Avancés',
collapsable: false,
children: [
'/fr/guide/advanced/navigation-guards.md',
'/fr/guide/advanced/meta.md',
'/fr/guide/advanced/transitions.md',
'/fr/guide/advanced/data-fetching.md',
'/fr/guide/advanced/scroll-behavior.md',
'/fr/guide/advanced/lazy-loading.md'
]
}
]
},
label: 'Français',
selectText: 'Langues',
editLinkText: 'Editer cette page sur Github',
nav: [
{
text: 'Guide',
link: '/fr/guide/'
},
{
text: 'API',
link: '/fr/api/'
},
{
text: 'Notes de version',
link: 'https://github.com/vuejs/vue-router/releases'
}
],
sidebar: [
'/fr/installation.md',
'/fr/',
{
title: 'Essentiels',
collapsable: false,
children: [
'/fr/guide/',
'/fr/guide/essentials/dynamic-matching.md',
'/fr/guide/essentials/nested-routes.md',
'/fr/guide/essentials/navigation.md',
'/fr/guide/essentials/named-routes.md',
'/fr/guide/essentials/named-views.md',
'/fr/guide/essentials/redirect-and-alias.md',
'/fr/guide/essentials/passing-props.md',
'/fr/guide/essentials/history-mode.md'
]
},
{
title: 'Avancés',
collapsable: false,
children: [
'/fr/guide/advanced/navigation-guards.md',
'/fr/guide/advanced/meta.md',
'/fr/guide/advanced/transitions.md',
'/fr/guide/advanced/data-fetching.md',
'/fr/guide/advanced/scroll-behavior.md',
'/fr/guide/advanced/lazy-loading.md'
]
}
]
}
}
}
}
60 changes: 60 additions & 0 deletions docs/guide/advanced/navigation-failures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Navigation Failures

> New in 3.4.0

When using `router-link`, Vue Router calls `router.push` to trigger a navigation. While the expected behavior for most links is to navigate a user to a new page, there are a few situations where users will remain on the same page:

- Users are already on the page that they are trying to navigate to
- A [navigation guard](./navigation-guards.md) aborts the navigation by calling `next(false)`
- A [navigation guard](./navigation-guards.md) throws an error or calls `next(new Error())`

When using a `router-link` component, **none of these failures will log an error**. However, if you are using `router.push` or `router.replace`, you might come across an _"Uncaught (in promise) Error"_ message followed by a more specific message in your console. Let's understand how to differentiate _Navigation Failures_.

::: tip Background story
In v3.2.0, _Navigation Failures_ were exposed through the two optional callbacks of `router.push`: `onComplete` and `onAbort`. Since version 3.1.0, `router.push` and `router.replace` return a _Promise_ if no `onComplete`/`onAbort` callback is provided. This _Promise_ resolves instead of invoking `onComplete` and rejects instead of invoking `onAbort`.
:::

## Detecting Navigation Failures

_Navigation Failures_ are `Error` instances with a few extra properties. To check if an error comes from the Router, use the `isNavigationFailure` function:

```js
import { NavigationFailureType, isNavigationFailure } from 'vue-router'

// trying to access the admin page
router.push('/admin').catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
// show a small notification to the user
showToast('Login in order to access the admin panel')
}
})
```

::: tip
If you omit the second parameter: `isNavigationFailure(failure)`, it will only check if the error is a _Navigation Failure_.
:::

## `NavigationFailureType`

`NavigationFailureType` help developers to differentiate between the various types of _Navigation Failures_. There are four different types:

- `redirected`: `next(newLocation)` was called inside of a navigation guard to redirect somewhere else.
- `aborted`: `next(false)` was called inside of a navigation guard to the navigation.
- `cancelled`: A new navigation completely took place before the current navigation could finish. e.g. `router.push` was called while waiting inside of a navigation guard.
- `duplicated`: The navigation was prevented because we are already at the target location.

## _Navigation Failures_'s properties

All navigation failures expose `to` and `from` properties to reflect the current location as well as the target location for the navigation that failed:

```js
// trying to access the admin page
router.push('/admin').catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
failure.to.path // '/admin'
failure.from.path // '/'
}
})
```

In all cases, `to` and `from` are normalized route locations.
5 changes: 2 additions & 3 deletions src/history/abstract.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import type Router from '../index'
import { History } from './base'
import { isRouterError } from '../util/warn'
import { NavigationFailureType } from './errors'
import { NavigationFailureType, isNavigationFailure } from '../util/errors'

export class AbstractHistory extends History {
index: number
Expand Down Expand Up @@ -51,7 +50,7 @@ export class AbstractHistory extends History {
this.updateRoute(route)
},
err => {
if (isRouterError(err, NavigationFailureType.duplicated)) {
if (isNavigationFailure(err, NavigationFailureType.duplicated)) {
this.index = targetIndex
}
}
Expand Down
16 changes: 11 additions & 5 deletions src/history/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { _Vue } from '../install'
import type Router from '../index'
import { inBrowser } from '../util/dom'
import { runQueue } from '../util/async'
import { warn, isError, isRouterError } from '../util/warn'
import { warn } from '../util/warn'
import { START, isSameRoute } from '../util/route'
import {
flatten,
Expand All @@ -16,8 +16,10 @@ import {
createNavigationCancelledError,
createNavigationRedirectedError,
createNavigationAbortedError,
isError,
isNavigationFailure,
NavigationFailureType
} from './errors'
} from '../util/errors'

export class History {
router: Router
Expand All @@ -35,7 +37,11 @@ export class History {
// implemented by sub-classes
+go: (n: number) => void
+push: (loc: RawLocation, onComplete?: Function, onAbort?: Function) => void
+replace: (loc: RawLocation, onComplete?: Function, onAbort?: Function) => void
+replace: (
loc: RawLocation,
onComplete?: Function,
onAbort?: Function
) => void
+ensureURL: (push?: boolean) => void
+getCurrentLocation: () => string
+setupListeners: Function
Expand Down Expand Up @@ -115,7 +121,7 @@ export class History {
this.ready = true
// Initial redirection should still trigger the onReady onSuccess
// https://github.com/vuejs/vue-router/issues/3225
if (!isRouterError(err, NavigationFailureType.redirected)) {
if (!isNavigationFailure(err, NavigationFailureType.redirected)) {
this.readyErrorCbs.forEach(cb => {
cb(err)
})
Expand All @@ -135,7 +141,7 @@ export class History {
// changed after adding errors with
// https://github.com/vuejs/vue-router/pull/3047 before that change,
// redirect and aborted navigation would produce an err == null
if (!isRouterError(err) && isError(err)) {
if (!isNavigationFailure(err) && isError(err)) {
if (this.errorCbs.length) {
this.errorCbs.forEach(cb => {
cb(err)
Expand Down
Loading