From bee5d73a065a27bfa5de8492cd828d4cc00b7f34 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Sat, 13 Jun 2020 11:05:52 +0200 Subject: [PATCH 01/32] chore(changelog): 3.3.4 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccc53a245..7e5e25451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [3.3.4](https://github.com/vuejs/vue-router/compare/v3.3.3...v3.3.4) (2020-06-13) + + +### Bug Fixes + +* **matcher:** navigate to same as current location ([62598b9](https://github.com/vuejs/vue-router/commit/62598b9)), closes [#3216](https://github.com/vuejs/vue-router/issues/3216) +* **types:** missing children ([c1df447](https://github.com/vuejs/vue-router/commit/c1df447)), closes [#3230](https://github.com/vuejs/vue-router/issues/3230) + + + ## [3.3.3](https://github.com/vuejs/vue-router/compare/v3.3.2...v3.3.3) (2020-06-12) From 84398aedac8b9203b236a7c128ebc7d10a04e4a5 Mon Sep 17 00:00:00 2001 From: Calvin Liang Date: Thu, 18 Jun 2020 06:38:36 -0700 Subject: [PATCH 02/32] fix(scroll): run scrollBehavior on initial load (fix #3196) (#3199) * Add handleScroll on initial history.transitionTo * Update index.js * test(scroll-behavior): add tests for scrollBehavior on load * refactor(scroll-behavior): scrollBehavior on load * style: remove spaces * fix: handleScroll not called with from * test(scroll-behavior): remove redundant tests * fix: call handleScroll only if initial navigation succeeds Co-authored-by: Eduardo San Martin Morote --- src/index.js | 13 ++++++++++++- test/e2e/specs/scroll-behavior.js | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index d0fb34b7b..ae3b69e15 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,7 @@ import { cleanPath } from './util/path' import { createMatcher } from './create-matcher' import { normalizeLocation } from './util/location' import { supportsPushState } from './util/push-state' +import { handleScroll } from './util/scroll' import { HashHistory } from './history/hash' import { HTML5History } from './history/html5' @@ -117,8 +118,18 @@ export default class VueRouter { const history = this.history if (history instanceof HTML5History || history instanceof HashHistory) { - const setupListeners = () => { + const handleInitialScroll = (routeOrError) => { + const from = history.current + const expectScroll = this.options.scrollBehavior + const supportsScroll = supportsPushState && expectScroll + + if (supportsScroll && 'fullPath' in routeOrError) { + handleScroll(this, routeOrError, from, false) + } + } + const setupListeners = (routeOrError) => { history.setupListeners() + handleInitialScroll(routeOrError) } history.transitionTo(history.getCurrentLocation(), setupListeners, setupListeners) } diff --git a/test/e2e/specs/scroll-behavior.js b/test/e2e/specs/scroll-behavior.js index 12119ed0b..db317d591 100644 --- a/test/e2e/specs/scroll-behavior.js +++ b/test/e2e/specs/scroll-behavior.js @@ -119,6 +119,20 @@ module.exports = { null, 'scroll to anchor that starts with number' ) + + .url('http://localhost:8080/scroll-behavior/bar#anchor') + .execute(function () { + location.reload(true) + }) + .waitForElementVisible('#app', 1000) + .assert.evaluate( + function () { + return document.getElementById('anchor').getBoundingClientRect().top < 1 + }, + null, + 'scroll to anchor on load' + ) + .end() } } From f0d9c2d93cf06b5b2202bd77df6281cf47970b20 Mon Sep 17 00:00:00 2001 From: MohamedAhmed12 Date: Thu, 18 Jun 2020 15:56:51 +0200 Subject: [PATCH 03/32] fix(query): cast query values to strings (fix #2131) (#3232) * fix(query): Fix query props should be casted into string (fix #2131) * Apply suggestions from code review * Update test/unit/specs/query.spec.js Co-authored-by: mohamed.gad Co-authored-by: Eduardo San Martin Morote --- src/util/query.js | 3 ++- test/unit/specs/query.spec.js | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/util/query.js b/src/util/query.js index b9abc211f..b2c5286a5 100644 --- a/src/util/query.js +++ b/src/util/query.js @@ -29,7 +29,8 @@ export function resolveQuery ( parsedQuery = {} } for (const key in extraQuery) { - parsedQuery[key] = extraQuery[key] + const value = extraQuery[key] + parsedQuery[key] = Array.isArray(value) ? value.map(v => '' + v) : '' + value } return parsedQuery } diff --git a/test/unit/specs/query.spec.js b/test/unit/specs/query.spec.js index 22c1e6c74..58060ff2c 100644 --- a/test/unit/specs/query.spec.js +++ b/test/unit/specs/query.spec.js @@ -19,6 +19,15 @@ describe('Query utils', () => { arr: ['1', null, '2'] }) }) + + it('should cast query values into string', () => { + const query = resolveQuery('foo=bar&foo=k', { baz: 1 }) + expect(query.baz).toBe('1') + }) + it('should cast query array values into string', () => { + const query = resolveQuery('foo=bar&foo=k', { baz: [1, '2'] }) + expect(query.baz).toEqual(['1', '2']) + }) }) describe('stringifyQuery', () => { From 40e4df739e2fab759e8b10a6648a052951482a1d Mon Sep 17 00:00:00 2001 From: Raul Cabello Martin Date: Fri, 3 Jul 2020 21:49:20 +0200 Subject: [PATCH 04/32] feat(errors): capture errors thrown in redirect callback in onError (#3251) * feat(errors): capture errors thrown in redirect callback in onError Resolves #3201 * feat(errors): capture errors thrown in redirect callback in onError try catch just router.match Resolves #3201 * feat(errors): capture errors thrown in redirect callback in onError remove unnecessary if Resolves #3201 Co-authored-by: Raul Cabello --- src/history/base.js | 12 ++++++++++- test/unit/specs/error-handling.spec.js | 29 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/history/base.js b/src/history/base.js index 4bfa0722b..a070f27c6 100644 --- a/src/history/base.js +++ b/src/history/base.js @@ -77,7 +77,17 @@ export class History { onComplete?: Function, onAbort?: Function ) { - const route = this.router.match(location, this.current) + let route + try { + route = this.router.match(location, this.current) + } catch (e) { + this.errorCbs.forEach(cb => { + cb(e) + }) + // Exception should still be thrown + // https://github.com/vuejs/vue-router/issues/3201 + throw e + } this.confirmTransition( route, () => { diff --git a/test/unit/specs/error-handling.spec.js b/test/unit/specs/error-handling.spec.js index d0d6f6e1d..d8784aa5e 100644 --- a/test/unit/specs/error-handling.spec.js +++ b/test/unit/specs/error-handling.spec.js @@ -183,4 +183,33 @@ describe('error handling', () => { done() }) }) + + it('should trigger onError when an exception is thrown', done => { + const config = [{ + path: '/oldpath/:part', + redirect: (to) => { + if (to.ooopsmistake.part) { + return `/newpath/${to.params.part}` + } + return '/newpath/' + } + }] + + const router = new VueRouter({ + routes: config + }) + + const onError = jasmine.createSpy('onError') + router.onError(onError) + const pushCatch = jasmine.createSpy('pushCatch') + + router + .push('/oldpath/test') + .catch(pushCatch) + .finally(() => { + expect(pushCatch).toHaveBeenCalled() + expect(onError).toHaveBeenCalled() + done() + }) + }) }) From b0ef4974aa4a8a6a4865717f1fad50954379800a Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Fri, 3 Jul 2020 23:16:21 +0200 Subject: [PATCH 05/32] test: improve redirect error test --- src/history/base.js | 2 +- test/unit/specs/error-handling.spec.js | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/history/base.js b/src/history/base.js index a070f27c6..7425df13a 100644 --- a/src/history/base.js +++ b/src/history/base.js @@ -78,6 +78,7 @@ export class History { onAbort?: Function ) { let route + // catch redirect option https://github.com/vuejs/vue-router/issues/3201 try { route = this.router.match(location, this.current) } catch (e) { @@ -85,7 +86,6 @@ export class History { cb(e) }) // Exception should still be thrown - // https://github.com/vuejs/vue-router/issues/3201 throw e } this.confirmTransition( diff --git a/test/unit/specs/error-handling.spec.js b/test/unit/specs/error-handling.spec.js index d8784aa5e..003dad6f9 100644 --- a/test/unit/specs/error-handling.spec.js +++ b/test/unit/specs/error-handling.spec.js @@ -184,14 +184,12 @@ describe('error handling', () => { }) }) - it('should trigger onError when an exception is thrown', done => { + it('should trigger onError if error is thrown inside redirect option', done => { + const error = new Error('foo') const config = [{ path: '/oldpath/:part', redirect: (to) => { - if (to.ooopsmistake.part) { - return `/newpath/${to.params.part}` - } - return '/newpath/' + throw error } }] @@ -207,8 +205,8 @@ describe('error handling', () => { .push('/oldpath/test') .catch(pushCatch) .finally(() => { - expect(pushCatch).toHaveBeenCalled() - expect(onError).toHaveBeenCalled() + expect(pushCatch).toHaveBeenCalledWith(error) + expect(onError).toHaveBeenCalledWith(error) done() }) }) From e9703f20c5c7d3173cc835caa78c2cdd221b716b Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 17 Jul 2020 18:00:37 -0400 Subject: [PATCH 06/32] chore: bump docs theme --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 730d54485..1163dea8e 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "vue-loader": "^15.2.1", "vue-template-compiler": "^2.5.16", "vuepress": "^0.14.11", - "vuepress-theme-vue": "^1.1.0", + "vuepress-theme-vue": "^1.1.1", "webpack": "^4.35.2", "webpack-dev-middleware": "^3.7.0", "yorkie": "^2.0.0" diff --git a/yarn.lock b/yarn.lock index 309e4edb7..9c2583c23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11005,10 +11005,10 @@ vuepress-html-webpack-plugin@^3.2.0: toposort "^1.0.0" util.promisify "1.0.0" -vuepress-theme-vue@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vuepress-theme-vue/-/vuepress-theme-vue-1.1.0.tgz#8b109e3db8cad7b18459f4d77076808110c2bcaa" - integrity sha512-oYbfWkeaQ6aUU8sl8fMzLnBnvsd23V0wkiqI0bR3Tq3mlI8EJu26PFOyyF7KvJu0qNo/lcL7CDSmX7ymUVpnvw== +vuepress-theme-vue@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/vuepress-theme-vue/-/vuepress-theme-vue-1.1.1.tgz#9fb14859cf1eecd18b44fed88e7f51e6573b437a" + integrity sha512-ZXe7TgJ9kW1LnLGi7772U4h27wdZqXuU3PlEq1rR7zwuxuszHIjn3zyxdQzbxVCChzaYz4dRMg1X4lVSnPn4vA== vuepress@^0.14.11: version "0.14.11" From 83920c93333486bb94edfb409118702d645c81bf Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 21 Jul 2020 14:38:58 +0100 Subject: [PATCH 07/32] fix(types): add missing `options` property type (#3248) --- types/router.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/router.d.ts b/types/router.d.ts index 384a7424b..de0e327fe 100644 --- a/types/router.d.ts +++ b/types/router.d.ts @@ -21,6 +21,7 @@ export declare class VueRouter { constructor(options?: RouterOptions) app: Vue + options: RouterOptions; mode: RouterMode currentRoute: Route From 94606a790278360334205a4d8ad4f9378fa94eb9 Mon Sep 17 00:00:00 2001 From: catch on me <906920658@qq.com> Date: Tue, 28 Jul 2020 15:31:48 +0800 Subject: [PATCH 08/32] chore: typo (#3267) --- src/components/view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/view.js b/src/components/view.js index e24d6fa4e..de764fdf5 100644 --- a/src/components/view.js +++ b/src/components/view.js @@ -97,7 +97,7 @@ export default { } const configProps = matched.props && matched.props[name] - // save route and configProps in cachce + // save route and configProps in cache if (configProps) { extend(cache[name], { route, From 347eb238f1290068f0a898025b36a253dfa70b9a Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Fri, 5 Jun 2020 20:44:32 +0200 Subject: [PATCH 09/32] docs: add navigation failures --- docs/.vuepress/config.js | 131 ++++++++++++--------- docs/guide/advanced/navigation-failures.md | 53 +++++++++ 2 files changed, 127 insertions(+), 57 deletions(-) create mode 100644 docs/guide/advanced/navigation-failures.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 636f2a240..365261eb6 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -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', @@ -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' ] } ] @@ -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' + ] + } + ] + } } } } diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md new file mode 100644 index 000000000..ef6d9bee8 --- /dev/null +++ b/docs/guide/advanced/navigation-failures.md @@ -0,0 +1,53 @@ +# Navigation Failures + +When using `router-link`, Vue Router internally calls `router.push` to trigger a navigation. Depending on the current location and existing [Navigation Guards](./navigation-guards.md), this navigation might end up in a new page being shown, but there are a couple of situations where we will stay on the same page: + +- We are already on the page we are trying to go to +- A navigation guard aborts the navigation by calling `next(false)` +- An error is thrown in a navigation by calling `next(new Error())` + +When using a regular `router-link`, **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. This is part of the _Navigation Failures_ system in Vue Router and it is only available from version 3.2.0 onwards but existed for a long time before: through the two optional callbacks, `onComplete` and `onAbort` that can be passed to `router.push`. 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 `osComplete` and rejects instead of invoking `onAbort`. + +## Detecting Navigation Failures + +_Navigation Failures_ are `Error` instances with a few extra properties. Among them, you can find a `type` property. This will allow you to check the type of the navigation failure: + +```js +import { NavigationFailureTypes } from 'vue-router' + +// trying to access an admin-only route +router.push('/admin').catch(failure => { + if (failure) { + if (failure.type === NavigationFailureTypes.redirected) { + // show a small notification to the user + showToast('Login in order to access the admin panel') + } + } +}) +``` + +## `NavigationFailureTypes` + +`NavigationFailureTypes` exposes the following properties to differentiate _Navigation Failures_: + +- `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 + +Apart from exposing a `type` property, 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 +// given we are at `/` +router.push('/admin').catch(failure => { + if (failure) { + if (failure.type === NavigationFailureTypes.redirected) { + failure.to.path // '/admin' + failure.from.path // '/' + } + } +``` + +In all cases, `from` and `to` are normalized route locations. From 06b5a452399339a0d7ae6105665690cf0d08b380 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 10 Jun 2020 13:20:50 +0200 Subject: [PATCH 10/32] Update docs/guide/advanced/navigation-failures.md Co-authored-by: Natalia Tepluhina --- docs/guide/advanced/navigation-failures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index ef6d9bee8..d746bdff7 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -6,7 +6,7 @@ When using `router-link`, Vue Router internally calls `router.push` to trigger a - A navigation guard aborts the navigation by calling `next(false)` - An error is thrown in a navigation by calling `next(new Error())` -When using a regular `router-link`, **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. This is part of the _Navigation Failures_ system in Vue Router and it is only available from version 3.2.0 onwards but existed for a long time before: through the two optional callbacks, `onComplete` and `onAbort` that can be passed to `router.push`. 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 `osComplete` and rejects instead of invoking `onAbort`. +When using a regular `router-link`, **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. This is part of the _Navigation Failures_ system in Vue Router and it is only available from version 3.2.0 onwards but existed for a long time before: through the two optional callbacks, `onComplete` and `onAbort` that can be passed to `router.push`. 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 From 81f188e2a5491434b3b69fc50b8360d8edda1416 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 13:09:33 +0200 Subject: [PATCH 11/32] Update docs/guide/advanced/navigation-failures.md --- docs/guide/advanced/navigation-failures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index d746bdff7..f9dd432e7 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -4,7 +4,7 @@ When using `router-link`, Vue Router internally calls `router.push` to trigger a - We are already on the page we are trying to go to - A navigation guard aborts the navigation by calling `next(false)` -- An error is thrown in a navigation by calling `next(new Error())` +- A navigation guard throws an error or calls `next(new Error())` When using a regular `router-link`, **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. This is part of the _Navigation Failures_ system in Vue Router and it is only available from version 3.2.0 onwards but existed for a long time before: through the two optional callbacks, `onComplete` and `onAbort` that can be passed to `router.push`. 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`. From b854a20b1f1560b31f1fd37a539b3420fc20350d Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 13:24:24 +0200 Subject: [PATCH 12/32] feat(errors): NavigationDuplicated name for backwards compatibility --- src/history/abstract.js | 2 +- src/history/base.js | 6 ++++-- src/{history => util}/errors.js | 5 ++++- 3 files changed, 9 insertions(+), 4 deletions(-) rename src/{history => util}/errors.js (91%) diff --git a/src/history/abstract.js b/src/history/abstract.js index 1add43d01..6de8ce269 100644 --- a/src/history/abstract.js +++ b/src/history/abstract.js @@ -3,7 +3,7 @@ import type Router from '../index' import { History } from './base' import { isRouterError } from '../util/warn' -import { NavigationFailureType } from './errors' +import { NavigationFailureType } from '../util/errors' export class AbstractHistory extends History { index: number diff --git a/src/history/base.js b/src/history/base.js index 7425df13a..d1c299561 100644 --- a/src/history/base.js +++ b/src/history/base.js @@ -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, @@ -16,8 +16,10 @@ import { createNavigationCancelledError, createNavigationRedirectedError, createNavigationAbortedError, + isError, + isRouterError, NavigationFailureType -} from './errors' +} from '../util/errors' export class History { router: Router diff --git a/src/history/errors.js b/src/util/errors.js similarity index 91% rename from src/history/errors.js rename to src/util/errors.js index 0eaa5a8df..140f181fa 100644 --- a/src/history/errors.js +++ b/src/util/errors.js @@ -17,12 +17,15 @@ export function createNavigationRedirectedError (from, to) { } export function createNavigationDuplicatedError (from, to) { - return createRouterError( + const error = createRouterError( from, to, NavigationFailureType.duplicated, `Avoided redundant navigation to current location: "${from.fullPath}".` ) + // backwards compatible with the first introduction of Errors + error.name = 'NavigationDuplicated' + return error } export function createNavigationCancelledError (from, to) { From 49d3d5108a07a3161e6c165d41b4b8229a9ad49e Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 13:31:37 +0200 Subject: [PATCH 13/32] docs: use singular form --- docs/guide/advanced/navigation-failures.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index f9dd432e7..ef759a89e 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -13,12 +13,12 @@ When using a regular `router-link`, **none of these failures will log an error** _Navigation Failures_ are `Error` instances with a few extra properties. Among them, you can find a `type` property. This will allow you to check the type of the navigation failure: ```js -import { NavigationFailureTypes } from 'vue-router' +import { NavigationFailureType } from 'vue-router' // trying to access an admin-only route router.push('/admin').catch(failure => { if (failure) { - if (failure.type === NavigationFailureTypes.redirected) { + if (failure.type === NavigationFailureType.redirected) { // show a small notification to the user showToast('Login in order to access the admin panel') } @@ -26,9 +26,9 @@ router.push('/admin').catch(failure => { }) ``` -## `NavigationFailureTypes` +## `NavigationFailureType` -`NavigationFailureTypes` exposes the following properties to differentiate _Navigation Failures_: +`NavigationFailureType` exposes the following properties to differentiate _Navigation Failures_: - `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. @@ -43,7 +43,7 @@ Apart from exposing a `type` property, all navigation failures expose `to` and ` // given we are at `/` router.push('/admin').catch(failure => { if (failure) { - if (failure.type === NavigationFailureTypes.redirected) { + if (failure.type === NavigationFailureType.redirected) { failure.to.path // '/admin' failure.from.path // '/' } From 8d92dc017e602a43e0d25aa782e8e9510c9ea1e0 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 13:40:17 +0200 Subject: [PATCH 14/32] feat(errors): expose isRouterError --- docs/guide/advanced/navigation-failures.md | 16 +++-- src/history/abstract.js | 3 +- src/index.js | 80 ++++++++++++---------- src/util/errors.js | 8 +++ src/util/resolve-components.js | 3 +- src/util/warn.js | 7 -- 6 files changed, 63 insertions(+), 54 deletions(-) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index ef759a89e..28274408e 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -10,15 +10,15 @@ When using a regular `router-link`, **none of these failures will log an error** ## Detecting Navigation Failures -_Navigation Failures_ are `Error` instances with a few extra properties. Among them, you can find a `type` property. This will allow you to check the type of the navigation failure: +_Navigation Failures_ are `Error` instances with a few extra properties. To check if an error comes from the Router, use the `isRouterError` function: ```js -import { NavigationFailureType } from 'vue-router' +import { NavigationFailureType, isRouterError } from 'vue-router' -// trying to access an admin-only route +// trying to access the admin page router.push('/admin').catch(failure => { if (failure) { - if (failure.type === NavigationFailureType.redirected) { + if (isRouterError(failure, NavigationFailureType.redirected)) { // show a small notification to the user showToast('Login in order to access the admin panel') } @@ -26,6 +26,10 @@ router.push('/admin').catch(failure => { }) ``` +::: tip +If you omit the second parameter: `isRouterError(failure)`, it will only check if the error comes from the Router. +::: + ## `NavigationFailureType` `NavigationFailureType` exposes the following properties to differentiate _Navigation Failures_: @@ -40,10 +44,10 @@ router.push('/admin').catch(failure => { Apart from exposing a `type` property, 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 -// given we are at `/` +// trying to access the admin page router.push('/admin').catch(failure => { if (failure) { - if (failure.type === NavigationFailureType.redirected) { + if (isRouterError(failure, NavigationFailureType.redirected)) { failure.to.path // '/admin' failure.from.path // '/' } diff --git a/src/history/abstract.js b/src/history/abstract.js index 6de8ce269..6e8410a30 100644 --- a/src/history/abstract.js +++ b/src/history/abstract.js @@ -2,8 +2,7 @@ import type Router from '../index' import { History } from './base' -import { isRouterError } from '../util/warn' -import { NavigationFailureType } from '../util/errors' +import { NavigationFailureType, isRouterError } from '../util/errors' export class AbstractHistory extends History { index: number diff --git a/src/index.js b/src/index.js index ae3b69e15..1debc3016 100644 --- a/src/index.js +++ b/src/index.js @@ -16,22 +16,24 @@ import { AbstractHistory } from './history/abstract' import type { Matcher } from './create-matcher' +import { isRouterError, NavigationFailureType } from './util/errors' + export default class VueRouter { - static install: () => void; - static version: string; - - app: any; - apps: Array; - ready: boolean; - readyCbs: Array; - options: RouterOptions; - mode: string; - history: HashHistory | HTML5History | AbstractHistory; - matcher: Matcher; - fallback: boolean; - beforeHooks: Array; - resolveHooks: Array; - afterHooks: Array; + static install: () => void + static version: string + + app: any + apps: Array + ready: boolean + readyCbs: Array + options: RouterOptions + mode: string + history: HashHistory | HTML5History | AbstractHistory + matcher: Matcher + fallback: boolean + beforeHooks: Array + resolveHooks: Array + afterHooks: Array constructor (options: RouterOptions = {}) { this.app = null @@ -43,7 +45,8 @@ export default class VueRouter { this.matcher = createMatcher(options.routes || [], this) let mode = options.mode || 'hash' - this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false + this.fallback = + mode === 'history' && !supportsPushState && options.fallback !== false if (this.fallback) { mode = 'hash' } @@ -69,11 +72,7 @@ export default class VueRouter { } } - match ( - raw: RawLocation, - current?: Route, - redirectedFrom?: Location - ): Route { + match (raw: RawLocation, current?: Route, redirectedFrom?: Location): Route { return this.matcher.match(raw, current, redirectedFrom) } @@ -82,11 +81,12 @@ export default class VueRouter { } init (app: any /* Vue component instance */) { - process.env.NODE_ENV !== 'production' && assert( - install.installed, - `not installed. Make sure to call \`Vue.use(VueRouter)\` ` + - `before creating root instance.` - ) + process.env.NODE_ENV !== 'production' && + assert( + install.installed, + `not installed. Make sure to call \`Vue.use(VueRouter)\` ` + + `before creating root instance.` + ) this.apps.push(app) @@ -131,11 +131,15 @@ export default class VueRouter { history.setupListeners() handleInitialScroll(routeOrError) } - history.transitionTo(history.getCurrentLocation(), setupListeners, setupListeners) + history.transitionTo( + history.getCurrentLocation(), + setupListeners, + setupListeners + ) } history.listen(route => { - this.apps.forEach((app) => { + this.apps.forEach(app => { app._route = route }) }) @@ -204,11 +208,14 @@ export default class VueRouter { if (!route) { return [] } - return [].concat.apply([], route.matched.map(m => { - return Object.keys(m.components).map(key => { - return m.components[key] + return [].concat.apply( + [], + route.matched.map(m => { + return Object.keys(m.components).map(key => { + return m.components[key] + }) }) - })) + ) } resolve ( @@ -224,12 +231,7 @@ export default class VueRouter { resolved: Route } { current = current || this.history.current - const location = normalizeLocation( - to, - current, - append, - this - ) + const location = normalizeLocation(to, current, append, this) const route = this.match(location, current) const fullPath = route.redirectedFrom || route.fullPath const base = this.history.base @@ -267,6 +269,8 @@ function createHref (base: string, fullPath: string, mode) { VueRouter.install = install VueRouter.version = '__VERSION__' +VueRouter.isRouterError = isRouterError +VueRouter.NavigationFailureType = NavigationFailureType if (inBrowser && window.Vue) { window.Vue.use(VueRouter) diff --git a/src/util/errors.js b/src/util/errors.js index 140f181fa..9bc8b7a14 100644 --- a/src/util/errors.js +++ b/src/util/errors.js @@ -71,3 +71,11 @@ function stringifyRoute (to) { }) return JSON.stringify(location, null, 2) } + +export function isError (err) { + return Object.prototype.toString.call(err).indexOf('Error') > -1 +} + +export function isRouterError (err, errorType) { + return isError(err) && err._isRouter && (errorType == null || err.type === errorType) +} diff --git a/src/util/resolve-components.js b/src/util/resolve-components.js index 3f7608cd5..204e3ca8f 100644 --- a/src/util/resolve-components.js +++ b/src/util/resolve-components.js @@ -1,7 +1,8 @@ /* @flow */ import { _Vue } from '../install' -import { warn, isError } from './warn' +import { warn } from './warn' +import { isError } from '../util/errors' export function resolveAsyncComponents (matched: Array): Function { return (to, from, next) => { diff --git a/src/util/warn.js b/src/util/warn.js index 73e70caf8..025d3b20f 100644 --- a/src/util/warn.js +++ b/src/util/warn.js @@ -12,10 +12,3 @@ export function warn (condition: any, message: string) { } } -export function isError (err: any): boolean { - return Object.prototype.toString.call(err).indexOf('Error') > -1 -} - -export function isRouterError (err: any, errorType: ?string): boolean { - return isError(err) && err._isRouter && (errorType == null || err.type === errorType) -} From 3c446f0e0f42f3bc64a3134606a41a20e9a3fa39 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 13:46:56 +0200 Subject: [PATCH 15/32] refactor: fix flow types --- src/index.js | 2 ++ test/unit/specs/error-handling.spec.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 1debc3016..75dcfeff6 100644 --- a/src/index.js +++ b/src/index.js @@ -21,6 +21,8 @@ import { isRouterError, NavigationFailureType } from './util/errors' export default class VueRouter { static install: () => void static version: string + static isRouterError: Function + static NavigationFailureType: any app: any apps: Array diff --git a/test/unit/specs/error-handling.spec.js b/test/unit/specs/error-handling.spec.js index 003dad6f9..2f0589f4d 100644 --- a/test/unit/specs/error-handling.spec.js +++ b/test/unit/specs/error-handling.spec.js @@ -1,6 +1,6 @@ import Vue from 'vue' import VueRouter from '../../../src/index' -import { NavigationFailureType } from '../../../src/history/errors' +import { NavigationFailureType } from '../../../src/util/errors' Vue.use(VueRouter) From adb3532455e3dfcc9268b4b398605b3143265a5e Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 13:47:16 +0200 Subject: [PATCH 16/32] types(errors): add TS types --- types/router.d.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/types/router.d.ts b/types/router.d.ts index de0e327fe..50780140e 100644 --- a/types/router.d.ts +++ b/types/router.d.ts @@ -61,6 +61,17 @@ export declare class VueRouter { } static install: PluginFunction + static version: string + + static isRouterError: (error: any, type?: NavigationFailureTypeE) => error is Error + static NavigationFailureType: NavigationFailureTypeE +} + +export enum NavigationFailureTypeE { + redirected = 1, + aborted = 2, + cancelled = 3, + duplicated = 4 } type Position = { x: number; y: number } From ba3d8e6d844b10e2b0197e55a0a8376ce5fd99c3 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 14:38:41 +0200 Subject: [PATCH 17/32] docs: add new in comment [skip ci] --- docs/guide/advanced/navigation-failures.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index 28274408e..eb646c3c7 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -1,5 +1,7 @@ # Navigation Failures +> New in 3.4.0 + When using `router-link`, Vue Router internally calls `router.push` to trigger a navigation. Depending on the current location and existing [Navigation Guards](./navigation-guards.md), this navigation might end up in a new page being shown, but there are a couple of situations where we will stay on the same page: - We are already on the page we are trying to go to From cf2a9a92ccfebcc372ef202ee12f01aa26cd9792 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 14:46:07 +0200 Subject: [PATCH 18/32] docs: rename isRouterError [skip ci] --- docs/guide/advanced/navigation-failures.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index eb646c3c7..52ed273e6 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -12,15 +12,15 @@ When using a regular `router-link`, **none of these failures will log an error** ## Detecting Navigation Failures -_Navigation Failures_ are `Error` instances with a few extra properties. To check if an error comes from the Router, use the `isRouterError` function: +_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, isRouterError } from 'vue-router' +import { NavigationFailureType, isNavigationFailure } from 'vue-router' // trying to access the admin page router.push('/admin').catch(failure => { if (failure) { - if (isRouterError(failure, NavigationFailureType.redirected)) { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { // show a small notification to the user showToast('Login in order to access the admin panel') } @@ -29,7 +29,7 @@ router.push('/admin').catch(failure => { ``` ::: tip -If you omit the second parameter: `isRouterError(failure)`, it will only check if the error comes from the Router. +If you omit the second parameter: `isNavigationFailure(failure)`, it will only check if the error comes from the Router. ::: ## `NavigationFailureType` @@ -48,12 +48,11 @@ Apart from exposing a `type` property, all navigation failures expose `to` and ` ```js // trying to access the admin page router.push('/admin').catch(failure => { - if (failure) { - if (isRouterError(failure, NavigationFailureType.redirected)) { - failure.to.path // '/admin' - failure.from.path // '/' - } + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + failure.to.path // '/admin' + failure.from.path // '/' } +}) ``` In all cases, `from` and `to` are normalized route locations. From 9744c654961d2176a34a3be8764b511c003ab538 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 14:49:01 +0200 Subject: [PATCH 19/32] docs: remove extra if [skip ci] --- docs/guide/advanced/navigation-failures.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index 52ed273e6..7fc48aae7 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -19,11 +19,9 @@ import { NavigationFailureType, isNavigationFailure } from 'vue-router' // trying to access the admin page router.push('/admin').catch(failure => { - if (failure) { - if (isNavigationFailure(failure, NavigationFailureType.redirected)) { - // show a small notification to the user - showToast('Login in order to access the admin panel') - } + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + // show a small notification to the user + showToast('Login in order to access the admin panel') } }) ``` @@ -43,7 +41,7 @@ If you omit the second parameter: `isNavigationFailure(failure)`, it will only c ## _Navigation Failures_'s properties -Apart from exposing a `type` property, all navigation failures expose `to` and `from` properties to reflect the current location as well as the target location for the navigation that failed: +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 From 486fb72e023f063532d5e04e350672f0e869222b Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Jun 2020 15:02:20 +0200 Subject: [PATCH 20/32] docs: split background story and explanation [skip ci] --- docs/guide/advanced/navigation-failures.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index 7fc48aae7..427cc607c 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -8,7 +8,11 @@ When using `router-link`, Vue Router internally calls `router.push` to trigger a - A navigation guard aborts the navigation by calling `next(false)` - A navigation guard throws an error or calls `next(new Error())` -When using a regular `router-link`, **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. This is part of the _Navigation Failures_ system in Vue Router and it is only available from version 3.2.0 onwards but existed for a long time before: through the two optional callbacks, `onComplete` and `onAbort` that can be passed to `router.push`. 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`. +When using a regular `router-link`, **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 + _Navigation Failures_ were exposed on version 3.2.0 but existed for a long time before: through the two optional callbacks of `router.push`, `onComplete` and `onAbort` that can be passed to `router.push`. 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 @@ -27,7 +31,7 @@ router.push('/admin').catch(failure => { ``` ::: tip -If you omit the second parameter: `isNavigationFailure(failure)`, it will only check if the error comes from the Router. +If you omit the second parameter: `isNavigationFailure(failure)`, it will only check if the error is a _Navigation Failure_. ::: ## `NavigationFailureType` From 179105f81049f42b5987cfbac657be13046f29a8 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Tue, 30 Jun 2020 16:30:08 +0200 Subject: [PATCH 21/32] Apply suggestions from code review Co-authored-by: Ben Hong --- docs/guide/advanced/navigation-failures.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index 427cc607c..135196c14 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -2,16 +2,16 @@ > New in 3.4.0 -When using `router-link`, Vue Router internally calls `router.push` to trigger a navigation. Depending on the current location and existing [Navigation Guards](./navigation-guards.md), this navigation might end up in a new page being shown, but there are a couple of situations where we will stay on the same page: +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: - We are already on the page we are trying to go to -- A navigation guard aborts the navigation by calling `next(false)` -- A navigation guard throws an error or calls `next(new Error())` +- 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 regular `router-link`, **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 - _Navigation Failures_ were exposed on version 3.2.0 but existed for a long time before: through the two optional callbacks of `router.push`, `onComplete` and `onAbort` that can be passed to `router.push`. 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`. + 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 @@ -36,7 +36,7 @@ If you omit the second parameter: `isNavigationFailure(failure)`, it will only c ## `NavigationFailureType` -`NavigationFailureType` exposes the following properties to differentiate _Navigation Failures_: +`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. @@ -57,4 +57,4 @@ router.push('/admin').catch(failure => { }) ``` -In all cases, `from` and `to` are normalized route locations. +In all cases, `to` and `from` are normalized route locations. From 662915684e1e35e8147a845240dfbfafbe76aecd Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Fri, 3 Jul 2020 23:10:20 +0200 Subject: [PATCH 22/32] refactor: adapt values for errors --- src/util/errors.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/errors.js b/src/util/errors.js index 9bc8b7a14..355f2cb19 100644 --- a/src/util/errors.js +++ b/src/util/errors.js @@ -1,8 +1,8 @@ export const NavigationFailureType = { - redirected: 1, - aborted: 2, - cancelled: 3, - duplicated: 4 + redirected: 2, + aborted: 4, + cancelled: 8, + duplicated: 16 } export function createNavigationRedirectedError (from, to) { From b65104098fe66189b2ef0b82cc9f6ead81dfdd75 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Fri, 31 Jul 2020 21:01:08 +0200 Subject: [PATCH 23/32] Apply suggestions from code review Co-authored-by: Ben Hong --- docs/guide/advanced/navigation-failures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index 135196c14..d784a4aec 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -4,11 +4,11 @@ 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: -- We are already on the page we are trying to go to +- 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 regular `router-link`, **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_. +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`. From 9f5989e4777a6beca19f4354d438d57dfc7fb01a Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Fri, 31 Jul 2020 21:04:16 +0200 Subject: [PATCH 24/32] refactor: isRouterError -> isNavigationFailure --- src/history/abstract.js | 4 ++-- src/history/base.js | 12 ++++++++---- src/index.js | 10 +++++----- src/util/errors.js | 8 ++++++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/history/abstract.js b/src/history/abstract.js index 6e8410a30..325e4b954 100644 --- a/src/history/abstract.js +++ b/src/history/abstract.js @@ -2,7 +2,7 @@ import type Router from '../index' import { History } from './base' -import { NavigationFailureType, isRouterError } from '../util/errors' +import { NavigationFailureType, isNavigationFailure } from '../util/errors' export class AbstractHistory extends History { index: number @@ -50,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 } } diff --git a/src/history/base.js b/src/history/base.js index d1c299561..072cfb8e8 100644 --- a/src/history/base.js +++ b/src/history/base.js @@ -17,7 +17,7 @@ import { createNavigationRedirectedError, createNavigationAbortedError, isError, - isRouterError, + isNavigationFailure, NavigationFailureType } from '../util/errors' @@ -37,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 @@ -117,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) }) @@ -137,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) diff --git a/src/index.js b/src/index.js index 75dcfeff6..22ecff96d 100644 --- a/src/index.js +++ b/src/index.js @@ -16,12 +16,12 @@ import { AbstractHistory } from './history/abstract' import type { Matcher } from './create-matcher' -import { isRouterError, NavigationFailureType } from './util/errors' +import { isNavigationFailure, NavigationFailureType } from './util/errors' export default class VueRouter { static install: () => void static version: string - static isRouterError: Function + static isNavigationFailure: Function static NavigationFailureType: any app: any @@ -120,7 +120,7 @@ export default class VueRouter { const history = this.history if (history instanceof HTML5History || history instanceof HashHistory) { - const handleInitialScroll = (routeOrError) => { + const handleInitialScroll = routeOrError => { const from = history.current const expectScroll = this.options.scrollBehavior const supportsScroll = supportsPushState && expectScroll @@ -129,7 +129,7 @@ export default class VueRouter { handleScroll(this, routeOrError, from, false) } } - const setupListeners = (routeOrError) => { + const setupListeners = routeOrError => { history.setupListeners() handleInitialScroll(routeOrError) } @@ -271,7 +271,7 @@ function createHref (base: string, fullPath: string, mode) { VueRouter.install = install VueRouter.version = '__VERSION__' -VueRouter.isRouterError = isRouterError +VueRouter.isNavigationFailure = isNavigationFailure VueRouter.NavigationFailureType = NavigationFailureType if (inBrowser && window.Vue) { diff --git a/src/util/errors.js b/src/util/errors.js index 355f2cb19..bacf6881e 100644 --- a/src/util/errors.js +++ b/src/util/errors.js @@ -76,6 +76,10 @@ export function isError (err) { return Object.prototype.toString.call(err).indexOf('Error') > -1 } -export function isRouterError (err, errorType) { - return isError(err) && err._isRouter && (errorType == null || err.type === errorType) +export function isNavigationFailure (err, errorType) { + return ( + isError(err) && + err._isRouter && + (errorType == null || err.type === errorType) + ) } From dfc289202703319cf7beb38d03c9258c806c4d62 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Fri, 31 Jul 2020 21:09:47 +0200 Subject: [PATCH 25/32] refactor: add missing type rename [skip ci] --- types/router.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/router.d.ts b/types/router.d.ts index 50780140e..07f912eb6 100644 --- a/types/router.d.ts +++ b/types/router.d.ts @@ -63,7 +63,7 @@ export declare class VueRouter { static install: PluginFunction static version: string - static isRouterError: (error: any, type?: NavigationFailureTypeE) => error is Error + static isNavigationFailure: (error: any, type?: NavigationFailureTypeE) => error is Error static NavigationFailureType: NavigationFailureTypeE } From 2c47d3cec240f35604872c6a14a5d06012fd6bb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2020 11:29:37 +0200 Subject: [PATCH 26/32] build(deps): bump elliptic from 6.5.0 to 6.5.3 [skip-ci] (#3269) Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.0 to 6.5.3. - [Release notes](https://github.com/indutny/elliptic/releases) - [Commits](https://github.com/indutny/elliptic/compare/v6.5.0...v6.5.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9c2583c23..51e3e43a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2329,9 +2329,9 @@ bluebird@^3.5.0: integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" - integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== body-parser@1.19.0: version "1.19.0" @@ -4082,9 +4082,9 @@ elegant-spinner@^1.0.1: integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= elliptic@^6.0.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca" - integrity sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg== + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== dependencies: bn.js "^4.4.0" brorand "^1.0.1" From d2740c115ddf12080972029dc7b3f275e3d26072 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2020 11:29:55 +0200 Subject: [PATCH 27/32] build(deps): bump lodash from 4.17.15 to 4.17.19 [skip-ci] (#3259) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 51e3e43a5..aa4a92bbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6860,9 +6860,9 @@ lodash.uniq@^4.5.0: integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= lodash@^4.14.14, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== log-symbols@2.2.0, log-symbols@^2.1.0, log-symbols@^2.2.0: version "2.2.0" From fe41809f0840d1063856e2cebc2bda151341862e Mon Sep 17 00:00:00 2001 From: Potpot <136290354@qq.com> Date: Mon, 3 Aug 2020 18:15:43 +0800 Subject: [PATCH 28/32] docs(zh): add more details to resolution flow [skip ci] (#2710) * add more details to resolution flow * Apply suggestions from code review [skip ci] Co-authored-by: GU Yiling Co-authored-by: Eduardo San Martin Morote Co-authored-by: GU Yiling --- docs/zh/guide/advanced/navigation-guards.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/guide/advanced/navigation-guards.md b/docs/zh/guide/advanced/navigation-guards.md index 21d7937b8..210f88bac 100644 --- a/docs/zh/guide/advanced/navigation-guards.md +++ b/docs/zh/guide/advanced/navigation-guards.md @@ -172,4 +172,4 @@ beforeRouteLeave (to, from, next) { 9. 导航被确认。 10. 调用全局的 `afterEach` 钩子。 11. 触发 DOM 更新。 -12. 用创建好的实例调用 `beforeRouteEnter` 守卫中传给 `next` 的回调函数。 +12. 调用 `beforeRouteEnter` 守卫中传给 `next` 的回调函数,创建好的组件实例会作为回调函数的参数传入。 From ea03de16e18218c55203ba497365ad470ebd8e29 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 5 Aug 2020 10:36:07 +0200 Subject: [PATCH 29/32] feat: add vetur tags and attributes --- package.json | 8 +++++++- vetur/attributes.json | 38 ++++++++++++++++++++++++++++++++++++++ vetur/tags.json | 20 ++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 vetur/attributes.json create mode 100644 vetur/tags.json diff --git a/package.json b/package.json index 1163dea8e..fa6cff2b1 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,14 @@ "files": [ "src", "dist/*.js", - "types/*.d.ts" + "types/*.d.ts", + "vetur/tags.json", + "vetur/attributes.json" ], + "vetur": { + "tags": "vetur/tags.json", + "attributes": "vetur/attributes.json" + }, "keywords": [ "vue", "router", diff --git a/vetur/attributes.json b/vetur/attributes.json new file mode 100644 index 000000000..4a3e30105 --- /dev/null +++ b/vetur/attributes.json @@ -0,0 +1,38 @@ +{ + "name": { + "type": "string", + "description": "When a `` has a `name` prop, it will render the component with the corresponding name in the matched route record's components option." + }, + "to": { + "description": "Denotes the target route of the link. When clicked, the value of the `to` prop will be internally passed to `router.push()`, so the value can be either a string or a location descriptor object." + }, + "append": { + "type": "boolean", + "description": "Setting the append prop always appends the relative path to the current path. For example, assuming we are navigating from /a to a relative link b, without append we will end up at /b, but with append we will end up at /a/b." + }, + "tag": { + "description": "Specify which tag to render to, and it will still listen to click events for navigation. By default, an `a` tag is rendered." + }, + "event": { + "description": "Specify the event(s) that can trigger the link navigation. By default, the `click` event triggers a navigation." + }, + "replace": { + "type": "boolean", + "description": "Call `router.replace()` instead of `router.push()` when the link is clicked, so the navigation replaces the current history entry." + }, + "exact": { + "description": "The default active class matching behavior is inclusive match. For example, `` will get this class applied as long as the current path starts with /a/ or is /a.\nOne consequence of this is that `` will be active for every route! To force the link into \"exact match mode\", use the exact prop: ``" + }, + "active-class": { + "type": "string", + "description": "Configure the active CSS class applied when the link is active. Note the default value can also be configured globally via the `linkActiveClass` router constructor option." + }, + "exact-active-class": { + "type": "string", + "description": "Configure the active CSS class applied when the link is exactly active. Note the default value can also be configured globally via the `linkExactActiveClass` router constructor option." + }, + "aria-current-value": { + "options": ["page", "step", "location", "date", "time", "true", "false"], + "description": "Configure the value of `aria-current` when the link is active with exact match. It must be one of the [allowed values for `aria-current`](https://www.w3.org/TR/wai-aria-1.2/#aria-current) in the ARIA spec. In most cases, the default of `page` should be the best fit." + } +} diff --git a/vetur/tags.json b/vetur/tags.json new file mode 100644 index 000000000..bc31049f8 --- /dev/null +++ b/vetur/tags.json @@ -0,0 +1,20 @@ +{ + "router-view": { + "attributes": ["name"], + "description": "Component that renders the matched component for the current location. Components rendered by `` can also contain their own `` to render nested routes." + }, + "router-link": { + "attributes": [ + "to", + "replace", + "append", + "tag", + "active-class", + "exact", + "event", + "exact-active-class", + "aria-current-value" + ], + "description": "Component that renders an `` with the correct `href` attribute and click listeners to trigger a local navigation when clicked. Can also customize its rendering by providing the `custom` prop and using its `v-slot` API." + } +} From 8d5ddb01e6318fe72332c5655cfb5fc67b264514 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 5 Aug 2020 10:37:36 +0200 Subject: [PATCH 30/32] build: bundle 3.4.0 --- dist/vue-router.common.js | 244 +++++++++++++++------------ dist/vue-router.esm.browser.js | 256 ++++++++++++++++------------- dist/vue-router.esm.browser.min.js | 4 +- dist/vue-router.esm.js | 244 +++++++++++++++------------ dist/vue-router.js | 244 +++++++++++++++------------ dist/vue-router.min.js | 4 +- 6 files changed, 555 insertions(+), 441 deletions(-) diff --git a/dist/vue-router.common.js b/dist/vue-router.common.js index fbad4a93b..648177211 100644 --- a/dist/vue-router.common.js +++ b/dist/vue-router.common.js @@ -1,5 +1,5 @@ /*! - * vue-router v3.3.4 + * vue-router v3.4.0 * (c) 2020 Evan You * @license MIT */ @@ -19,14 +19,6 @@ function warn (condition, message) { } } -function isError (err) { - return Object.prototype.toString.call(err).indexOf('Error') > -1 -} - -function isRouterError (err, errorType) { - return isError(err) && err._isRouter && (errorType == null || err.type === errorType) -} - function extend (a, b) { for (var key in b) { a[key] = b[key]; @@ -135,7 +127,7 @@ var View = { }; var configProps = matched.props && matched.props[name]; - // save route and configProps in cachce + // save route and configProps in cache if (configProps) { extend(cache[name], { route: route, @@ -217,7 +209,8 @@ function resolveQuery ( parsedQuery = {}; } for (var key in extraQuery) { - parsedQuery[key] = extraQuery[key]; + var value = extraQuery[key]; + parsedQuery[key] = Array.isArray(value) ? value.map(function (v) { return '' + v; }) : '' + value; } return parsedQuery } @@ -1906,6 +1899,88 @@ function runQueue (queue, fn, cb) { step(0); } +var NavigationFailureType = { + redirected: 2, + aborted: 4, + cancelled: 8, + duplicated: 16 +}; + +function createNavigationRedirectedError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.redirected, + ("Redirected when going from \"" + (from.fullPath) + "\" to \"" + (stringifyRoute( + to + )) + "\" via a navigation guard.") + ) +} + +function createNavigationDuplicatedError (from, to) { + var error = createRouterError( + from, + to, + NavigationFailureType.duplicated, + ("Avoided redundant navigation to current location: \"" + (from.fullPath) + "\".") + ); + // backwards compatible with the first introduction of Errors + error.name = 'NavigationDuplicated'; + return error +} + +function createNavigationCancelledError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.cancelled, + ("Navigation cancelled from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" with a new navigation.") + ) +} + +function createNavigationAbortedError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.aborted, + ("Navigation aborted from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" via a navigation guard.") + ) +} + +function createRouterError (from, to, type, message) { + var error = new Error(message); + error._isRouter = true; + error.from = from; + error.to = to; + error.type = type; + + return error +} + +var propertiesToLog = ['params', 'query', 'hash']; + +function stringifyRoute (to) { + if (typeof to === 'string') { return to } + if ('path' in to) { return to.path } + var location = {}; + propertiesToLog.forEach(function (key) { + if (key in to) { location[key] = to[key]; } + }); + return JSON.stringify(location, null, 2) +} + +function isError (err) { + return Object.prototype.toString.call(err).indexOf('Error') > -1 +} + +function isNavigationFailure (err, errorType) { + return ( + isError(err) && + err._isRouter && + (errorType == null || err.type === errorType) + ) +} + /* */ function resolveAsyncComponents (matched) { @@ -2015,73 +2090,6 @@ function once (fn) { } } -var NavigationFailureType = { - redirected: 1, - aborted: 2, - cancelled: 3, - duplicated: 4 -}; - -function createNavigationRedirectedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.redirected, - ("Redirected when going from \"" + (from.fullPath) + "\" to \"" + (stringifyRoute( - to - )) + "\" via a navigation guard.") - ) -} - -function createNavigationDuplicatedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.duplicated, - ("Avoided redundant navigation to current location: \"" + (from.fullPath) + "\".") - ) -} - -function createNavigationCancelledError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.cancelled, - ("Navigation cancelled from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" with a new navigation.") - ) -} - -function createNavigationAbortedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.aborted, - ("Navigation aborted from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" via a navigation guard.") - ) -} - -function createRouterError (from, to, type, message) { - var error = new Error(message); - error._isRouter = true; - error.from = from; - error.to = to; - error.type = type; - - return error -} - -var propertiesToLog = ['params', 'query', 'hash']; - -function stringifyRoute (to) { - if (typeof to === 'string') { return to } - if ('path' in to) { return to.path } - var location = {}; - propertiesToLog.forEach(function (key) { - if (key in to) { location[key] = to[key]; } - }); - return JSON.stringify(location, null, 2) -} - /* */ var History = function History (router, base) { @@ -2123,7 +2131,17 @@ History.prototype.transitionTo = function transitionTo ( ) { var this$1 = this; - var route = this.router.match(location, this.current); + var route; + // catch redirect option https://github.com/vuejs/vue-router/issues/3201 + try { + route = this.router.match(location, this.current); + } catch (e) { + this.errorCbs.forEach(function (cb) { + cb(e); + }); + // Exception should still be thrown + throw e + } this.confirmTransition( route, function () { @@ -2151,7 +2169,7 @@ History.prototype.transitionTo = function transitionTo ( this$1.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$1.readyErrorCbs.forEach(function (cb) { cb(err); }); @@ -2173,7 +2191,7 @@ History.prototype.confirmTransition = function confirmTransition (route, onCompl // 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$1.errorCbs.length) { this$1.errorCbs.forEach(function (cb) { cb(err); @@ -2759,7 +2777,7 @@ var AbstractHistory = /*@__PURE__*/(function (History) { this$1.updateRoute(route); }, function (err) { - if (isRouterError(err, NavigationFailureType.duplicated)) { + if (isNavigationFailure(err, NavigationFailureType.duplicated)) { this$1.index = targetIndex; } } @@ -2780,8 +2798,6 @@ var AbstractHistory = /*@__PURE__*/(function (History) { /* */ - - var VueRouter = function VueRouter (options) { if ( options === void 0 ) options = {}; @@ -2794,7 +2810,8 @@ var VueRouter = function VueRouter (options) { this.matcher = createMatcher(options.routes || [], this); var mode = options.mode || 'hash'; - this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false; + this.fallback = + mode === 'history' && !supportsPushState && options.fallback !== false; if (this.fallback) { mode = 'hash'; } @@ -2822,11 +2839,7 @@ var VueRouter = function VueRouter (options) { var prototypeAccessors = { currentRoute: { configurable: true } }; -VueRouter.prototype.match = function match ( - raw, - current, - redirectedFrom -) { +VueRouter.prototype.match = function match (raw, current, redirectedFrom) { return this.matcher.match(raw, current, redirectedFrom) }; @@ -2837,11 +2850,12 @@ prototypeAccessors.currentRoute.get = function () { VueRouter.prototype.init = function init (app /* Vue component instance */) { var this$1 = this; - process.env.NODE_ENV !== 'production' && assert( - install.installed, - "not installed. Make sure to call `Vue.use(VueRouter)` " + - "before creating root instance." - ); + process.env.NODE_ENV !== 'production' && + assert( + install.installed, + "not installed. Make sure to call `Vue.use(VueRouter)` " + + "before creating root instance." + ); this.apps.push(app); @@ -2873,10 +2887,24 @@ VueRouter.prototype.init = function init (app /* Vue component instance */) { var history = this.history; if (history instanceof HTML5History || history instanceof HashHistory) { - var setupListeners = function () { + var handleInitialScroll = function (routeOrError) { + var from = history.current; + var expectScroll = this$1.options.scrollBehavior; + var supportsScroll = supportsPushState && expectScroll; + + if (supportsScroll && 'fullPath' in routeOrError) { + handleScroll(this$1, routeOrError, from, false); + } + }; + var setupListeners = function (routeOrError) { history.setupListeners(); + handleInitialScroll(routeOrError); }; - history.transitionTo(history.getCurrentLocation(), setupListeners, setupListeners); + history.transitionTo( + history.getCurrentLocation(), + setupListeners, + setupListeners + ); } history.listen(function (route) { @@ -2953,11 +2981,14 @@ VueRouter.prototype.getMatchedComponents = function getMatchedComponents (to) { if (!route) { return [] } - return [].concat.apply([], route.matched.map(function (m) { - return Object.keys(m.components).map(function (key) { - return m.components[key] + return [].concat.apply( + [], + route.matched.map(function (m) { + return Object.keys(m.components).map(function (key) { + return m.components[key] + }) }) - })) + ) }; VueRouter.prototype.resolve = function resolve ( @@ -2966,12 +2997,7 @@ VueRouter.prototype.resolve = function resolve ( append ) { current = current || this.history.current; - var location = normalizeLocation( - to, - current, - append, - this - ); + var location = normalizeLocation(to, current, append, this); var route = this.match(location, current); var fullPath = route.redirectedFrom || route.fullPath; var base = this.history.base; @@ -3009,7 +3035,9 @@ function createHref (base, fullPath, mode) { } VueRouter.install = install; -VueRouter.version = '3.3.4'; +VueRouter.version = '3.4.0'; +VueRouter.isNavigationFailure = isNavigationFailure; +VueRouter.NavigationFailureType = NavigationFailureType; if (inBrowser && window.Vue) { window.Vue.use(VueRouter); diff --git a/dist/vue-router.esm.browser.js b/dist/vue-router.esm.browser.js index 08e5c9553..2badba788 100644 --- a/dist/vue-router.esm.browser.js +++ b/dist/vue-router.esm.browser.js @@ -1,5 +1,5 @@ /*! - * vue-router v3.3.4 + * vue-router v3.4.0 * (c) 2020 Evan You * @license MIT */ @@ -17,14 +17,6 @@ function warn (condition, message) { } } -function isError (err) { - return Object.prototype.toString.call(err).indexOf('Error') > -1 -} - -function isRouterError (err, errorType) { - return isError(err) && err._isRouter && (errorType == null || err.type === errorType) -} - function extend (a, b) { for (const key in b) { a[key] = b[key]; @@ -128,7 +120,7 @@ var View = { }; const configProps = matched.props && matched.props[name]; - // save route and configProps in cachce + // save route and configProps in cache if (configProps) { extend(cache[name], { route, @@ -208,7 +200,8 @@ function resolveQuery ( parsedQuery = {}; } for (const key in extraQuery) { - parsedQuery[key] = extraQuery[key]; + const value = extraQuery[key]; + parsedQuery[key] = Array.isArray(value) ? value.map(v => '' + v) : '' + value; } return parsedQuery } @@ -1882,6 +1875,92 @@ function runQueue (queue, fn, cb) { step(0); } +const NavigationFailureType = { + redirected: 2, + aborted: 4, + cancelled: 8, + duplicated: 16 +}; + +function createNavigationRedirectedError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.redirected, + `Redirected when going from "${from.fullPath}" to "${stringifyRoute( + to + )}" via a navigation guard.` + ) +} + +function createNavigationDuplicatedError (from, to) { + const error = createRouterError( + from, + to, + NavigationFailureType.duplicated, + `Avoided redundant navigation to current location: "${from.fullPath}".` + ); + // backwards compatible with the first introduction of Errors + error.name = 'NavigationDuplicated'; + return error +} + +function createNavigationCancelledError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.cancelled, + `Navigation cancelled from "${from.fullPath}" to "${ + to.fullPath + }" with a new navigation.` + ) +} + +function createNavigationAbortedError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.aborted, + `Navigation aborted from "${from.fullPath}" to "${ + to.fullPath + }" via a navigation guard.` + ) +} + +function createRouterError (from, to, type, message) { + const error = new Error(message); + error._isRouter = true; + error.from = from; + error.to = to; + error.type = type; + + return error +} + +const propertiesToLog = ['params', 'query', 'hash']; + +function stringifyRoute (to) { + if (typeof to === 'string') return to + if ('path' in to) return to.path + const location = {}; + propertiesToLog.forEach(key => { + if (key in to) location[key] = to[key]; + }); + return JSON.stringify(location, null, 2) +} + +function isError (err) { + return Object.prototype.toString.call(err).indexOf('Error') > -1 +} + +function isNavigationFailure (err, errorType) { + return ( + isError(err) && + err._isRouter && + (errorType == null || err.type === errorType) + ) +} + /* */ function resolveAsyncComponents (matched) { @@ -1988,77 +2067,6 @@ function once (fn) { } } -const NavigationFailureType = { - redirected: 1, - aborted: 2, - cancelled: 3, - duplicated: 4 -}; - -function createNavigationRedirectedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.redirected, - `Redirected when going from "${from.fullPath}" to "${stringifyRoute( - to - )}" via a navigation guard.` - ) -} - -function createNavigationDuplicatedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.duplicated, - `Avoided redundant navigation to current location: "${from.fullPath}".` - ) -} - -function createNavigationCancelledError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.cancelled, - `Navigation cancelled from "${from.fullPath}" to "${ - to.fullPath - }" with a new navigation.` - ) -} - -function createNavigationAbortedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.aborted, - `Navigation aborted from "${from.fullPath}" to "${ - to.fullPath - }" via a navigation guard.` - ) -} - -function createRouterError (from, to, type, message) { - const error = new Error(message); - error._isRouter = true; - error.from = from; - error.to = to; - error.type = type; - - return error -} - -const propertiesToLog = ['params', 'query', 'hash']; - -function stringifyRoute (to) { - if (typeof to === 'string') return to - if ('path' in to) return to.path - const location = {}; - propertiesToLog.forEach(key => { - if (key in to) location[key] = to[key]; - }); - return JSON.stringify(location, null, 2) -} - /* */ class History { @@ -2119,7 +2127,17 @@ class History { onComplete, onAbort ) { - const route = this.router.match(location, this.current); + let route; + // catch redirect option https://github.com/vuejs/vue-router/issues/3201 + try { + route = this.router.match(location, this.current); + } catch (e) { + this.errorCbs.forEach(cb => { + cb(e); + }); + // Exception should still be thrown + throw e + } this.confirmTransition( route, () => { @@ -2147,7 +2165,7 @@ 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); }); @@ -2167,7 +2185,7 @@ 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); @@ -2718,7 +2736,7 @@ class AbstractHistory extends History { this.updateRoute(route); }, err => { - if (isRouterError(err, NavigationFailureType.duplicated)) { + if (isNavigationFailure(err, NavigationFailureType.duplicated)) { this.index = targetIndex; } } @@ -2737,11 +2755,11 @@ class AbstractHistory extends History { /* */ - - class VueRouter { + + @@ -2766,7 +2784,8 @@ class VueRouter { this.matcher = createMatcher(options.routes || [], this); let mode = options.mode || 'hash'; - this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false; + this.fallback = + mode === 'history' && !supportsPushState && options.fallback !== false; if (this.fallback) { mode = 'hash'; } @@ -2792,11 +2811,7 @@ class VueRouter { } } - match ( - raw, - current, - redirectedFrom - ) { + match (raw, current, redirectedFrom) { return this.matcher.match(raw, current, redirectedFrom) } @@ -2805,11 +2820,12 @@ class VueRouter { } init (app /* Vue component instance */) { - assert( - install.installed, - `not installed. Make sure to call \`Vue.use(VueRouter)\` ` + - `before creating root instance.` - ); + + assert( + install.installed, + `not installed. Make sure to call \`Vue.use(VueRouter)\` ` + + `before creating root instance.` + ); this.apps.push(app); @@ -2841,14 +2857,28 @@ class VueRouter { const history = this.history; if (history instanceof HTML5History || history instanceof HashHistory) { - const setupListeners = () => { + const handleInitialScroll = routeOrError => { + const from = history.current; + const expectScroll = this.options.scrollBehavior; + const supportsScroll = supportsPushState && expectScroll; + + if (supportsScroll && 'fullPath' in routeOrError) { + handleScroll(this, routeOrError, from, false); + } + }; + const setupListeners = routeOrError => { history.setupListeners(); + handleInitialScroll(routeOrError); }; - history.transitionTo(history.getCurrentLocation(), setupListeners, setupListeners); + history.transitionTo( + history.getCurrentLocation(), + setupListeners, + setupListeners + ); } history.listen(route => { - this.apps.forEach((app) => { + this.apps.forEach(app => { app._route = route; }); }); @@ -2917,11 +2947,14 @@ class VueRouter { if (!route) { return [] } - return [].concat.apply([], route.matched.map(m => { - return Object.keys(m.components).map(key => { - return m.components[key] + return [].concat.apply( + [], + route.matched.map(m => { + return Object.keys(m.components).map(key => { + return m.components[key] + }) }) - })) + ) } resolve ( @@ -2930,12 +2963,7 @@ class VueRouter { append ) { current = current || this.history.current; - const location = normalizeLocation( - to, - current, - append, - this - ); + const location = normalizeLocation(to, current, append, this); const route = this.match(location, current); const fullPath = route.redirectedFrom || route.fullPath; const base = this.history.base; @@ -2972,7 +3000,9 @@ function createHref (base, fullPath, mode) { } VueRouter.install = install; -VueRouter.version = '3.3.4'; +VueRouter.version = '3.4.0'; +VueRouter.isNavigationFailure = isNavigationFailure; +VueRouter.NavigationFailureType = NavigationFailureType; if (inBrowser && window.Vue) { window.Vue.use(VueRouter); diff --git a/dist/vue-router.esm.browser.min.js b/dist/vue-router.esm.browser.min.js index e611425c0..334c452c9 100644 --- a/dist/vue-router.esm.browser.min.js +++ b/dist/vue-router.esm.browser.min.js @@ -1,6 +1,6 @@ /*! - * vue-router v3.3.4 + * vue-router v3.4.0 * (c) 2020 Evan You * @license MIT */ -function t(t){return Object.prototype.toString.call(t).indexOf("Error")>-1}function e(e,n){return t(e)&&e._isRouter&&(null==n||e.type===n)}function n(t,e){for(const n in e)t[n]=e[n];return t}var r={name:"RouterView",functional:!0,props:{name:{type:String,default:"default"}},render(t,{props:e,children:r,parent:i,data:s}){s.routerView=!0;const a=i.$createElement,c=e.name,u=i.$route,h=i._routerViewCache||(i._routerViewCache={});let p=0,l=!1;for(;i&&i._routerRoot!==i;){const t=i.$vnode?i.$vnode.data:{};t.routerView&&p++,t.keepAlive&&i._directInactive&&i._inactive&&(l=!0),i=i.$parent}if(s.routerViewDepth=p,l){const t=h[c],e=t&&t.component;return e?(t.configProps&&o(e,s,t.route,t.configProps),a(e,s,r)):a()}const f=u.matched[p],d=f&&f.components[c];if(!f||!d)return h[c]=null,a();h[c]={component:d},s.registerRouteInstance=(t,e)=>{const n=f.instances[c];(e&&n!==t||!e&&n===t)&&(f.instances[c]=e)},(s.hook||(s.hook={})).prepatch=(t,e)=>{f.instances[c]=e.componentInstance},s.hook.init=t=>{t.data.keepAlive&&t.componentInstance&&t.componentInstance!==f.instances[c]&&(f.instances[c]=t.componentInstance)};const y=f.props&&f.props[c];return y&&(n(h[c],{route:u,configProps:y}),o(d,s,u,y)),a(d,s,r)}};function o(t,e,r,o){let i=e.props=function(t,e){switch(typeof e){case"undefined":return;case"object":return e;case"function":return e(t);case"boolean":return e?t.params:void 0}}(r,o);if(i){i=e.props=n({},i);const r=e.attrs=e.attrs||{};for(const e in i)t.props&&e in t.props||(r[e]=i[e],delete i[e])}}const i=/[!'()*]/g,s=t=>"%"+t.charCodeAt(0).toString(16),a=/%2C/g,c=t=>encodeURIComponent(t).replace(i,s).replace(a,","),u=decodeURIComponent;function h(t){const e={};return(t=t.trim().replace(/^(\?|#|&)/,""))?(t.split("&").forEach(t=>{const n=t.replace(/\+/g," ").split("="),r=u(n.shift()),o=n.length>0?u(n.join("=")):null;void 0===e[r]?e[r]=o:Array.isArray(e[r])?e[r].push(o):e[r]=[e[r],o]}),e):e}function p(t){const e=t?Object.keys(t).map(e=>{const n=t[e];if(void 0===n)return"";if(null===n)return c(e);if(Array.isArray(n)){const t=[];return n.forEach(n=>{void 0!==n&&(null===n?t.push(c(e)):t.push(c(e)+"="+c(n)))}),t.join("&")}return c(e)+"="+c(n)}).filter(t=>t.length>0).join("&"):null;return e?`?${e}`:""}const l=/\/?$/;function f(t,e,n,r){const o=r&&r.options.stringifyQuery;let i=e.query||{};try{i=d(i)}catch(t){}const s={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:i,params:e.params||{},fullPath:g(e,o),matched:t?m(t):[]};return n&&(s.redirectedFrom=g(n,o)),Object.freeze(s)}function d(t){if(Array.isArray(t))return t.map(d);if(t&&"object"==typeof t){const e={};for(const n in t)e[n]=d(t[n]);return e}return t}const y=f(null,{path:"/"});function m(t){const e=[];for(;t;)e.unshift(t),t=t.parent;return e}function g({path:t,query:e={},hash:n=""},r){return(t||"/")+(r||p)(e)+n}function w(t,e){return e===y?t===e:!!e&&(t.path&&e.path?t.path.replace(l,"")===e.path.replace(l,"")&&t.hash===e.hash&&v(t.query,e.query):!(!t.name||!e.name)&&(t.name===e.name&&t.hash===e.hash&&v(t.query,e.query)&&v(t.params,e.params)))}function v(t={},e={}){if(!t||!e)return t===e;const n=Object.keys(t),r=Object.keys(e);return n.length===r.length&&n.every(n=>{const r=t[n],o=e[n];return"object"==typeof r&&"object"==typeof o?v(r,o):String(r)===String(o)})}function b(t,e,n){const r=t.charAt(0);if("/"===r)return t;if("?"===r||"#"===r)return e+t;const o=e.split("/");n&&o[o.length-1]||o.pop();const i=t.replace(/^\//,"").split("/");for(let t=0;t=0&&(e=t.slice(r),t=t.slice(0,r));const o=t.indexOf("?");return o>=0&&(n=t.slice(o+1),t=t.slice(0,o)),{path:t,query:n,hash:e}}(i.path||""),a=e&&e.path||"/",c=s.path?b(s.path,a,r||i.append):a,u=function(t,e={},n){const r=n||h;let o;try{o=r(t||"")}catch(t){o={}}for(const t in e)o[t]=e[t];return o}(s.query,i.query,o&&o.options.parseQuery);let p=i.hash||s.hash;return p&&"#"!==p.charAt(0)&&(p=`#${p}`),{_normalized:!0,path:c,query:u,hash:p}}const H=[String,Object],z=[String,Array],F=()=>{};var N={name:"RouterLink",props:{to:{type:H,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,ariaCurrentValue:{type:String,default:"page"},event:{type:z,default:"click"}},render(t){const e=this.$router,r=this.$route,{location:o,route:i,href:s}=e.resolve(this.to,r,this.append),a={},c=e.options.linkActiveClass,u=e.options.linkExactActiveClass,h=null==c?"router-link-active":c,p=null==u?"router-link-exact-active":u,d=null==this.activeClass?h:this.activeClass,y=null==this.exactActiveClass?p:this.exactActiveClass,m=i.redirectedFrom?f(null,B(i.redirectedFrom),null,e):i;a[y]=w(r,m),a[d]=this.exact?a[y]:function(t,e){return 0===t.path.replace(l,"/").indexOf(e.path.replace(l,"/"))&&(!e.hash||t.hash===e.hash)&&function(t,e){for(const n in e)if(!(n in t))return!1;return!0}(t.query,e.query)}(r,m);const g=a[y]?this.ariaCurrentValue:null,v=t=>{D(t)&&(this.replace?e.replace(o,F):e.push(o,F))},b={click:D};Array.isArray(this.event)?this.event.forEach(t=>{b[t]=v}):b[this.event]=v;const x={class:a},R=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:s,route:i,navigate:v,isActive:a[d],isExactActive:a[y]});if(R){if(1===R.length)return R[0];if(R.length>1||!R.length)return 0===R.length?t():t("span",{},R)}if("a"===this.tag)x.on=b,x.attrs={href:s,"aria-current":g};else{const t=function t(e){if(e){let n;for(let r=0;r{!function t(e,n,r,o,i,s){const{path:a,name:c}=o;const u=o.pathToRegexpOptions||{};const h=function(t,e,n){n||(t=t.replace(/\/$/,""));return"/"===t[0]?t:null==e?t:x(`${e.path}/${t}`)}(a,i,u.strict);"boolean"==typeof o.caseSensitive&&(u.sensitive=o.caseSensitive);const p={path:h,regex:X(h,u),components:o.components||{default:o.component},instances:{},name:c,parent:i,matchAs:s,redirect:o.redirect,beforeEnter:o.beforeEnter,meta:o.meta||{},props:null==o.props?{}:o.components?o.props:{default:o.props}};o.children&&o.children.forEach(o=>{const i=s?x(`${s}/${o.path}`):void 0;t(e,n,r,o,p,i)});n[p.path]||(e.push(p.path),n[p.path]=p);if(void 0!==o.alias){const s=Array.isArray(o.alias)?o.alias:[o.alias];for(let a=0;a!t.optional).map(t=>t.name);if("object"!=typeof c.params&&(c.params={}),i&&"object"==typeof i.params)for(const t in i.params)!(t in c.params)&&e.indexOf(t)>-1&&(c.params[t]=i.params[t]);return c.path=V(t.path,c.params),a(t,c,s)}if(c.path){c.params={};for(let t=0;t{window.removeEventListener("popstate",at)}}function it(t,e,n,r){if(!t.app)return;const o=t.options.scrollBehavior;o&&t.app.$nextTick(()=>{const i=function(){const t=et();if(t)return rt[t]}(),s=o.call(t,e,n,r?i:null);s&&("function"==typeof s.then?s.then(t=>{lt(t,i)}).catch(t=>{}):lt(s,i))})}function st(){const t=et();t&&(rt[t]={x:window.pageXOffset,y:window.pageYOffset})}function at(t){st(),t.state&&t.state.key&&nt(t.state.key)}function ct(t){return ht(t.x)||ht(t.y)}function ut(t){return{x:ht(t.x)?t.x:window.pageXOffset,y:ht(t.y)?t.y:window.pageYOffset}}function ht(t){return"number"==typeof t}const pt=/^#\d/;function lt(t,e){const n="object"==typeof t;if(n&&"string"==typeof t.selector){const n=pt.test(t.selector)?document.getElementById(t.selector.slice(1)):document.querySelector(t.selector);if(n){let o=t.offset&&"object"==typeof t.offset?t.offset:{};e=function(t,e){const n=document.documentElement.getBoundingClientRect(),r=t.getBoundingClientRect();return{x:r.left-n.left-e.x,y:r.top-n.top-e.y}}(n,o={x:ht((r=o).x)?r.x:0,y:ht(r.y)?r.y:0})}else ct(t)&&(e=ut(t))}else n&&ct(t)&&(e=ut(t));var r;e&&window.scrollTo(e.x,e.y)}const ft=J&&function(){const t=window.navigator.userAgent;return(-1===t.indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone"))&&(window.history&&"function"==typeof window.history.pushState)}();function dt(t,e){st();const r=window.history;try{if(e){const e=n({},r.state);e.key=et(),r.replaceState(e,"",t)}else r.pushState({key:nt(Z())},"",t)}catch(n){window.location[e?"replace":"assign"](t)}}function yt(t){dt(t,!0)}function mt(t,e,n){const r=o=>{o>=t.length?n():t[o]?e(t[o],()=>{r(o+1)}):r(o+1)};r(0)}function gt(e){return(n,r,o)=>{let i=!1,s=0,a=null;wt(e,(e,n,r,c)=>{if("function"==typeof e&&void 0===e.cid){i=!0,s++;const n=xt(t=>{(function(t){return t.__esModule||bt&&"Module"===t[Symbol.toStringTag]})(t)&&(t=t.default),e.resolved="function"==typeof t?t:K.extend(t),r.components[c]=t,--s<=0&&o()}),u=xt(e=>{const n=`Failed to resolve async component ${c}: ${e}`;a||(a=t(e)?e:new Error(n),o(a))});let h;try{h=e(n,u)}catch(t){u(t)}if(h)if("function"==typeof h.then)h.then(n,u);else{const t=h.component;t&&"function"==typeof t.then&&t.then(n,u)}}}),i||o()}}function wt(t,e){return vt(t.map(t=>Object.keys(t.components).map(n=>e(t.components[n],t.instances[n],t,n))))}function vt(t){return Array.prototype.concat.apply([],t)}const bt="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;function xt(t){let e=!1;return function(...n){if(!e)return e=!0,t.apply(this,n)}}const Rt={redirected:1,aborted:2,cancelled:3,duplicated:4};function kt(t,e){return $t(t,e,Rt.redirected,`Redirected when going from "${t.fullPath}" to "${function(t){if("string"==typeof t)return t;if("path"in t)return t.path;const e={};return At.forEach(n=>{n in t&&(e[n]=t[n])}),JSON.stringify(e,null,2)}(e)}" via a navigation guard.`)}function Et(t,e){return $t(t,e,Rt.cancelled,`Navigation cancelled from "${t.fullPath}" to "${e.fullPath}" with a new navigation.`)}function $t(t,e,n,r){const o=new Error(r);return o._isRouter=!0,o.from=t,o.to=e,o.type=n,o}const At=["params","query","hash"];class Ct{constructor(t,e){this.router=t,this.base=function(t){if(!t)if(J){const e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else t="/";"/"!==t.charAt(0)&&(t="/"+t);return t.replace(/\/$/,"")}(e),this.current=y,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[],this.listeners=[]}listen(t){this.cb=t}onReady(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))}onError(t){this.errorCbs.push(t)}transitionTo(t,n,r){const o=this.router.match(t,this.current);this.confirmTransition(o,()=>{const t=this.current;this.updateRoute(o),n&&n(o),this.ensureURL(),this.router.afterHooks.forEach(e=>{e&&e(o,t)}),this.ready||(this.ready=!0,this.readyCbs.forEach(t=>{t(o)}))},t=>{r&&r(t),t&&!this.ready&&(this.ready=!0,e(t,Rt.redirected)?this.readyCbs.forEach(t=>{t(o)}):this.readyErrorCbs.forEach(e=>{e(t)}))})}confirmTransition(n,r,o){const i=this.current,s=n=>{!e(n)&&t(n)&&(this.errorCbs.length?this.errorCbs.forEach(t=>{t(n)}):console.error(n)),o&&o(n)},a=n.matched.length-1,c=i.matched.length-1;if(w(n,i)&&a===c&&n.matched[a]===i.matched[c])return this.ensureURL(),s($t(u=i,n,Rt.duplicated,`Avoided redundant navigation to current location: "${u.fullPath}".`));var u;const{updated:h,deactivated:p,activated:l}=function(t,e){let n;const r=Math.max(t.length,e.length);for(n=0;nt.beforeEnter),gt(l));this.pending=n;const d=(e,r)=>{if(this.pending!==n)return s(Et(i,n));try{e(n,i,e=>{!1===e?(this.ensureURL(!0),s(function(t,e){return $t(t,e,Rt.aborted,`Navigation aborted from "${t.fullPath}" to "${e.fullPath}" via a navigation guard.`)}(i,n))):t(e)?(this.ensureURL(!0),s(e)):"string"==typeof e||"object"==typeof e&&("string"==typeof e.path||"string"==typeof e.name)?(s(kt(i,n)),"object"==typeof e&&e.replace?this.replace(e):this.push(e)):r(e)})}catch(t){s(t)}};mt(f,d,()=>{const t=[];mt(function(t,e,n){return Ot(t,"beforeRouteEnter",(t,r,o,i)=>(function(t,e,n,r,o){return function(i,s,a){return t(i,s,t=>{"function"==typeof t&&r.push(()=>{!function t(e,n,r,o){n[r]&&!n[r]._isBeingDestroyed?e(n[r]):o()&&setTimeout(()=>{t(e,n,r,o)},16)}(t,e.instances,n,o)}),a(t)})}})(t,o,i,e,n))}(l,t,()=>this.current===n).concat(this.router.resolveHooks),d,()=>{if(this.pending!==n)return s(Et(i,n));this.pending=null,r(n),this.router.app&&this.router.app.$nextTick(()=>{t.forEach(t=>{t()})})})})}updateRoute(t){this.current=t,this.cb&&this.cb(t)}setupListeners(){}teardownListeners(){this.listeners.forEach(t=>{t()}),this.listeners=[]}}function Ot(t,e,n,r){const o=wt(t,(t,r,o,i)=>{const s=function(t,e){"function"!=typeof t&&(t=K.extend(t));return t.options[e]}(t,e);if(s)return Array.isArray(s)?s.map(t=>n(t,r,o,i)):n(s,r,o,i)});return vt(r?o.reverse():o)}function St(t,e){if(e)return function(){return t.apply(e,arguments)}}class jt extends Ct{constructor(t,e){super(t,e),this._startLocation=Lt(this.base)}setupListeners(){if(this.listeners.length>0)return;const t=this.router,e=t.options.scrollBehavior,n=ft&&e;n&&this.listeners.push(ot());const r=()=>{const e=this.current,r=Lt(this.base);this.current===y&&r===this._startLocation||this.transitionTo(r,r=>{n&&it(t,r,e,!0)})};window.addEventListener("popstate",r),this.listeners.push(()=>{window.removeEventListener("popstate",r)})}go(t){window.history.go(t)}push(t,e,n){const{current:r}=this;this.transitionTo(t,t=>{dt(x(this.base+t.fullPath)),it(this.router,t,r,!1),e&&e(t)},n)}replace(t,e,n){const{current:r}=this;this.transitionTo(t,t=>{yt(x(this.base+t.fullPath)),it(this.router,t,r,!1),e&&e(t)},n)}ensureURL(t){if(Lt(this.base)!==this.current.fullPath){const e=x(this.base+this.current.fullPath);t?dt(e):yt(e)}}getCurrentLocation(){return Lt(this.base)}}function Lt(t){let e=decodeURI(window.location.pathname);return t&&0===e.toLowerCase().indexOf(t.toLowerCase())&&(e=e.slice(t.length)),(e||"/")+window.location.search+window.location.hash}class Tt extends Ct{constructor(t,e,n){super(t,e),n&&function(t){const e=Lt(t);if(!/^\/#/.test(e))return window.location.replace(x(t+"/#"+e)),!0}(this.base)||_t()}setupListeners(){if(this.listeners.length>0)return;const t=this.router.options.scrollBehavior,e=ft&&t;e&&this.listeners.push(ot());const n=()=>{const t=this.current;_t()&&this.transitionTo(Pt(),n=>{e&&it(this.router,n,t,!0),ft||It(n.fullPath)})},r=ft?"popstate":"hashchange";window.addEventListener(r,n),this.listeners.push(()=>{window.removeEventListener(r,n)})}push(t,e,n){const{current:r}=this;this.transitionTo(t,t=>{Ut(t.fullPath),it(this.router,t,r,!1),e&&e(t)},n)}replace(t,e,n){const{current:r}=this;this.transitionTo(t,t=>{It(t.fullPath),it(this.router,t,r,!1),e&&e(t)},n)}go(t){window.history.go(t)}ensureURL(t){const e=this.current.fullPath;Pt()!==e&&(t?Ut(e):It(e))}getCurrentLocation(){return Pt()}}function _t(){const t=Pt();return"/"===t.charAt(0)||(It("/"+t),!1)}function Pt(){let t=window.location.href;const e=t.indexOf("#");if(e<0)return"";const n=(t=t.slice(e+1)).indexOf("?");if(n<0){const e=t.indexOf("#");t=e>-1?decodeURI(t.slice(0,e))+t.slice(e):decodeURI(t)}else t=decodeURI(t.slice(0,n))+t.slice(n);return t}function qt(t){const e=window.location.href,n=e.indexOf("#");return`${n>=0?e.slice(0,n):e}#${t}`}function Ut(t){ft?dt(qt(t)):window.location.hash=t}function It(t){ft?yt(qt(t)):window.location.replace(qt(t))}class Mt extends Ct{constructor(t,e){super(t,e),this.stack=[],this.index=-1}push(t,e,n){this.transitionTo(t,t=>{this.stack=this.stack.slice(0,this.index+1).concat(t),this.index++,e&&e(t)},n)}replace(t,e,n){this.transitionTo(t,t=>{this.stack=this.stack.slice(0,this.index).concat(t),e&&e(t)},n)}go(t){const n=this.index+t;if(n<0||n>=this.stack.length)return;const r=this.stack[n];this.confirmTransition(r,()=>{this.index=n,this.updateRoute(r)},t=>{e(t,Rt.duplicated)&&(this.index=n)})}getCurrentLocation(){const t=this.stack[this.stack.length-1];return t?t.fullPath:"/"}ensureURL(){}}class Vt{constructor(t={}){this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=Y(t.routes||[],this);let e=t.mode||"hash";switch(this.fallback="history"===e&&!ft&&!1!==t.fallback,this.fallback&&(e="hash"),J||(e="abstract"),this.mode=e,e){case"history":this.history=new jt(this,t.base);break;case"hash":this.history=new Tt(this,t.base,this.fallback);break;case"abstract":this.history=new Mt(this,t.base)}}match(t,e,n){return this.matcher.match(t,e,n)}get currentRoute(){return this.history&&this.history.current}init(t){if(this.apps.push(t),t.$once("hook:destroyed",()=>{const e=this.apps.indexOf(t);e>-1&&this.apps.splice(e,1),this.app===t&&(this.app=this.apps[0]||null),this.app||this.history.teardownListeners()}),this.app)return;this.app=t;const e=this.history;if(e instanceof jt||e instanceof Tt){const t=()=>{e.setupListeners()};e.transitionTo(e.getCurrentLocation(),t,t)}e.listen(t=>{this.apps.forEach(e=>{e._route=t})})}beforeEach(t){return Bt(this.beforeHooks,t)}beforeResolve(t){return Bt(this.resolveHooks,t)}afterEach(t){return Bt(this.afterHooks,t)}onReady(t,e){this.history.onReady(t,e)}onError(t){this.history.onError(t)}push(t,e,n){if(!e&&!n&&"undefined"!=typeof Promise)return new Promise((e,n)=>{this.history.push(t,e,n)});this.history.push(t,e,n)}replace(t,e,n){if(!e&&!n&&"undefined"!=typeof Promise)return new Promise((e,n)=>{this.history.replace(t,e,n)});this.history.replace(t,e,n)}go(t){this.history.go(t)}back(){this.go(-1)}forward(){this.go(1)}getMatchedComponents(t){const e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map(t=>Object.keys(t.components).map(e=>t.components[e]))):[]}resolve(t,e,n){const r=B(t,e=e||this.history.current,n,this),o=this.match(r,e),i=o.redirectedFrom||o.fullPath;return{location:r,route:o,href:function(t,e,n){var r="hash"===n?"#"+e:e;return t?x(t+"/"+r):r}(this.history.base,i,this.mode),normalizedTo:r,resolved:o}}addRoutes(t){this.matcher.addRoutes(t),this.history.current!==y&&this.history.transitionTo(this.history.getCurrentLocation())}}function Bt(t,e){return t.push(e),()=>{const n=t.indexOf(e);n>-1&&t.splice(n,1)}}Vt.install=function t(e){if(t.installed&&K===e)return;t.installed=!0,K=e;const n=t=>void 0!==t,o=(t,e)=>{let r=t.$options._parentVnode;n(r)&&n(r=r.data)&&n(r=r.registerRouteInstance)&&r(t,e)};e.mixin({beforeCreate(){n(this.$options.router)?(this._routerRoot=this,this._router=this.$options.router,this._router.init(this),e.util.defineReactive(this,"_route",this._router.history.current)):this._routerRoot=this.$parent&&this.$parent._routerRoot||this,o(this,this)},destroyed(){o(this)}}),Object.defineProperty(e.prototype,"$router",{get(){return this._routerRoot._router}}),Object.defineProperty(e.prototype,"$route",{get(){return this._routerRoot._route}}),e.component("RouterView",r),e.component("RouterLink",N);const i=e.config.optionMergeStrategies;i.beforeRouteEnter=i.beforeRouteLeave=i.beforeRouteUpdate=i.created},Vt.version="3.3.4",J&&window.Vue&&window.Vue.use(Vt);export default Vt; \ No newline at end of file +function t(t,e){for(const n in e)t[n]=e[n];return t}var e={name:"RouterView",functional:!0,props:{name:{type:String,default:"default"}},render(e,{props:r,children:o,parent:i,data:s}){s.routerView=!0;const a=i.$createElement,c=r.name,u=i.$route,h=i._routerViewCache||(i._routerViewCache={});let p=0,l=!1;for(;i&&i._routerRoot!==i;){const t=i.$vnode?i.$vnode.data:{};t.routerView&&p++,t.keepAlive&&i._directInactive&&i._inactive&&(l=!0),i=i.$parent}if(s.routerViewDepth=p,l){const t=h[c],e=t&&t.component;return e?(t.configProps&&n(e,s,t.route,t.configProps),a(e,s,o)):a()}const f=u.matched[p],d=f&&f.components[c];if(!f||!d)return h[c]=null,a();h[c]={component:d},s.registerRouteInstance=(t,e)=>{const n=f.instances[c];(e&&n!==t||!e&&n===t)&&(f.instances[c]=e)},(s.hook||(s.hook={})).prepatch=(t,e)=>{f.instances[c]=e.componentInstance},s.hook.init=t=>{t.data.keepAlive&&t.componentInstance&&t.componentInstance!==f.instances[c]&&(f.instances[c]=t.componentInstance)};const y=f.props&&f.props[c];return y&&(t(h[c],{route:u,configProps:y}),n(d,s,u,y)),a(d,s,o)}};function n(e,n,r,o){let i=n.props=function(t,e){switch(typeof e){case"undefined":return;case"object":return e;case"function":return e(t);case"boolean":return e?t.params:void 0}}(r,o);if(i){i=n.props=t({},i);const r=n.attrs=n.attrs||{};for(const t in i)e.props&&t in e.props||(r[t]=i[t],delete i[t])}}const r=/[!'()*]/g,o=t=>"%"+t.charCodeAt(0).toString(16),i=/%2C/g,s=t=>encodeURIComponent(t).replace(r,o).replace(i,","),a=decodeURIComponent;function c(t){const e={};return(t=t.trim().replace(/^(\?|#|&)/,""))?(t.split("&").forEach(t=>{const n=t.replace(/\+/g," ").split("="),r=a(n.shift()),o=n.length>0?a(n.join("=")):null;void 0===e[r]?e[r]=o:Array.isArray(e[r])?e[r].push(o):e[r]=[e[r],o]}),e):e}function u(t){const e=t?Object.keys(t).map(e=>{const n=t[e];if(void 0===n)return"";if(null===n)return s(e);if(Array.isArray(n)){const t=[];return n.forEach(n=>{void 0!==n&&(null===n?t.push(s(e)):t.push(s(e)+"="+s(n)))}),t.join("&")}return s(e)+"="+s(n)}).filter(t=>t.length>0).join("&"):null;return e?`?${e}`:""}const h=/\/?$/;function p(t,e,n,r){const o=r&&r.options.stringifyQuery;let i=e.query||{};try{i=l(i)}catch(t){}const s={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:i,params:e.params||{},fullPath:y(e,o),matched:t?d(t):[]};return n&&(s.redirectedFrom=y(n,o)),Object.freeze(s)}function l(t){if(Array.isArray(t))return t.map(l);if(t&&"object"==typeof t){const e={};for(const n in t)e[n]=l(t[n]);return e}return t}const f=p(null,{path:"/"});function d(t){const e=[];for(;t;)e.unshift(t),t=t.parent;return e}function y({path:t,query:e={},hash:n=""},r){return(t||"/")+(r||u)(e)+n}function m(t,e){return e===f?t===e:!!e&&(t.path&&e.path?t.path.replace(h,"")===e.path.replace(h,"")&&t.hash===e.hash&&g(t.query,e.query):!(!t.name||!e.name)&&(t.name===e.name&&t.hash===e.hash&&g(t.query,e.query)&&g(t.params,e.params)))}function g(t={},e={}){if(!t||!e)return t===e;const n=Object.keys(t),r=Object.keys(e);return n.length===r.length&&n.every(n=>{const r=t[n],o=e[n];return"object"==typeof r&&"object"==typeof o?g(r,o):String(r)===String(o)})}function w(t,e,n){const r=t.charAt(0);if("/"===r)return t;if("?"===r||"#"===r)return e+t;const o=e.split("/");n&&o[o.length-1]||o.pop();const i=t.replace(/^\//,"").split("/");for(let t=0;t=0&&(e=t.slice(r),t=t.slice(0,r));const o=t.indexOf("?");return o>=0&&(n=t.slice(o+1),t=t.slice(0,o)),{path:t,query:n,hash:e}}(i.path||""),a=n&&n.path||"/",u=s.path?w(s.path,a,r||i.append):a,h=function(t,e={},n){const r=n||c;let o;try{o=r(t||"")}catch(t){o={}}for(const t in e){const n=e[t];o[t]=Array.isArray(n)?n.map(t=>""+t):""+n}return o}(s.query,i.query,o&&o.options.parseQuery);let p=i.hash||s.hash;return p&&"#"!==p.charAt(0)&&(p=`#${p}`),{_normalized:!0,path:u,query:h,hash:p}}const V=[String,Object],B=[String,Array],F=()=>{};var H={name:"RouterLink",props:{to:{type:V,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,ariaCurrentValue:{type:String,default:"page"},event:{type:B,default:"click"}},render(e){const n=this.$router,r=this.$route,{location:o,route:i,href:s}=n.resolve(this.to,r,this.append),a={},c=n.options.linkActiveClass,u=n.options.linkExactActiveClass,l=null==c?"router-link-active":c,f=null==u?"router-link-exact-active":u,d=null==this.activeClass?l:this.activeClass,y=null==this.exactActiveClass?f:this.exactActiveClass,g=i.redirectedFrom?p(null,M(i.redirectedFrom),null,n):i;a[y]=m(r,g),a[d]=this.exact?a[y]:function(t,e){return 0===t.path.replace(h,"/").indexOf(e.path.replace(h,"/"))&&(!e.hash||t.hash===e.hash)&&function(t,e){for(const n in e)if(!(n in t))return!1;return!0}(t.query,e.query)}(r,g);const w=a[y]?this.ariaCurrentValue:null,v=t=>{N(t)&&(this.replace?n.replace(o,F):n.push(o,F))},b={click:N};Array.isArray(this.event)?this.event.forEach(t=>{b[t]=v}):b[this.event]=v;const x={class:a},R=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:s,route:i,navigate:v,isActive:a[d],isExactActive:a[y]});if(R){if(1===R.length)return R[0];if(R.length>1||!R.length)return 0===R.length?e():e("span",{},R)}if("a"===this.tag)x.on=b,x.attrs={href:s,"aria-current":w};else{const e=function t(e){if(e){let n;for(let r=0;r{!function t(e,n,r,o,i,s){const{path:a,name:c}=o;const u=o.pathToRegexpOptions||{};const h=function(t,e,n){n||(t=t.replace(/\/$/,""));return"/"===t[0]?t:null==e?t:v(`${e.path}/${t}`)}(a,i,u.strict);"boolean"==typeof o.caseSensitive&&(u.sensitive=o.caseSensitive);const p={path:h,regex:J(h,u),components:o.components||{default:o.component},instances:{},name:c,parent:i,matchAs:s,redirect:o.redirect,beforeEnter:o.beforeEnter,meta:o.meta||{},props:null==o.props?{}:o.components?o.props:{default:o.props}};o.children&&o.children.forEach(o=>{const i=s?v(`${s}/${o.path}`):void 0;t(e,n,r,o,p,i)});n[p.path]||(e.push(p.path),n[p.path]=p);if(void 0!==o.alias){const s=Array.isArray(o.alias)?o.alias:[o.alias];for(let a=0;a!t.optional).map(t=>t.name);if("object"!=typeof c.params&&(c.params={}),i&&"object"==typeof i.params)for(const t in i.params)!(t in c.params)&&e.indexOf(t)>-1&&(c.params[t]=i.params[t]);return c.path=I(t.path,c.params),a(t,c,s)}if(c.path){c.params={};for(let t=0;t{window.removeEventListener("popstate",it)}}function rt(t,e,n,r){if(!t.app)return;const o=t.options.scrollBehavior;o&&t.app.$nextTick(()=>{const i=function(){const t=Z();if(t)return et[t]}(),s=o.call(t,e,n,r?i:null);s&&("function"==typeof s.then?s.then(t=>{ht(t,i)}).catch(t=>{}):ht(s,i))})}function ot(){const t=Z();t&&(et[t]={x:window.pageXOffset,y:window.pageYOffset})}function it(t){ot(),t.state&&t.state.key&&tt(t.state.key)}function st(t){return ct(t.x)||ct(t.y)}function at(t){return{x:ct(t.x)?t.x:window.pageXOffset,y:ct(t.y)?t.y:window.pageYOffset}}function ct(t){return"number"==typeof t}const ut=/^#\d/;function ht(t,e){const n="object"==typeof t;if(n&&"string"==typeof t.selector){const n=ut.test(t.selector)?document.getElementById(t.selector.slice(1)):document.querySelector(t.selector);if(n){let o=t.offset&&"object"==typeof t.offset?t.offset:{};e=function(t,e){const n=document.documentElement.getBoundingClientRect(),r=t.getBoundingClientRect();return{x:r.left-n.left-e.x,y:r.top-n.top-e.y}}(n,o={x:ct((r=o).x)?r.x:0,y:ct(r.y)?r.y:0})}else st(t)&&(e=at(t))}else n&&st(t)&&(e=at(t));var r;e&&window.scrollTo(e.x,e.y)}const pt=D&&function(){const t=window.navigator.userAgent;return(-1===t.indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone"))&&(window.history&&"function"==typeof window.history.pushState)}();function lt(e,n){ot();const r=window.history;try{if(n){const n=t({},r.state);n.key=Z(),r.replaceState(n,"",e)}else r.pushState({key:tt(W())},"",e)}catch(t){window.location[n?"replace":"assign"](e)}}function ft(t){lt(t,!0)}function dt(t,e,n){const r=o=>{o>=t.length?n():t[o]?e(t[o],()=>{r(o+1)}):r(o+1)};r(0)}const yt={redirected:2,aborted:4,cancelled:8,duplicated:16};function mt(t,e){return wt(t,e,yt.redirected,`Redirected when going from "${t.fullPath}" to "${function(t){if("string"==typeof t)return t;if("path"in t)return t.path;const e={};return vt.forEach(n=>{n in t&&(e[n]=t[n])}),JSON.stringify(e,null,2)}(e)}" via a navigation guard.`)}function gt(t,e){return wt(t,e,yt.cancelled,`Navigation cancelled from "${t.fullPath}" to "${e.fullPath}" with a new navigation.`)}function wt(t,e,n,r){const o=new Error(r);return o._isRouter=!0,o.from=t,o.to=e,o.type=n,o}const vt=["params","query","hash"];function bt(t){return Object.prototype.toString.call(t).indexOf("Error")>-1}function xt(t,e){return bt(t)&&t._isRouter&&(null==e||t.type===e)}function Rt(t){return(e,n,r)=>{let o=!1,i=0,s=null;kt(t,(t,e,n,a)=>{if("function"==typeof t&&void 0===t.cid){o=!0,i++;const e=At(e=>{(function(t){return t.__esModule||$t&&"Module"===t[Symbol.toStringTag]})(e)&&(e=e.default),t.resolved="function"==typeof e?e:z.extend(e),n.components[a]=e,--i<=0&&r()}),c=At(t=>{const e=`Failed to resolve async component ${a}: ${t}`;s||(s=bt(t)?t:new Error(e),r(s))});let u;try{u=t(e,c)}catch(t){c(t)}if(u)if("function"==typeof u.then)u.then(e,c);else{const t=u.component;t&&"function"==typeof t.then&&t.then(e,c)}}}),o||r()}}function kt(t,e){return Et(t.map(t=>Object.keys(t.components).map(n=>e(t.components[n],t.instances[n],t,n))))}function Et(t){return Array.prototype.concat.apply([],t)}const $t="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;function At(t){let e=!1;return function(...n){if(!e)return e=!0,t.apply(this,n)}}class Ct{constructor(t,e){this.router=t,this.base=function(t){if(!t)if(D){const e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else t="/";"/"!==t.charAt(0)&&(t="/"+t);return t.replace(/\/$/,"")}(e),this.current=f,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[],this.listeners=[]}listen(t){this.cb=t}onReady(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))}onError(t){this.errorCbs.push(t)}transitionTo(t,e,n){let r;try{r=this.router.match(t,this.current)}catch(t){throw this.errorCbs.forEach(e=>{e(t)}),t}this.confirmTransition(r,()=>{const t=this.current;this.updateRoute(r),e&&e(r),this.ensureURL(),this.router.afterHooks.forEach(e=>{e&&e(r,t)}),this.ready||(this.ready=!0,this.readyCbs.forEach(t=>{t(r)}))},t=>{n&&n(t),t&&!this.ready&&(this.ready=!0,xt(t,yt.redirected)?this.readyCbs.forEach(t=>{t(r)}):this.readyErrorCbs.forEach(e=>{e(t)}))})}confirmTransition(t,e,n){const r=this.current,o=t=>{!xt(t)&&bt(t)&&(this.errorCbs.length?this.errorCbs.forEach(e=>{e(t)}):console.error(t)),n&&n(t)},i=t.matched.length-1,s=r.matched.length-1;if(m(t,r)&&i===s&&t.matched[i]===r.matched[s])return this.ensureURL(),o(function(t,e){const n=wt(t,e,yt.duplicated,`Avoided redundant navigation to current location: "${t.fullPath}".`);return n.name="NavigationDuplicated",n}(r,t));const{updated:a,deactivated:c,activated:u}=function(t,e){let n;const r=Math.max(t.length,e.length);for(n=0;nt.beforeEnter),Rt(u));this.pending=t;const p=(e,n)=>{if(this.pending!==t)return o(gt(r,t));try{e(t,r,e=>{!1===e?(this.ensureURL(!0),o(function(t,e){return wt(t,e,yt.aborted,`Navigation aborted from "${t.fullPath}" to "${e.fullPath}" via a navigation guard.`)}(r,t))):bt(e)?(this.ensureURL(!0),o(e)):"string"==typeof e||"object"==typeof e&&("string"==typeof e.path||"string"==typeof e.name)?(o(mt(r,t)),"object"==typeof e&&e.replace?this.replace(e):this.push(e)):n(e)})}catch(t){o(t)}};dt(h,p,()=>{const n=[];dt(function(t,e,n){return Ot(t,"beforeRouteEnter",(t,r,o,i)=>(function(t,e,n,r,o){return function(i,s,a){return t(i,s,t=>{"function"==typeof t&&r.push(()=>{!function t(e,n,r,o){n[r]&&!n[r]._isBeingDestroyed?e(n[r]):o()&&setTimeout(()=>{t(e,n,r,o)},16)}(t,e.instances,n,o)}),a(t)})}})(t,o,i,e,n))}(u,n,()=>this.current===t).concat(this.router.resolveHooks),p,()=>{if(this.pending!==t)return o(gt(r,t));this.pending=null,e(t),this.router.app&&this.router.app.$nextTick(()=>{n.forEach(t=>{t()})})})})}updateRoute(t){this.current=t,this.cb&&this.cb(t)}setupListeners(){}teardownListeners(){this.listeners.forEach(t=>{t()}),this.listeners=[]}}function Ot(t,e,n,r){const o=kt(t,(t,r,o,i)=>{const s=function(t,e){"function"!=typeof t&&(t=z.extend(t));return t.options[e]}(t,e);if(s)return Array.isArray(s)?s.map(t=>n(t,r,o,i)):n(s,r,o,i)});return Et(r?o.reverse():o)}function St(t,e){if(e)return function(){return t.apply(e,arguments)}}class jt extends Ct{constructor(t,e){super(t,e),this._startLocation=Lt(this.base)}setupListeners(){if(this.listeners.length>0)return;const t=this.router,e=t.options.scrollBehavior,n=pt&&e;n&&this.listeners.push(nt());const r=()=>{const e=this.current,r=Lt(this.base);this.current===f&&r===this._startLocation||this.transitionTo(r,r=>{n&&rt(t,r,e,!0)})};window.addEventListener("popstate",r),this.listeners.push(()=>{window.removeEventListener("popstate",r)})}go(t){window.history.go(t)}push(t,e,n){const{current:r}=this;this.transitionTo(t,t=>{lt(v(this.base+t.fullPath)),rt(this.router,t,r,!1),e&&e(t)},n)}replace(t,e,n){const{current:r}=this;this.transitionTo(t,t=>{ft(v(this.base+t.fullPath)),rt(this.router,t,r,!1),e&&e(t)},n)}ensureURL(t){if(Lt(this.base)!==this.current.fullPath){const e=v(this.base+this.current.fullPath);t?lt(e):ft(e)}}getCurrentLocation(){return Lt(this.base)}}function Lt(t){let e=decodeURI(window.location.pathname);return t&&0===e.toLowerCase().indexOf(t.toLowerCase())&&(e=e.slice(t.length)),(e||"/")+window.location.search+window.location.hash}class Tt extends Ct{constructor(t,e,n){super(t,e),n&&function(t){const e=Lt(t);if(!/^\/#/.test(e))return window.location.replace(v(t+"/#"+e)),!0}(this.base)||Pt()}setupListeners(){if(this.listeners.length>0)return;const t=this.router.options.scrollBehavior,e=pt&&t;e&&this.listeners.push(nt());const n=()=>{const t=this.current;Pt()&&this.transitionTo(_t(),n=>{e&&rt(this.router,n,t,!0),pt||It(n.fullPath)})},r=pt?"popstate":"hashchange";window.addEventListener(r,n),this.listeners.push(()=>{window.removeEventListener(r,n)})}push(t,e,n){const{current:r}=this;this.transitionTo(t,t=>{Ut(t.fullPath),rt(this.router,t,r,!1),e&&e(t)},n)}replace(t,e,n){const{current:r}=this;this.transitionTo(t,t=>{It(t.fullPath),rt(this.router,t,r,!1),e&&e(t)},n)}go(t){window.history.go(t)}ensureURL(t){const e=this.current.fullPath;_t()!==e&&(t?Ut(e):It(e))}getCurrentLocation(){return _t()}}function Pt(){const t=_t();return"/"===t.charAt(0)||(It("/"+t),!1)}function _t(){let t=window.location.href;const e=t.indexOf("#");if(e<0)return"";const n=(t=t.slice(e+1)).indexOf("?");if(n<0){const e=t.indexOf("#");t=e>-1?decodeURI(t.slice(0,e))+t.slice(e):decodeURI(t)}else t=decodeURI(t.slice(0,n))+t.slice(n);return t}function qt(t){const e=window.location.href,n=e.indexOf("#");return`${n>=0?e.slice(0,n):e}#${t}`}function Ut(t){pt?lt(qt(t)):window.location.hash=t}function It(t){pt?ft(qt(t)):window.location.replace(qt(t))}class Mt extends Ct{constructor(t,e){super(t,e),this.stack=[],this.index=-1}push(t,e,n){this.transitionTo(t,t=>{this.stack=this.stack.slice(0,this.index+1).concat(t),this.index++,e&&e(t)},n)}replace(t,e,n){this.transitionTo(t,t=>{this.stack=this.stack.slice(0,this.index).concat(t),e&&e(t)},n)}go(t){const e=this.index+t;if(e<0||e>=this.stack.length)return;const n=this.stack[e];this.confirmTransition(n,()=>{this.index=e,this.updateRoute(n)},t=>{xt(t,yt.duplicated)&&(this.index=e)})}getCurrentLocation(){const t=this.stack[this.stack.length-1];return t?t.fullPath:"/"}ensureURL(){}}class Vt{constructor(t={}){this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=Q(t.routes||[],this);let e=t.mode||"hash";switch(this.fallback="history"===e&&!pt&&!1!==t.fallback,this.fallback&&(e="hash"),D||(e="abstract"),this.mode=e,e){case"history":this.history=new jt(this,t.base);break;case"hash":this.history=new Tt(this,t.base,this.fallback);break;case"abstract":this.history=new Mt(this,t.base)}}match(t,e,n){return this.matcher.match(t,e,n)}get currentRoute(){return this.history&&this.history.current}init(t){if(this.apps.push(t),t.$once("hook:destroyed",()=>{const e=this.apps.indexOf(t);e>-1&&this.apps.splice(e,1),this.app===t&&(this.app=this.apps[0]||null),this.app||this.history.teardownListeners()}),this.app)return;this.app=t;const e=this.history;if(e instanceof jt||e instanceof Tt){const t=t=>{const n=e.current,r=this.options.scrollBehavior;pt&&r&&"fullPath"in t&&rt(this,t,n,!1)},n=n=>{e.setupListeners(),t(n)};e.transitionTo(e.getCurrentLocation(),n,n)}e.listen(t=>{this.apps.forEach(e=>{e._route=t})})}beforeEach(t){return Bt(this.beforeHooks,t)}beforeResolve(t){return Bt(this.resolveHooks,t)}afterEach(t){return Bt(this.afterHooks,t)}onReady(t,e){this.history.onReady(t,e)}onError(t){this.history.onError(t)}push(t,e,n){if(!e&&!n&&"undefined"!=typeof Promise)return new Promise((e,n)=>{this.history.push(t,e,n)});this.history.push(t,e,n)}replace(t,e,n){if(!e&&!n&&"undefined"!=typeof Promise)return new Promise((e,n)=>{this.history.replace(t,e,n)});this.history.replace(t,e,n)}go(t){this.history.go(t)}back(){this.go(-1)}forward(){this.go(1)}getMatchedComponents(t){const e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map(t=>Object.keys(t.components).map(e=>t.components[e]))):[]}resolve(t,e,n){const r=M(t,e=e||this.history.current,n,this),o=this.match(r,e),i=o.redirectedFrom||o.fullPath;return{location:r,route:o,href:function(t,e,n){var r="hash"===n?"#"+e:e;return t?v(t+"/"+r):r}(this.history.base,i,this.mode),normalizedTo:r,resolved:o}}addRoutes(t){this.matcher.addRoutes(t),this.history.current!==f&&this.history.transitionTo(this.history.getCurrentLocation())}}function Bt(t,e){return t.push(e),()=>{const n=t.indexOf(e);n>-1&&t.splice(n,1)}}Vt.install=function t(n){if(t.installed&&z===n)return;t.installed=!0,z=n;const r=t=>void 0!==t,o=(t,e)=>{let n=t.$options._parentVnode;r(n)&&r(n=n.data)&&r(n=n.registerRouteInstance)&&n(t,e)};n.mixin({beforeCreate(){r(this.$options.router)?(this._routerRoot=this,this._router=this.$options.router,this._router.init(this),n.util.defineReactive(this,"_route",this._router.history.current)):this._routerRoot=this.$parent&&this.$parent._routerRoot||this,o(this,this)},destroyed(){o(this)}}),Object.defineProperty(n.prototype,"$router",{get(){return this._routerRoot._router}}),Object.defineProperty(n.prototype,"$route",{get(){return this._routerRoot._route}}),n.component("RouterView",e),n.component("RouterLink",H);const i=n.config.optionMergeStrategies;i.beforeRouteEnter=i.beforeRouteLeave=i.beforeRouteUpdate=i.created},Vt.version="3.4.0",Vt.isNavigationFailure=xt,Vt.NavigationFailureType=yt,D&&window.Vue&&window.Vue.use(Vt);export default Vt; \ No newline at end of file diff --git a/dist/vue-router.esm.js b/dist/vue-router.esm.js index b5ffade82..2b49b81bd 100644 --- a/dist/vue-router.esm.js +++ b/dist/vue-router.esm.js @@ -1,5 +1,5 @@ /*! - * vue-router v3.3.4 + * vue-router v3.4.0 * (c) 2020 Evan You * @license MIT */ @@ -17,14 +17,6 @@ function warn (condition, message) { } } -function isError (err) { - return Object.prototype.toString.call(err).indexOf('Error') > -1 -} - -function isRouterError (err, errorType) { - return isError(err) && err._isRouter && (errorType == null || err.type === errorType) -} - function extend (a, b) { for (var key in b) { a[key] = b[key]; @@ -133,7 +125,7 @@ var View = { }; var configProps = matched.props && matched.props[name]; - // save route and configProps in cachce + // save route and configProps in cache if (configProps) { extend(cache[name], { route: route, @@ -215,7 +207,8 @@ function resolveQuery ( parsedQuery = {}; } for (var key in extraQuery) { - parsedQuery[key] = extraQuery[key]; + var value = extraQuery[key]; + parsedQuery[key] = Array.isArray(value) ? value.map(function (v) { return '' + v; }) : '' + value; } return parsedQuery } @@ -1904,6 +1897,88 @@ function runQueue (queue, fn, cb) { step(0); } +var NavigationFailureType = { + redirected: 2, + aborted: 4, + cancelled: 8, + duplicated: 16 +}; + +function createNavigationRedirectedError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.redirected, + ("Redirected when going from \"" + (from.fullPath) + "\" to \"" + (stringifyRoute( + to + )) + "\" via a navigation guard.") + ) +} + +function createNavigationDuplicatedError (from, to) { + var error = createRouterError( + from, + to, + NavigationFailureType.duplicated, + ("Avoided redundant navigation to current location: \"" + (from.fullPath) + "\".") + ); + // backwards compatible with the first introduction of Errors + error.name = 'NavigationDuplicated'; + return error +} + +function createNavigationCancelledError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.cancelled, + ("Navigation cancelled from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" with a new navigation.") + ) +} + +function createNavigationAbortedError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.aborted, + ("Navigation aborted from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" via a navigation guard.") + ) +} + +function createRouterError (from, to, type, message) { + var error = new Error(message); + error._isRouter = true; + error.from = from; + error.to = to; + error.type = type; + + return error +} + +var propertiesToLog = ['params', 'query', 'hash']; + +function stringifyRoute (to) { + if (typeof to === 'string') { return to } + if ('path' in to) { return to.path } + var location = {}; + propertiesToLog.forEach(function (key) { + if (key in to) { location[key] = to[key]; } + }); + return JSON.stringify(location, null, 2) +} + +function isError (err) { + return Object.prototype.toString.call(err).indexOf('Error') > -1 +} + +function isNavigationFailure (err, errorType) { + return ( + isError(err) && + err._isRouter && + (errorType == null || err.type === errorType) + ) +} + /* */ function resolveAsyncComponents (matched) { @@ -2013,73 +2088,6 @@ function once (fn) { } } -var NavigationFailureType = { - redirected: 1, - aborted: 2, - cancelled: 3, - duplicated: 4 -}; - -function createNavigationRedirectedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.redirected, - ("Redirected when going from \"" + (from.fullPath) + "\" to \"" + (stringifyRoute( - to - )) + "\" via a navigation guard.") - ) -} - -function createNavigationDuplicatedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.duplicated, - ("Avoided redundant navigation to current location: \"" + (from.fullPath) + "\".") - ) -} - -function createNavigationCancelledError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.cancelled, - ("Navigation cancelled from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" with a new navigation.") - ) -} - -function createNavigationAbortedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.aborted, - ("Navigation aborted from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" via a navigation guard.") - ) -} - -function createRouterError (from, to, type, message) { - var error = new Error(message); - error._isRouter = true; - error.from = from; - error.to = to; - error.type = type; - - return error -} - -var propertiesToLog = ['params', 'query', 'hash']; - -function stringifyRoute (to) { - if (typeof to === 'string') { return to } - if ('path' in to) { return to.path } - var location = {}; - propertiesToLog.forEach(function (key) { - if (key in to) { location[key] = to[key]; } - }); - return JSON.stringify(location, null, 2) -} - /* */ var History = function History (router, base) { @@ -2121,7 +2129,17 @@ History.prototype.transitionTo = function transitionTo ( ) { var this$1 = this; - var route = this.router.match(location, this.current); + var route; + // catch redirect option https://github.com/vuejs/vue-router/issues/3201 + try { + route = this.router.match(location, this.current); + } catch (e) { + this.errorCbs.forEach(function (cb) { + cb(e); + }); + // Exception should still be thrown + throw e + } this.confirmTransition( route, function () { @@ -2149,7 +2167,7 @@ History.prototype.transitionTo = function transitionTo ( this$1.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$1.readyErrorCbs.forEach(function (cb) { cb(err); }); @@ -2171,7 +2189,7 @@ History.prototype.confirmTransition = function confirmTransition (route, onCompl // 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$1.errorCbs.length) { this$1.errorCbs.forEach(function (cb) { cb(err); @@ -2757,7 +2775,7 @@ var AbstractHistory = /*@__PURE__*/(function (History) { this$1.updateRoute(route); }, function (err) { - if (isRouterError(err, NavigationFailureType.duplicated)) { + if (isNavigationFailure(err, NavigationFailureType.duplicated)) { this$1.index = targetIndex; } } @@ -2778,8 +2796,6 @@ var AbstractHistory = /*@__PURE__*/(function (History) { /* */ - - var VueRouter = function VueRouter (options) { if ( options === void 0 ) options = {}; @@ -2792,7 +2808,8 @@ var VueRouter = function VueRouter (options) { this.matcher = createMatcher(options.routes || [], this); var mode = options.mode || 'hash'; - this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false; + this.fallback = + mode === 'history' && !supportsPushState && options.fallback !== false; if (this.fallback) { mode = 'hash'; } @@ -2820,11 +2837,7 @@ var VueRouter = function VueRouter (options) { var prototypeAccessors = { currentRoute: { configurable: true } }; -VueRouter.prototype.match = function match ( - raw, - current, - redirectedFrom -) { +VueRouter.prototype.match = function match (raw, current, redirectedFrom) { return this.matcher.match(raw, current, redirectedFrom) }; @@ -2835,11 +2848,12 @@ prototypeAccessors.currentRoute.get = function () { VueRouter.prototype.init = function init (app /* Vue component instance */) { var this$1 = this; - process.env.NODE_ENV !== 'production' && assert( - install.installed, - "not installed. Make sure to call `Vue.use(VueRouter)` " + - "before creating root instance." - ); + process.env.NODE_ENV !== 'production' && + assert( + install.installed, + "not installed. Make sure to call `Vue.use(VueRouter)` " + + "before creating root instance." + ); this.apps.push(app); @@ -2871,10 +2885,24 @@ VueRouter.prototype.init = function init (app /* Vue component instance */) { var history = this.history; if (history instanceof HTML5History || history instanceof HashHistory) { - var setupListeners = function () { + var handleInitialScroll = function (routeOrError) { + var from = history.current; + var expectScroll = this$1.options.scrollBehavior; + var supportsScroll = supportsPushState && expectScroll; + + if (supportsScroll && 'fullPath' in routeOrError) { + handleScroll(this$1, routeOrError, from, false); + } + }; + var setupListeners = function (routeOrError) { history.setupListeners(); + handleInitialScroll(routeOrError); }; - history.transitionTo(history.getCurrentLocation(), setupListeners, setupListeners); + history.transitionTo( + history.getCurrentLocation(), + setupListeners, + setupListeners + ); } history.listen(function (route) { @@ -2951,11 +2979,14 @@ VueRouter.prototype.getMatchedComponents = function getMatchedComponents (to) { if (!route) { return [] } - return [].concat.apply([], route.matched.map(function (m) { - return Object.keys(m.components).map(function (key) { - return m.components[key] + return [].concat.apply( + [], + route.matched.map(function (m) { + return Object.keys(m.components).map(function (key) { + return m.components[key] + }) }) - })) + ) }; VueRouter.prototype.resolve = function resolve ( @@ -2964,12 +2995,7 @@ VueRouter.prototype.resolve = function resolve ( append ) { current = current || this.history.current; - var location = normalizeLocation( - to, - current, - append, - this - ); + var location = normalizeLocation(to, current, append, this); var route = this.match(location, current); var fullPath = route.redirectedFrom || route.fullPath; var base = this.history.base; @@ -3007,7 +3033,9 @@ function createHref (base, fullPath, mode) { } VueRouter.install = install; -VueRouter.version = '3.3.4'; +VueRouter.version = '3.4.0'; +VueRouter.isNavigationFailure = isNavigationFailure; +VueRouter.NavigationFailureType = NavigationFailureType; if (inBrowser && window.Vue) { window.Vue.use(VueRouter); diff --git a/dist/vue-router.js b/dist/vue-router.js index 4a369c994..c799b63cf 100644 --- a/dist/vue-router.js +++ b/dist/vue-router.js @@ -1,5 +1,5 @@ /*! - * vue-router v3.3.4 + * vue-router v3.4.0 * (c) 2020 Evan You * @license MIT */ @@ -23,14 +23,6 @@ } } - function isError (err) { - return Object.prototype.toString.call(err).indexOf('Error') > -1 - } - - function isRouterError (err, errorType) { - return isError(err) && err._isRouter && (errorType == null || err.type === errorType) - } - function extend (a, b) { for (var key in b) { a[key] = b[key]; @@ -139,7 +131,7 @@ }; var configProps = matched.props && matched.props[name]; - // save route and configProps in cachce + // save route and configProps in cache if (configProps) { extend(cache[name], { route: route, @@ -221,7 +213,8 @@ parsedQuery = {}; } for (var key in extraQuery) { - parsedQuery[key] = extraQuery[key]; + var value = extraQuery[key]; + parsedQuery[key] = Array.isArray(value) ? value.map(function (v) { return '' + v; }) : '' + value; } return parsedQuery } @@ -1910,6 +1903,88 @@ step(0); } + var NavigationFailureType = { + redirected: 2, + aborted: 4, + cancelled: 8, + duplicated: 16 + }; + + function createNavigationRedirectedError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.redirected, + ("Redirected when going from \"" + (from.fullPath) + "\" to \"" + (stringifyRoute( + to + )) + "\" via a navigation guard.") + ) + } + + function createNavigationDuplicatedError (from, to) { + var error = createRouterError( + from, + to, + NavigationFailureType.duplicated, + ("Avoided redundant navigation to current location: \"" + (from.fullPath) + "\".") + ); + // backwards compatible with the first introduction of Errors + error.name = 'NavigationDuplicated'; + return error + } + + function createNavigationCancelledError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.cancelled, + ("Navigation cancelled from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" with a new navigation.") + ) + } + + function createNavigationAbortedError (from, to) { + return createRouterError( + from, + to, + NavigationFailureType.aborted, + ("Navigation aborted from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" via a navigation guard.") + ) + } + + function createRouterError (from, to, type, message) { + var error = new Error(message); + error._isRouter = true; + error.from = from; + error.to = to; + error.type = type; + + return error + } + + var propertiesToLog = ['params', 'query', 'hash']; + + function stringifyRoute (to) { + if (typeof to === 'string') { return to } + if ('path' in to) { return to.path } + var location = {}; + propertiesToLog.forEach(function (key) { + if (key in to) { location[key] = to[key]; } + }); + return JSON.stringify(location, null, 2) + } + + function isError (err) { + return Object.prototype.toString.call(err).indexOf('Error') > -1 + } + + function isNavigationFailure (err, errorType) { + return ( + isError(err) && + err._isRouter && + (errorType == null || err.type === errorType) + ) + } + /* */ function resolveAsyncComponents (matched) { @@ -2019,73 +2094,6 @@ } } - var NavigationFailureType = { - redirected: 1, - aborted: 2, - cancelled: 3, - duplicated: 4 - }; - - function createNavigationRedirectedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.redirected, - ("Redirected when going from \"" + (from.fullPath) + "\" to \"" + (stringifyRoute( - to - )) + "\" via a navigation guard.") - ) - } - - function createNavigationDuplicatedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.duplicated, - ("Avoided redundant navigation to current location: \"" + (from.fullPath) + "\".") - ) - } - - function createNavigationCancelledError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.cancelled, - ("Navigation cancelled from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" with a new navigation.") - ) - } - - function createNavigationAbortedError (from, to) { - return createRouterError( - from, - to, - NavigationFailureType.aborted, - ("Navigation aborted from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" via a navigation guard.") - ) - } - - function createRouterError (from, to, type, message) { - var error = new Error(message); - error._isRouter = true; - error.from = from; - error.to = to; - error.type = type; - - return error - } - - var propertiesToLog = ['params', 'query', 'hash']; - - function stringifyRoute (to) { - if (typeof to === 'string') { return to } - if ('path' in to) { return to.path } - var location = {}; - propertiesToLog.forEach(function (key) { - if (key in to) { location[key] = to[key]; } - }); - return JSON.stringify(location, null, 2) - } - /* */ var History = function History (router, base) { @@ -2127,7 +2135,17 @@ ) { var this$1 = this; - var route = this.router.match(location, this.current); + var route; + // catch redirect option https://github.com/vuejs/vue-router/issues/3201 + try { + route = this.router.match(location, this.current); + } catch (e) { + this.errorCbs.forEach(function (cb) { + cb(e); + }); + // Exception should still be thrown + throw e + } this.confirmTransition( route, function () { @@ -2155,7 +2173,7 @@ this$1.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$1.readyErrorCbs.forEach(function (cb) { cb(err); }); @@ -2177,7 +2195,7 @@ // 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$1.errorCbs.length) { this$1.errorCbs.forEach(function (cb) { cb(err); @@ -2763,7 +2781,7 @@ this$1.updateRoute(route); }, function (err) { - if (isRouterError(err, NavigationFailureType.duplicated)) { + if (isNavigationFailure(err, NavigationFailureType.duplicated)) { this$1.index = targetIndex; } } @@ -2784,8 +2802,6 @@ /* */ - - var VueRouter = function VueRouter (options) { if ( options === void 0 ) options = {}; @@ -2798,7 +2814,8 @@ this.matcher = createMatcher(options.routes || [], this); var mode = options.mode || 'hash'; - this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false; + this.fallback = + mode === 'history' && !supportsPushState && options.fallback !== false; if (this.fallback) { mode = 'hash'; } @@ -2826,11 +2843,7 @@ var prototypeAccessors = { currentRoute: { configurable: true } }; - VueRouter.prototype.match = function match ( - raw, - current, - redirectedFrom - ) { + VueRouter.prototype.match = function match (raw, current, redirectedFrom) { return this.matcher.match(raw, current, redirectedFrom) }; @@ -2841,11 +2854,12 @@ VueRouter.prototype.init = function init (app /* Vue component instance */) { var this$1 = this; - assert( - install.installed, - "not installed. Make sure to call `Vue.use(VueRouter)` " + - "before creating root instance." - ); + + assert( + install.installed, + "not installed. Make sure to call `Vue.use(VueRouter)` " + + "before creating root instance." + ); this.apps.push(app); @@ -2877,10 +2891,24 @@ var history = this.history; if (history instanceof HTML5History || history instanceof HashHistory) { - var setupListeners = function () { + var handleInitialScroll = function (routeOrError) { + var from = history.current; + var expectScroll = this$1.options.scrollBehavior; + var supportsScroll = supportsPushState && expectScroll; + + if (supportsScroll && 'fullPath' in routeOrError) { + handleScroll(this$1, routeOrError, from, false); + } + }; + var setupListeners = function (routeOrError) { history.setupListeners(); + handleInitialScroll(routeOrError); }; - history.transitionTo(history.getCurrentLocation(), setupListeners, setupListeners); + history.transitionTo( + history.getCurrentLocation(), + setupListeners, + setupListeners + ); } history.listen(function (route) { @@ -2957,11 +2985,14 @@ if (!route) { return [] } - return [].concat.apply([], route.matched.map(function (m) { - return Object.keys(m.components).map(function (key) { - return m.components[key] + return [].concat.apply( + [], + route.matched.map(function (m) { + return Object.keys(m.components).map(function (key) { + return m.components[key] + }) }) - })) + ) }; VueRouter.prototype.resolve = function resolve ( @@ -2970,12 +3001,7 @@ append ) { current = current || this.history.current; - var location = normalizeLocation( - to, - current, - append, - this - ); + var location = normalizeLocation(to, current, append, this); var route = this.match(location, current); var fullPath = route.redirectedFrom || route.fullPath; var base = this.history.base; @@ -3013,7 +3039,9 @@ } VueRouter.install = install; - VueRouter.version = '3.3.4'; + VueRouter.version = '3.4.0'; + VueRouter.isNavigationFailure = isNavigationFailure; + VueRouter.NavigationFailureType = NavigationFailureType; if (inBrowser && window.Vue) { window.Vue.use(VueRouter); diff --git a/dist/vue-router.min.js b/dist/vue-router.min.js index 90ffec353..505a64ebe 100644 --- a/dist/vue-router.min.js +++ b/dist/vue-router.min.js @@ -1,6 +1,6 @@ /*! - * vue-router v3.3.4 + * vue-router v3.4.0 * (c) 2020 Evan You * @license MIT */ -var t,e;t=this,e=function(){"use strict";function t(t){return Object.prototype.toString.call(t).indexOf("Error")>-1}function e(e,r){return t(e)&&e._isRouter&&(null==r||e.type===r)}function r(t,e){for(var r in e)t[r]=e[r];return t}var n={name:"RouterView",functional:!0,props:{name:{type:String,default:"default"}},render:function(t,e){var n=e.props,i=e.children,a=e.parent,s=e.data;s.routerView=!0;for(var u=a.$createElement,c=n.name,p=a.$route,f=a._routerViewCache||(a._routerViewCache={}),h=0,l=!1;a&&a._routerRoot!==a;){var d=a.$vnode?a.$vnode.data:{};d.routerView&&h++,d.keepAlive&&a._directInactive&&a._inactive&&(l=!0),a=a.$parent}if(s.routerViewDepth=h,l){var v=f[c],y=v&&v.component;return y?(v.configProps&&o(y,s,v.route,v.configProps),u(y,s,i)):u()}var m=p.matched[h],g=m&&m.components[c];if(!m||!g)return f[c]=null,u();f[c]={component:g},s.registerRouteInstance=function(t,e){var r=m.instances[c];(e&&r!==t||!e&&r===t)&&(m.instances[c]=e)},(s.hook||(s.hook={})).prepatch=function(t,e){m.instances[c]=e.componentInstance},s.hook.init=function(t){t.data.keepAlive&&t.componentInstance&&t.componentInstance!==m.instances[c]&&(m.instances[c]=t.componentInstance)};var w=m.props&&m.props[c];return w&&(r(f[c],{route:p,configProps:w}),o(g,s,p,w)),u(g,s,i)}};function o(t,e,n,o){var i=e.props=function(t,e){switch(typeof e){case"undefined":return;case"object":return e;case"function":return e(t);case"boolean":return e?t.params:void 0}}(n,o);if(i){i=e.props=r({},i);var a=e.attrs=e.attrs||{};for(var s in i)t.props&&s in t.props||(a[s]=i[s],delete i[s])}}var i=/[!'()*]/g,a=function(t){return"%"+t.charCodeAt(0).toString(16)},s=/%2C/g,u=function(t){return encodeURIComponent(t).replace(i,a).replace(s,",")},c=decodeURIComponent;function p(t){var e={};return(t=t.trim().replace(/^(\?|#|&)/,""))?(t.split("&").forEach(function(t){var r=t.replace(/\+/g," ").split("="),n=c(r.shift()),o=r.length>0?c(r.join("=")):null;void 0===e[n]?e[n]=o:Array.isArray(e[n])?e[n].push(o):e[n]=[e[n],o]}),e):e}function f(t){var e=t?Object.keys(t).map(function(e){var r=t[e];if(void 0===r)return"";if(null===r)return u(e);if(Array.isArray(r)){var n=[];return r.forEach(function(t){void 0!==t&&(null===t?n.push(u(e)):n.push(u(e)+"="+u(t)))}),n.join("&")}return u(e)+"="+u(r)}).filter(function(t){return t.length>0}).join("&"):null;return e?"?"+e:""}var h=/\/?$/;function l(t,e,r,n){var o=n&&n.options.stringifyQuery,i=e.query||{};try{i=d(i)}catch(t){}var a={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:i,params:e.params||{},fullPath:m(e,o),matched:t?y(t):[]};return r&&(a.redirectedFrom=m(r,o)),Object.freeze(a)}function d(t){if(Array.isArray(t))return t.map(d);if(t&&"object"==typeof t){var e={};for(var r in t)e[r]=d(t[r]);return e}return t}var v=l(null,{path:"/"});function y(t){for(var e=[];t;)e.unshift(t),t=t.parent;return e}function m(t,e){var r=t.path,n=t.query;void 0===n&&(n={});var o=t.hash;return void 0===o&&(o=""),(r||"/")+(e||f)(n)+o}function g(t,e){return e===v?t===e:!!e&&(t.path&&e.path?t.path.replace(h,"")===e.path.replace(h,"")&&t.hash===e.hash&&w(t.query,e.query):!(!t.name||!e.name)&&t.name===e.name&&t.hash===e.hash&&w(t.query,e.query)&&w(t.params,e.params))}function w(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var r=Object.keys(t),n=Object.keys(e);return r.length===n.length&&r.every(function(r){var n=t[r],o=e[r];return"object"==typeof n&&"object"==typeof o?w(n,o):String(n)===String(o)})}function b(t,e,r){var n=t.charAt(0);if("/"===n)return t;if("?"===n||"#"===n)return e+t;var o=e.split("/");r&&o[o.length-1]||o.pop();for(var i=t.replace(/^\//,"").split("/"),a=0;a=0&&(e=t.slice(n),t=t.slice(0,n));var o=t.indexOf("?");return o>=0&&(r=t.slice(o+1),t=t.slice(0,o)),{path:t,query:r,hash:e}}(i.path||""),f=e&&e.path||"/",h=c.path?b(c.path,f,n||i.append):f,l=function(t,e,r){void 0===e&&(e={});var n,o=r||p;try{n=o(t||"")}catch(t){n={}}for(var i in e)n[i]=e[i];return n}(c.query,i.query,o&&o.options.parseQuery),d=i.hash||c.hash;return d&&"#"!==d.charAt(0)&&(d="#"+d),{_normalized:!0,path:h,query:l,hash:d}}var H,z=[String,Object],F=[String,Array],N=function(){},D={name:"RouterLink",props:{to:{type:z,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,ariaCurrentValue:{type:String,default:"page"},event:{type:F,default:"click"}},render:function(t){var e=this,n=this.$router,o=this.$route,i=n.resolve(this.to,o,this.append),a=i.location,s=i.route,u=i.href,c={},p=n.options.linkActiveClass,f=n.options.linkExactActiveClass,d=null==p?"router-link-active":p,v=null==f?"router-link-exact-active":f,y=null==this.activeClass?d:this.activeClass,m=null==this.exactActiveClass?v:this.exactActiveClass,w=s.redirectedFrom?l(null,B(s.redirectedFrom),null,n):s;c[m]=g(o,w),c[y]=this.exact?c[m]:function(t,e){return 0===t.path.replace(h,"/").indexOf(e.path.replace(h,"/"))&&(!e.hash||t.hash===e.hash)&&function(t,e){for(var r in e)if(!(r in t))return!1;return!0}(t.query,e.query)}(o,w);var b=c[m]?this.ariaCurrentValue:null,x=function(t){K(t)&&(e.replace?n.replace(a,N):n.push(a,N))},R={click:K};Array.isArray(this.event)?this.event.forEach(function(t){R[t]=x}):R[this.event]=x;var k={class:c},E=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:u,route:s,navigate:x,isActive:c[y],isExactActive:c[m]});if(E){if(1===E.length)return E[0];if(E.length>1||!E.length)return 0===E.length?t():t("span",{},E)}if("a"===this.tag)k.on=R,k.attrs={href:u,"aria-current":b};else{var O=function t(e){if(e)for(var r,n=0;n-1&&(s.params[h]=r.params[h]);return s.path=V(p.path,s.params),u(p,s,a)}if(s.path){s.params={};for(var l=0;l=t.length?r():t[o]?e(t[o],function(){n(o+1)}):n(o+1)};n(0)}function gt(e){return function(r,n,o){var i=!1,a=0,s=null;wt(e,function(e,r,n,u){if("function"==typeof e&&void 0===e.cid){i=!0,a++;var c,p=Rt(function(t){var r;((r=t).__esModule||xt&&"Module"===r[Symbol.toStringTag])&&(t=t.default),e.resolved="function"==typeof t?t:H.extend(t),n.components[u]=t,--a<=0&&o()}),f=Rt(function(e){var r="Failed to resolve async component "+u+": "+e;s||(s=t(e)?e:new Error(r),o(s))});try{c=e(p,f)}catch(t){f(t)}if(c)if("function"==typeof c.then)c.then(p,f);else{var h=c.component;h&&"function"==typeof h.then&&h.then(p,f)}}}),i||o()}}function wt(t,e){return bt(t.map(function(t){return Object.keys(t.components).map(function(r){return e(t.components[r],t.instances[r],t,r)})}))}function bt(t){return Array.prototype.concat.apply([],t)}var xt="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;function Rt(t){var e=!1;return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(!e)return e=!0,t.apply(this,r)}}var kt={redirected:1,aborted:2,cancelled:3,duplicated:4};function Et(t,e){return At(t,e,kt.redirected,'Redirected when going from "'+t.fullPath+'" to "'+function(t){if("string"==typeof t)return t;if("path"in t)return t.path;var e={};return Ct.forEach(function(r){r in t&&(e[r]=t[r])}),JSON.stringify(e,null,2)}(e)+'" via a navigation guard.')}function Ot(t,e){return At(t,e,kt.cancelled,'Navigation cancelled from "'+t.fullPath+'" to "'+e.fullPath+'" with a new navigation.')}function At(t,e,r,n){var o=new Error(n);return o._isRouter=!0,o.from=t,o.to=e,o.type=r,o}var Ct=["params","query","hash"],_t=function(t,e){this.router=t,this.base=function(t){if(!t)if(J){var e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else t="/";return"/"!==t.charAt(0)&&(t="/"+t),t.replace(/\/$/,"")}(e),this.current=v,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[],this.listeners=[]};function jt(t,e,r,n){var o=wt(t,function(t,n,o,i){var a=function(t,e){return"function"!=typeof t&&(t=H.extend(t)),t.options[e]}(t,e);if(a)return Array.isArray(a)?a.map(function(t){return r(t,n,o,i)}):r(a,n,o,i)});return bt(n?o.reverse():o)}function St(t,e){if(e)return function(){return t.apply(e,arguments)}}_t.prototype.listen=function(t){this.cb=t},_t.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},_t.prototype.onError=function(t){this.errorCbs.push(t)},_t.prototype.transitionTo=function(t,r,n){var o=this,i=this.router.match(t,this.current);this.confirmTransition(i,function(){var t=o.current;o.updateRoute(i),r&&r(i),o.ensureURL(),o.router.afterHooks.forEach(function(e){e&&e(i,t)}),o.ready||(o.ready=!0,o.readyCbs.forEach(function(t){t(i)}))},function(t){n&&n(t),t&&!o.ready&&(o.ready=!0,e(t,kt.redirected)?o.readyCbs.forEach(function(t){t(i)}):o.readyErrorCbs.forEach(function(e){e(t)}))})},_t.prototype.confirmTransition=function(r,n,o){var i,a=this,s=this.current,u=function(r){!e(r)&&t(r)&&(a.errorCbs.length?a.errorCbs.forEach(function(t){t(r)}):console.error(r)),o&&o(r)},c=r.matched.length-1,p=s.matched.length-1;if(g(r,s)&&c===p&&r.matched[c]===s.matched[p])return this.ensureURL(),u(At(i=s,r,kt.duplicated,'Avoided redundant navigation to current location: "'+i.fullPath+'".'));var f=function(t,e){var r,n=Math.max(t.length,e.length);for(r=0;r0)){var e=this.router,r=e.options.scrollBehavior,n=dt&&r;n&&this.listeners.push(ot());var o=function(){var r=t.current,o=$t(t.base);t.current===v&&o===t._startLocation||t.transitionTo(o,function(t){n&&it(e,t,r,!0)})};window.addEventListener("popstate",o),this.listeners.push(function(){window.removeEventListener("popstate",o)})}},e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){vt(x(n.base+t.fullPath)),it(n.router,t,o,!1),e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){yt(x(n.base+t.fullPath)),it(n.router,t,o,!1),e&&e(t)},r)},e.prototype.ensureURL=function(t){if($t(this.base)!==this.current.fullPath){var e=x(this.base+this.current.fullPath);t?vt(e):yt(e)}},e.prototype.getCurrentLocation=function(){return $t(this.base)},e}(_t);function $t(t){var e=decodeURI(window.location.pathname);return t&&0===e.toLowerCase().indexOf(t.toLowerCase())&&(e=e.slice(t.length)),(e||"/")+window.location.search+window.location.hash}var Pt=function(t){function e(e,r,n){t.call(this,e,r),n&&function(t){var e=$t(t);if(!/^\/#/.test(e))return window.location.replace(x(t+"/#"+e)),!0}(this.base)||Tt()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this;if(!(this.listeners.length>0)){var e=this.router.options.scrollBehavior,r=dt&&e;r&&this.listeners.push(ot());var n=function(){var e=t.current;Tt()&&t.transitionTo(qt(),function(n){r&&it(t.router,n,e,!0),dt||Mt(n.fullPath)})},o=dt?"popstate":"hashchange";window.addEventListener(o,n),this.listeners.push(function(){window.removeEventListener(o,n)})}},e.prototype.push=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){It(t.fullPath),it(n.router,t,o,!1),e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){Mt(t.fullPath),it(n.router,t,o,!1),e&&e(t)},r)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;qt()!==e&&(t?It(e):Mt(e))},e.prototype.getCurrentLocation=function(){return qt()},e}(_t);function Tt(){var t=qt();return"/"===t.charAt(0)||(Mt("/"+t),!1)}function qt(){var t=window.location.href,e=t.indexOf("#");if(e<0)return"";var r=(t=t.slice(e+1)).indexOf("?");if(r<0){var n=t.indexOf("#");t=n>-1?decodeURI(t.slice(0,n))+t.slice(n):decodeURI(t)}else t=decodeURI(t.slice(0,r))+t.slice(r);return t}function Ut(t){var e=window.location.href,r=e.indexOf("#");return(r>=0?e.slice(0,r):e)+"#"+t}function It(t){dt?vt(Ut(t)):window.location.hash=t}function Mt(t){dt?yt(Ut(t)):window.location.replace(Ut(t))}var Vt=function(t){function r(e,r){t.call(this,e,r),this.stack=[],this.index=-1}return t&&(r.__proto__=t),r.prototype=Object.create(t&&t.prototype),r.prototype.constructor=r,r.prototype.push=function(t,e,r){var n=this;this.transitionTo(t,function(t){n.stack=n.stack.slice(0,n.index+1).concat(t),n.index++,e&&e(t)},r)},r.prototype.replace=function(t,e,r){var n=this;this.transitionTo(t,function(t){n.stack=n.stack.slice(0,n.index).concat(t),e&&e(t)},r)},r.prototype.go=function(t){var r=this,n=this.index+t;if(!(n<0||n>=this.stack.length)){var o=this.stack[n];this.confirmTransition(o,function(){r.index=n,r.updateRoute(o)},function(t){e(t,kt.duplicated)&&(r.index=n)})}},r.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},r.prototype.ensureURL=function(){},r}(_t),Bt=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=Y(t.routes||[],this);var e=t.mode||"hash";switch(this.fallback="history"===e&&!dt&&!1!==t.fallback,this.fallback&&(e="hash"),J||(e="abstract"),this.mode=e,e){case"history":this.history=new Lt(this,t.base);break;case"hash":this.history=new Pt(this,t.base,this.fallback);break;case"abstract":this.history=new Vt(this,t.base)}},Ht={currentRoute:{configurable:!0}};function zt(t,e){return t.push(e),function(){var r=t.indexOf(e);r>-1&&t.splice(r,1)}}return Bt.prototype.match=function(t,e,r){return this.matcher.match(t,e,r)},Ht.currentRoute.get=function(){return this.history&&this.history.current},Bt.prototype.init=function(t){var e=this;if(this.apps.push(t),t.$once("hook:destroyed",function(){var r=e.apps.indexOf(t);r>-1&&e.apps.splice(r,1),e.app===t&&(e.app=e.apps[0]||null),e.app||e.history.teardownListeners()}),!this.app){this.app=t;var r=this.history;if(r instanceof Lt||r instanceof Pt){var n=function(){r.setupListeners()};r.transitionTo(r.getCurrentLocation(),n,n)}r.listen(function(t){e.apps.forEach(function(e){e._route=t})})}},Bt.prototype.beforeEach=function(t){return zt(this.beforeHooks,t)},Bt.prototype.beforeResolve=function(t){return zt(this.resolveHooks,t)},Bt.prototype.afterEach=function(t){return zt(this.afterHooks,t)},Bt.prototype.onReady=function(t,e){this.history.onReady(t,e)},Bt.prototype.onError=function(t){this.history.onError(t)},Bt.prototype.push=function(t,e,r){var n=this;if(!e&&!r&&"undefined"!=typeof Promise)return new Promise(function(e,r){n.history.push(t,e,r)});this.history.push(t,e,r)},Bt.prototype.replace=function(t,e,r){var n=this;if(!e&&!r&&"undefined"!=typeof Promise)return new Promise(function(e,r){n.history.replace(t,e,r)});this.history.replace(t,e,r)},Bt.prototype.go=function(t){this.history.go(t)},Bt.prototype.back=function(){this.go(-1)},Bt.prototype.forward=function(){this.go(1)},Bt.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map(function(t){return Object.keys(t.components).map(function(e){return t.components[e]})})):[]},Bt.prototype.resolve=function(t,e,r){var n=B(t,e=e||this.history.current,r,this),o=this.match(n,e),i=o.redirectedFrom||o.fullPath;return{location:n,route:o,href:function(t,e,r){var n="hash"===r?"#"+e:e;return t?x(t+"/"+n):n}(this.history.base,i,this.mode),normalizedTo:n,resolved:o}},Bt.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==v&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(Bt.prototype,Ht),Bt.install=function t(e){if(!t.installed||H!==e){t.installed=!0,H=e;var r=function(t){return void 0!==t},o=function(t,e){var n=t.$options._parentVnode;r(n)&&r(n=n.data)&&r(n=n.registerRouteInstance)&&n(t,e)};e.mixin({beforeCreate:function(){r(this.$options.router)?(this._routerRoot=this,this._router=this.$options.router,this._router.init(this),e.util.defineReactive(this,"_route",this._router.history.current)):this._routerRoot=this.$parent&&this.$parent._routerRoot||this,o(this,this)},destroyed:function(){o(this)}}),Object.defineProperty(e.prototype,"$router",{get:function(){return this._routerRoot._router}}),Object.defineProperty(e.prototype,"$route",{get:function(){return this._routerRoot._route}}),e.component("RouterView",n),e.component("RouterLink",D);var i=e.config.optionMergeStrategies;i.beforeRouteEnter=i.beforeRouteLeave=i.beforeRouteUpdate=i.created}},Bt.version="3.3.4",J&&window.Vue&&window.Vue.use(Bt),Bt},"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).VueRouter=e(); \ No newline at end of file +var t,e;t=this,e=function(){"use strict";function t(t,e){for(var r in e)t[r]=e[r];return t}var e={name:"RouterView",functional:!0,props:{name:{type:String,default:"default"}},render:function(e,n){var o=n.props,i=n.children,a=n.parent,s=n.data;s.routerView=!0;for(var u=a.$createElement,c=o.name,p=a.$route,f=a._routerViewCache||(a._routerViewCache={}),h=0,l=!1;a&&a._routerRoot!==a;){var d=a.$vnode?a.$vnode.data:{};d.routerView&&h++,d.keepAlive&&a._directInactive&&a._inactive&&(l=!0),a=a.$parent}if(s.routerViewDepth=h,l){var v=f[c],y=v&&v.component;return y?(v.configProps&&r(y,s,v.route,v.configProps),u(y,s,i)):u()}var m=p.matched[h],g=m&&m.components[c];if(!m||!g)return f[c]=null,u();f[c]={component:g},s.registerRouteInstance=function(t,e){var r=m.instances[c];(e&&r!==t||!e&&r===t)&&(m.instances[c]=e)},(s.hook||(s.hook={})).prepatch=function(t,e){m.instances[c]=e.componentInstance},s.hook.init=function(t){t.data.keepAlive&&t.componentInstance&&t.componentInstance!==m.instances[c]&&(m.instances[c]=t.componentInstance)};var w=m.props&&m.props[c];return w&&(t(f[c],{route:p,configProps:w}),r(g,s,p,w)),u(g,s,i)}};function r(e,r,n,o){var i=r.props=function(t,e){switch(typeof e){case"undefined":return;case"object":return e;case"function":return e(t);case"boolean":return e?t.params:void 0}}(n,o);if(i){i=r.props=t({},i);var a=r.attrs=r.attrs||{};for(var s in i)e.props&&s in e.props||(a[s]=i[s],delete i[s])}}var n=/[!'()*]/g,o=function(t){return"%"+t.charCodeAt(0).toString(16)},i=/%2C/g,a=function(t){return encodeURIComponent(t).replace(n,o).replace(i,",")},s=decodeURIComponent;function u(t){var e={};return(t=t.trim().replace(/^(\?|#|&)/,""))?(t.split("&").forEach(function(t){var r=t.replace(/\+/g," ").split("="),n=s(r.shift()),o=r.length>0?s(r.join("=")):null;void 0===e[n]?e[n]=o:Array.isArray(e[n])?e[n].push(o):e[n]=[e[n],o]}),e):e}function c(t){var e=t?Object.keys(t).map(function(e){var r=t[e];if(void 0===r)return"";if(null===r)return a(e);if(Array.isArray(r)){var n=[];return r.forEach(function(t){void 0!==t&&(null===t?n.push(a(e)):n.push(a(e)+"="+a(t)))}),n.join("&")}return a(e)+"="+a(r)}).filter(function(t){return t.length>0}).join("&"):null;return e?"?"+e:""}var p=/\/?$/;function f(t,e,r,n){var o=n&&n.options.stringifyQuery,i=e.query||{};try{i=h(i)}catch(t){}var a={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:i,params:e.params||{},fullPath:v(e,o),matched:t?d(t):[]};return r&&(a.redirectedFrom=v(r,o)),Object.freeze(a)}function h(t){if(Array.isArray(t))return t.map(h);if(t&&"object"==typeof t){var e={};for(var r in t)e[r]=h(t[r]);return e}return t}var l=f(null,{path:"/"});function d(t){for(var e=[];t;)e.unshift(t),t=t.parent;return e}function v(t,e){var r=t.path,n=t.query;void 0===n&&(n={});var o=t.hash;return void 0===o&&(o=""),(r||"/")+(e||c)(n)+o}function y(t,e){return e===l?t===e:!!e&&(t.path&&e.path?t.path.replace(p,"")===e.path.replace(p,"")&&t.hash===e.hash&&m(t.query,e.query):!(!t.name||!e.name)&&t.name===e.name&&t.hash===e.hash&&m(t.query,e.query)&&m(t.params,e.params))}function m(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var r=Object.keys(t),n=Object.keys(e);return r.length===n.length&&r.every(function(r){var n=t[r],o=e[r];return"object"==typeof n&&"object"==typeof o?m(n,o):String(n)===String(o)})}function g(t,e,r){var n=t.charAt(0);if("/"===n)return t;if("?"===n||"#"===n)return e+t;var o=e.split("/");r&&o[o.length-1]||o.pop();for(var i=t.replace(/^\//,"").split("/"),a=0;a=0&&(e=t.slice(n),t=t.slice(0,n));var o=t.indexOf("?");return o>=0&&(r=t.slice(o+1),t=t.slice(0,o)),{path:t,query:r,hash:e}}(i.path||""),f=r&&r.path||"/",h=p.path?g(p.path,f,n||i.append):f,l=function(t,e,r){void 0===e&&(e={});var n,o=r||u;try{n=o(t||"")}catch(t){n={}}for(var i in e){var a=e[i];n[i]=Array.isArray(a)?a.map(function(t){return""+t}):""+a}return n}(p.query,i.query,o&&o.options.parseQuery),d=i.hash||p.hash;return d&&"#"!==d.charAt(0)&&(d="#"+d),{_normalized:!0,path:h,query:l,hash:d}}var V,B=[String,Object],F=[String,Array],H=function(){},N={name:"RouterLink",props:{to:{type:B,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,ariaCurrentValue:{type:String,default:"page"},event:{type:F,default:"click"}},render:function(e){var r=this,n=this.$router,o=this.$route,i=n.resolve(this.to,o,this.append),a=i.location,s=i.route,u=i.href,c={},h=n.options.linkActiveClass,l=n.options.linkExactActiveClass,d=null==h?"router-link-active":h,v=null==l?"router-link-exact-active":l,m=null==this.activeClass?d:this.activeClass,g=null==this.exactActiveClass?v:this.exactActiveClass,w=s.redirectedFrom?f(null,M(s.redirectedFrom),null,n):s;c[g]=y(o,w),c[m]=this.exact?c[g]:function(t,e){return 0===t.path.replace(p,"/").indexOf(e.path.replace(p,"/"))&&(!e.hash||t.hash===e.hash)&&function(t,e){for(var r in e)if(!(r in t))return!1;return!0}(t.query,e.query)}(o,w);var b=c[g]?this.ariaCurrentValue:null,x=function(t){z(t)&&(r.replace?n.replace(a,H):n.push(a,H))},R={click:z};Array.isArray(this.event)?this.event.forEach(function(t){R[t]=x}):R[this.event]=x;var k={class:c},E=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:u,route:s,navigate:x,isActive:c[m],isExactActive:c[g]});if(E){if(1===E.length)return E[0];if(E.length>1||!E.length)return 0===E.length?e():e("span",{},E)}if("a"===this.tag)k.on=R,k.attrs={href:u,"aria-current":b};else{var A=function t(e){if(e)for(var r,n=0;n-1&&(s.params[h]=r.params[h]);return s.path=I(p.path,s.params),u(p,s,a)}if(s.path){s.params={};for(var l=0;l=t.length?r():t[o]?e(t[o],function(){n(o+1)}):n(o+1)};n(0)}var yt={redirected:2,aborted:4,cancelled:8,duplicated:16};function mt(t,e){return wt(t,e,yt.redirected,'Redirected when going from "'+t.fullPath+'" to "'+function(t){if("string"==typeof t)return t;if("path"in t)return t.path;var e={};return bt.forEach(function(r){r in t&&(e[r]=t[r])}),JSON.stringify(e,null,2)}(e)+'" via a navigation guard.')}function gt(t,e){return wt(t,e,yt.cancelled,'Navigation cancelled from "'+t.fullPath+'" to "'+e.fullPath+'" with a new navigation.')}function wt(t,e,r,n){var o=new Error(n);return o._isRouter=!0,o.from=t,o.to=e,o.type=r,o}var bt=["params","query","hash"];function xt(t){return Object.prototype.toString.call(t).indexOf("Error")>-1}function Rt(t,e){return xt(t)&&t._isRouter&&(null==e||t.type===e)}function kt(t){return function(e,r,n){var o=!1,i=0,a=null;Et(t,function(t,e,r,s){if("function"==typeof t&&void 0===t.cid){o=!0,i++;var u,c=Ct(function(e){var o;((o=e).__esModule||Ot&&"Module"===o[Symbol.toStringTag])&&(e=e.default),t.resolved="function"==typeof e?e:V.extend(e),r.components[s]=e,--i<=0&&n()}),p=Ct(function(t){var e="Failed to resolve async component "+s+": "+t;a||(a=xt(t)?t:new Error(e),n(a))});try{u=t(c,p)}catch(t){p(t)}if(u)if("function"==typeof u.then)u.then(c,p);else{var f=u.component;f&&"function"==typeof f.then&&f.then(c,p)}}}),o||n()}}function Et(t,e){return At(t.map(function(t){return Object.keys(t.components).map(function(r){return e(t.components[r],t.instances[r],t,r)})}))}function At(t){return Array.prototype.concat.apply([],t)}var Ot="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;function Ct(t){var e=!1;return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(!e)return e=!0,t.apply(this,r)}}var _t=function(t,e){this.router=t,this.base=function(t){if(!t)if(D){var e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else t="/";return"/"!==t.charAt(0)&&(t="/"+t),t.replace(/\/$/,"")}(e),this.current=l,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[],this.listeners=[]};function jt(t,e,r,n){var o=Et(t,function(t,n,o,i){var a=function(t,e){return"function"!=typeof t&&(t=V.extend(t)),t.options[e]}(t,e);if(a)return Array.isArray(a)?a.map(function(t){return r(t,n,o,i)}):r(a,n,o,i)});return At(n?o.reverse():o)}function St(t,e){if(e)return function(){return t.apply(e,arguments)}}_t.prototype.listen=function(t){this.cb=t},_t.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},_t.prototype.onError=function(t){this.errorCbs.push(t)},_t.prototype.transitionTo=function(t,e,r){var n,o=this;try{n=this.router.match(t,this.current)}catch(t){throw this.errorCbs.forEach(function(e){e(t)}),t}this.confirmTransition(n,function(){var t=o.current;o.updateRoute(n),e&&e(n),o.ensureURL(),o.router.afterHooks.forEach(function(e){e&&e(n,t)}),o.ready||(o.ready=!0,o.readyCbs.forEach(function(t){t(n)}))},function(t){r&&r(t),t&&!o.ready&&(o.ready=!0,Rt(t,yt.redirected)?o.readyCbs.forEach(function(t){t(n)}):o.readyErrorCbs.forEach(function(e){e(t)}))})},_t.prototype.confirmTransition=function(t,e,r){var n,o,i=this,a=this.current,s=function(t){!Rt(t)&&xt(t)&&(i.errorCbs.length?i.errorCbs.forEach(function(e){e(t)}):console.error(t)),r&&r(t)},u=t.matched.length-1,c=a.matched.length-1;if(y(t,a)&&u===c&&t.matched[u]===a.matched[c])return this.ensureURL(),s(((o=wt(n=a,t,yt.duplicated,'Avoided redundant navigation to current location: "'+n.fullPath+'".')).name="NavigationDuplicated",o));var p=function(t,e){var r,n=Math.max(t.length,e.length);for(r=0;r0)){var e=this.router,r=e.options.scrollBehavior,n=ht&&r;n&&this.listeners.push(rt());var o=function(){var r=t.current,o=$t(t.base);t.current===l&&o===t._startLocation||t.transitionTo(o,function(t){n&&nt(e,t,r,!0)})};window.addEventListener("popstate",o),this.listeners.push(function(){window.removeEventListener("popstate",o)})}},e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){lt(w(n.base+t.fullPath)),nt(n.router,t,o,!1),e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){dt(w(n.base+t.fullPath)),nt(n.router,t,o,!1),e&&e(t)},r)},e.prototype.ensureURL=function(t){if($t(this.base)!==this.current.fullPath){var e=w(this.base+this.current.fullPath);t?lt(e):dt(e)}},e.prototype.getCurrentLocation=function(){return $t(this.base)},e}(_t);function $t(t){var e=decodeURI(window.location.pathname);return t&&0===e.toLowerCase().indexOf(t.toLowerCase())&&(e=e.slice(t.length)),(e||"/")+window.location.search+window.location.hash}var Pt=function(t){function e(e,r,n){t.call(this,e,r),n&&function(t){var e=$t(t);if(!/^\/#/.test(e))return window.location.replace(w(t+"/#"+e)),!0}(this.base)||Tt()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this;if(!(this.listeners.length>0)){var e=this.router.options.scrollBehavior,r=ht&&e;r&&this.listeners.push(rt());var n=function(){var e=t.current;Tt()&&t.transitionTo(qt(),function(n){r&&nt(t.router,n,e,!0),ht||Mt(n.fullPath)})},o=ht?"popstate":"hashchange";window.addEventListener(o,n),this.listeners.push(function(){window.removeEventListener(o,n)})}},e.prototype.push=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){It(t.fullPath),nt(n.router,t,o,!1),e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){Mt(t.fullPath),nt(n.router,t,o,!1),e&&e(t)},r)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;qt()!==e&&(t?It(e):Mt(e))},e.prototype.getCurrentLocation=function(){return qt()},e}(_t);function Tt(){var t=qt();return"/"===t.charAt(0)||(Mt("/"+t),!1)}function qt(){var t=window.location.href,e=t.indexOf("#");if(e<0)return"";var r=(t=t.slice(e+1)).indexOf("?");if(r<0){var n=t.indexOf("#");t=n>-1?decodeURI(t.slice(0,n))+t.slice(n):decodeURI(t)}else t=decodeURI(t.slice(0,r))+t.slice(r);return t}function Ut(t){var e=window.location.href,r=e.indexOf("#");return(r>=0?e.slice(0,r):e)+"#"+t}function It(t){ht?lt(Ut(t)):window.location.hash=t}function Mt(t){ht?dt(Ut(t)):window.location.replace(Ut(t))}var Vt=function(t){function e(e,r){t.call(this,e,r),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,r){var n=this;this.transitionTo(t,function(t){n.stack=n.stack.slice(0,n.index+1).concat(t),n.index++,e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this;this.transitionTo(t,function(t){n.stack=n.stack.slice(0,n.index).concat(t),e&&e(t)},r)},e.prototype.go=function(t){var e=this,r=this.index+t;if(!(r<0||r>=this.stack.length)){var n=this.stack[r];this.confirmTransition(n,function(){e.index=r,e.updateRoute(n)},function(t){Rt(t,yt.duplicated)&&(e.index=r)})}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},e.prototype.ensureURL=function(){},e}(_t),Bt=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=Q(t.routes||[],this);var e=t.mode||"hash";switch(this.fallback="history"===e&&!ht&&!1!==t.fallback,this.fallback&&(e="hash"),D||(e="abstract"),this.mode=e,e){case"history":this.history=new Lt(this,t.base);break;case"hash":this.history=new Pt(this,t.base,this.fallback);break;case"abstract":this.history=new Vt(this,t.base)}},Ft={currentRoute:{configurable:!0}};function Ht(t,e){return t.push(e),function(){var r=t.indexOf(e);r>-1&&t.splice(r,1)}}return Bt.prototype.match=function(t,e,r){return this.matcher.match(t,e,r)},Ft.currentRoute.get=function(){return this.history&&this.history.current},Bt.prototype.init=function(t){var e=this;if(this.apps.push(t),t.$once("hook:destroyed",function(){var r=e.apps.indexOf(t);r>-1&&e.apps.splice(r,1),e.app===t&&(e.app=e.apps[0]||null),e.app||e.history.teardownListeners()}),!this.app){this.app=t;var r=this.history;if(r instanceof Lt||r instanceof Pt){var n=function(t){r.setupListeners(),function(t){var n=r.current,o=e.options.scrollBehavior;ht&&o&&"fullPath"in t&&nt(e,t,n,!1)}(t)};r.transitionTo(r.getCurrentLocation(),n,n)}r.listen(function(t){e.apps.forEach(function(e){e._route=t})})}},Bt.prototype.beforeEach=function(t){return Ht(this.beforeHooks,t)},Bt.prototype.beforeResolve=function(t){return Ht(this.resolveHooks,t)},Bt.prototype.afterEach=function(t){return Ht(this.afterHooks,t)},Bt.prototype.onReady=function(t,e){this.history.onReady(t,e)},Bt.prototype.onError=function(t){this.history.onError(t)},Bt.prototype.push=function(t,e,r){var n=this;if(!e&&!r&&"undefined"!=typeof Promise)return new Promise(function(e,r){n.history.push(t,e,r)});this.history.push(t,e,r)},Bt.prototype.replace=function(t,e,r){var n=this;if(!e&&!r&&"undefined"!=typeof Promise)return new Promise(function(e,r){n.history.replace(t,e,r)});this.history.replace(t,e,r)},Bt.prototype.go=function(t){this.history.go(t)},Bt.prototype.back=function(){this.go(-1)},Bt.prototype.forward=function(){this.go(1)},Bt.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map(function(t){return Object.keys(t.components).map(function(e){return t.components[e]})})):[]},Bt.prototype.resolve=function(t,e,r){var n=M(t,e=e||this.history.current,r,this),o=this.match(n,e),i=o.redirectedFrom||o.fullPath;return{location:n,route:o,href:function(t,e,r){var n="hash"===r?"#"+e:e;return t?w(t+"/"+n):n}(this.history.base,i,this.mode),normalizedTo:n,resolved:o}},Bt.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==l&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(Bt.prototype,Ft),Bt.install=function t(r){if(!t.installed||V!==r){t.installed=!0,V=r;var n=function(t){return void 0!==t},o=function(t,e){var r=t.$options._parentVnode;n(r)&&n(r=r.data)&&n(r=r.registerRouteInstance)&&r(t,e)};r.mixin({beforeCreate:function(){n(this.$options.router)?(this._routerRoot=this,this._router=this.$options.router,this._router.init(this),r.util.defineReactive(this,"_route",this._router.history.current)):this._routerRoot=this.$parent&&this.$parent._routerRoot||this,o(this,this)},destroyed:function(){o(this)}}),Object.defineProperty(r.prototype,"$router",{get:function(){return this._routerRoot._router}}),Object.defineProperty(r.prototype,"$route",{get:function(){return this._routerRoot._route}}),r.component("RouterView",e),r.component("RouterLink",N);var i=r.config.optionMergeStrategies;i.beforeRouteEnter=i.beforeRouteLeave=i.beforeRouteUpdate=i.created}},Bt.version="3.4.0",Bt.isNavigationFailure=Rt,Bt.NavigationFailureType=yt,D&&window.Vue&&window.Vue.use(Bt),Bt},"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).VueRouter=e(); \ No newline at end of file From 4860ad0cb28e351a55026b922afdb96cd0769780 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 5 Aug 2020 10:37:38 +0200 Subject: [PATCH 31/32] chore(release): 3.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa6cff2b1..2173016e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-router", - "version": "3.3.4", + "version": "3.4.0", "description": "Official router for Vue.js 2", "author": "Evan You", "license": "MIT", From 4f752308cd1a220d3b74ca10e91181cfb7e89335 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 5 Aug 2020 10:38:24 +0200 Subject: [PATCH 32/32] chore(changelog): 3.4.0 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e5e25451..a3ab76aa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +# [3.4.0](https://github.com/vuejs/vue-router/compare/v3.3.4...v3.4.0) (2020-08-05) + + +### Bug Fixes + +* **query:** cast query values to strings (fix [#2131](https://github.com/vuejs/vue-router/issues/2131)) ([#3232](https://github.com/vuejs/vue-router/issues/3232)) ([f0d9c2d](https://github.com/vuejs/vue-router/commit/f0d9c2d)) +* **scroll:** run scrollBehavior on initial load (fix [#3196](https://github.com/vuejs/vue-router/issues/3196)) ([#3199](https://github.com/vuejs/vue-router/issues/3199)) ([84398ae](https://github.com/vuejs/vue-router/commit/84398ae)) +* **types:** add missing `options` property type ([#3248](https://github.com/vuejs/vue-router/issues/3248)) ([83920c9](https://github.com/vuejs/vue-router/commit/83920c9)) + + +### Features + +* add vetur tags and attributes ([bf1e1bd](https://github.com/vuejs/vue-router/commit/bf1e1bd)) +* **errors:** capture errors thrown in redirect callback in onError ([#3251](https://github.com/vuejs/vue-router/issues/3251)) ([40e4df7](https://github.com/vuejs/vue-router/commit/40e4df7)), closes [#3201](https://github.com/vuejs/vue-router/issues/3201) [#3201](https://github.com/vuejs/vue-router/issues/3201) [#3201](https://github.com/vuejs/vue-router/issues/3201) +* **errors:** expose `isNavigationFailure` ([8d92dc0](https://github.com/vuejs/vue-router/commit/8d92dc0)) +* **errors:** NavigationDuplicated name for backwards compatibility ([b854a20](https://github.com/vuejs/vue-router/commit/b854a20)) + + + ## [3.3.4](https://github.com/vuejs/vue-router/compare/v3.3.3...v3.3.4) (2020-06-13)