diff --git a/.eslintignore b/.eslintignore index 1521c8b76..2b88bf081 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ dist +*.ts diff --git a/.github/funding.yml b/.github/funding.yml index 56d2f2fba..e4c7570c1 100644 --- a/.github/funding.yml +++ b/.github/funding.yml @@ -1,3 +1,2 @@ github: [posva, yyx990803] -patreon: posva open_collective: vuejs diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml new file mode 100644 index 000000000..268578f3f --- /dev/null +++ b/.github/workflows/release-tag.yml @@ -0,0 +1,23 @@ +on: + push: + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Create Release + +jobs: + build: + name: Create Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@master + - name: Create Release for Tag + id: release_tag + uses: yyx990803/release-tag@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + body: | + Please refer to [CHANGELOG.md](https://github.com/vuejs/vue-router/blob/dev/CHANGELOG.md) for details. diff --git a/CHANGELOG.md b/CHANGELOG.md index 57bdbcce5..520f3ff13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,259 @@ -## [3.1.4](https://github.com/vuejs/vue-router/compare/v3.1.3...v3.1.4) (2020-01-14) +## [3.6.5](https://github.com/vuejs/vue-router/compare/v3.6.4...v3.6.5) (2022-09-06) + +### Bug Fixes + +- **types:** Component with 4 generics for Vue 2.6 ([d6064df](https://github.com/vuejs/vue-router/commit/d6064df1112497dac98e4302d81607efdb1a58c6)), closes [#3786](https://github.com/vuejs/vue-router/issues/3786) + +## [3.6.4](https://github.com/vuejs/vue-router/compare/v3.6.3...v3.6.4) (2022-08-25) + +This release fixes some compatibility issues of the new `vue-router/composables` with webpack 4. + +### Features + +- **types:** add composables.d.ts in root ([#3784](https://github.com/vuejs/vue-router/issues/3784)) ([0cf54de](https://github.com/vuejs/vue-router/commit/0cf54de782a0b05692bbe78a7181495b6a35b8d9)) + +## [3.6.3](https://github.com/vuejs/vue-router/compare/v3.6.2...v3.6.3) (2022-08-23) + +### Bug Fixes + +- **build:** export all named exports esm build ([a6647c8](https://github.com/vuejs/vue-router/commit/a6647c8c3d7022f1b702935461c7d234b052ca06)) +- **types:** allow jsx components ([0cb86b3](https://github.com/vuejs/vue-router/commit/0cb86b3865b713201f9db49c7a8d23e9a2876f29)), closes [#3776](https://github.com/vuejs/vue-router/issues/3776) +- **types:** missing NavigationFailureType and isNavigationFailure ([#3777](https://github.com/vuejs/vue-router/issues/3777)) ([9d001dd](https://github.com/vuejs/vue-router/commit/9d001dd0bebdea1e1a8ec2f0c77113b6a2e2b6a3)) + +## [3.6.2](https://github.com/vuejs/vue-router/compare/v3.6.1...v3.6.2) (2022-08-23) + +### Bug Fixes + +- **build:** add mjs build ([b4c3940](https://github.com/vuejs/vue-router/commit/b4c39404eff7ae2f657c405d7b0f939ce20cfdec)) +- **types:** missing start location ([1356acb](https://github.com/vuejs/vue-router/commit/1356acb983c5eccb00c5c0ec3f406218ae49a8c1)) + +## [3.6.1](https://github.com/vuejs/vue-router/compare/v3.6.0...v3.6.1) (2022-08-23) + +### Bug Fixes + +- **build:** ensure install fn before Vue.use ([0126bcb](https://github.com/vuejs/vue-router/commit/0126bcbfb0e3cb824bfce05090ca018faf02ce5e)), closes [#3772](https://github.com/vuejs/vue-router/issues/3772) + +# [3.6.0](https://github.com/vuejs/vue-router/compare/v3.5.4...v3.6.0) (2022-08-22) + +This release of Vue Router introduces composables from Vue Router 4: + +```js +import { useRoute, useRouter, useLink, onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router/composables' +``` + +Since these composables **require Vue 2.7**, they are only exposed under `vue-router/composables` submodule, so it shouldn't affect you if you stay on Vue 2.6 (Note there are no new features or fixes besides the composables in this release). Refer to [the Vue Router 4 API documentation](https://router.vuejs.org/api/#onbeforerouteleave) for details on the composables. + +### Features + +- **types:** useLink() ([77bd0e3](https://github.com/vuejs/vue-router/commit/77bd0e317dd5a9aebfca515f0f28f3284c7d8260)) +- useLink() ([50332e5](https://github.com/vuejs/vue-router/commit/50332e5e93e6aa1194a9e68a60937a6f9e8bcecd)) +- **types:** expose RouterLink and RouterView in d.ts ([cad978a](https://github.com/vuejs/vue-router/commit/cad978a832174aac59cad86fe780f8a64a9754d7)) +- add RouterLink and RouterView to esm ([4511f39](https://github.com/vuejs/vue-router/commit/4511f393334247c9702ed378220bf925cdc09add)) +- add vue 2.7 types ([cba9650](https://github.com/vuejs/vue-router/commit/cba9650e5cbf958c1db9cd259a2e7bfbc28bddbe)) +- onBeforeRouteUpdate onBeforeRouteLeave ([9861c55](https://github.com/vuejs/vue-router/commit/9861c553627f5f34a07ad3ac28e2ed02aab99d47)) +- useRoute and useRouter ([ea35594](https://github.com/vuejs/vue-router/commit/ea355943e097914ae55fa54ccb7df929c901e80d)) + +## [3.5.4](https://github.com/vuejs/vue-router/compare/v3.5.3...v3.5.4) (2022-05-16) + +### Bug Fixes + +- remove whitespace between mulitple slashes ([86d7f1f](https://github.com/vuejs/vue-router/commit/86d7f1fdaa36432f6564309925690ec20bb2981e)), closes [#3743](https://github.com/vuejs/vue-router/issues/3743) + +## [3.5.3](https://github.com/vuejs/vue-router/compare/v3.5.2...v3.5.3) (2021-10-26) +### Bug Fixes + +- clean more than two consecutive slashes ([#3652](https://github.com/vuejs/vue-router/issues/3652)) ([3e3a07e](https://github.com/vuejs/vue-router/commit/3e3a07ee6e7defd6cae75bddcede5a28b0092709)) +- **scrollBehavior:** trigger scroll behavior if same route with hash ([#3592](https://github.com/vuejs/vue-router/issues/3592)) ([57d8042](https://github.com/vuejs/vue-router/commit/57d8042c8b99f92bfe35493b8ae9bba827864bf0)) + +### Features + +- add constructor hint ([#3626](https://github.com/vuejs/vue-router/issues/3626)) ([28b769b](https://github.com/vuejs/vue-router/commit/28b769b2a07e3bf984c0ec20d6d797291a480e81)) + +## [3.5.2](https://github.com/vuejs/vue-router/compare/v3.5.1...v3.5.2) (2021-06-21) ### Bug Fixes -* suppress warning if `pathMatch` is empty ([#3081](https://github.com/vuejs/vue-router/issues/3081)) ([ddc6bc7](https://github.com/vuejs/vue-router/commit/ddc6bc7)), closes [#3072](https://github.com/vuejs/vue-router/issues/3072) -* **link:** correctly warn wrong v-slot usage ([a150291](https://github.com/vuejs/vue-router/commit/a150291)), closes [#3091](https://github.com/vuejs/vue-router/issues/3091) -* **location:** add a copy for params with named locations ([#2802](https://github.com/vuejs/vue-router/issues/2802)) ([2b39f5a](https://github.com/vuejs/vue-router/commit/2b39f5a)), closes [#2800](https://github.com/vuejs/vue-router/issues/2800) [#2938](https://github.com/vuejs/vue-router/issues/2938) [#2938](https://github.com/vuejs/vue-router/issues/2938) +- **history:** stricter check of base in HTML5 ([#3556](https://github.com/vuejs/vue-router/issues/3556)) ([11dd184](https://github.com/vuejs/vue-router/commit/11dd184dc6a872c6399977fa4b7c259225ce4834)) +- **types:** added missing router.match ([#3554](https://github.com/vuejs/vue-router/issues/3554)) ([394a3b6](https://github.com/vuejs/vue-router/commit/394a3b6cce5e395ae4ccf3e2efb0c115d492978c)) + +## [3.5.1](https://github.com/vuejs/vue-router/compare/v3.5.0...v3.5.1) (2021-01-26) + +### Bug Fixes +- **warn:** only warn if "tag" or "event" is used ([#3458](https://github.com/vuejs/vue-router/issues/3458)) ([b7a31b9](https://github.com/vuejs/vue-router/commit/b7a31b9)), closes [#3457](https://github.com/vuejs/vue-router/issues/3457) + +# [3.5.0](https://github.com/vuejs/vue-router/compare/v3.4.9...v3.5.0) (2021-01-25) ### Features -* **history:** preserve existing history.state ([c0d3376](https://github.com/vuejs/vue-router/commit/c0d3376)), closes [#3006](https://github.com/vuejs/vue-router/issues/3006) +- **link:** exact-path prop ([825328e](https://github.com/vuejs/vue-router/commit/825328e)), closes [#2040](https://github.com/vuejs/vue-router/issues/2040) +- **warn:** warn deprecated addRoutes ([2e41445](https://github.com/vuejs/vue-router/commit/2e41445)) +- expose START_LOCATION ([53b68dd](https://github.com/vuejs/vue-router/commit/53b68dd)), closes [#2718](https://github.com/vuejs/vue-router/issues/2718) +- **link:** deprecate v-slot without custom prop ([ceeda4c](https://github.com/vuejs/vue-router/commit/ceeda4c)) +- **link:** warn deprecated props ([d2cb951](https://github.com/vuejs/vue-router/commit/d2cb951)) +- **router:** add getRoutes ([6bc30aa](https://github.com/vuejs/vue-router/commit/6bc30aa)) +- **types:** add types for getRoutes addRoute ([fb9bb60](https://github.com/vuejs/vue-router/commit/fb9bb60)) +- addRoute as nested route ([ca80c44](https://github.com/vuejs/vue-router/commit/ca80c44)), closes [#1156](https://github.com/vuejs/vue-router/issues/1156) + +## [3.4.9](https://github.com/vuejs/vue-router/compare/v3.4.8...v3.4.9) (2020-11-05) + +### Bug Fixes + +- **encoding:** decode params ([#3350](https://github.com/vuejs/vue-router/issues/3350)) ([63c749c](https://github.com/vuejs/vue-router/commit/63c749c)) + +## [3.4.8](https://github.com/vuejs/vue-router/compare/v3.4.7...v3.4.8) (2020-10-26) + +### Features + +- **scroll:** add behavior support on scrollBehavior ([#3351](https://github.com/vuejs/vue-router/issues/3351)) ([4e0b3e0](https://github.com/vuejs/vue-router/commit/4e0b3e0)) + +## [3.4.7](https://github.com/vuejs/vue-router/compare/v3.4.6...v3.4.7) (2020-10-16) + +### Bug Fixes + +- **matcher:** should try catch decode only ([1f32f03](https://github.com/vuejs/vue-router/commit/1f32f03)) +- **query:** check existing keys ([4b926e3](https://github.com/vuejs/vue-router/commit/4b926e3)), closes [#3341](https://github.com/vuejs/vue-router/issues/3341) + +## [3.4.6](https://github.com/vuejs/vue-router/compare/v3.4.5...v3.4.6) (2020-10-07) + +### Bug Fixes + +- **encoding:** try catch decodes ([607ce2d](https://github.com/vuejs/vue-router/commit/607ce2d)) +- **ssr:** memory leak in poll method ([#2875](https://github.com/vuejs/vue-router/issues/2875)) ([7693eb5](https://github.com/vuejs/vue-router/commit/7693eb5)) +- remove duplicated decodeURIComponent ([#3323](https://github.com/vuejs/vue-router/issues/3323)) ([560d11d](https://github.com/vuejs/vue-router/commit/560d11d)) + +## [3.4.5](https://github.com/vuejs/vue-router/compare/v3.4.4...v3.4.5) (2020-09-26) + +### Bug Fixes + +- **history:** do not call onReady on initial redirection ([a1a290e](https://github.com/vuejs/vue-router/commit/a1a290e)), closes [#3331](https://github.com/vuejs/vue-router/issues/3331) + +## [3.4.4](https://github.com/vuejs/vue-router/compare/v3.4.3...v3.4.4) (2020-09-24) + +### Bug Fixes +- **abstract:** call afterHooks with go ([4da7021](https://github.com/vuejs/vue-router/commit/4da7021)), closes [#3250](https://github.com/vuejs/vue-router/issues/3250) +- **history:** mark redundant navigation as pending ([893d86b](https://github.com/vuejs/vue-router/commit/893d86b)), closes [#3133](https://github.com/vuejs/vue-router/issues/3133) +- **types:** add missing NavigationFailure types ([fda7067](https://github.com/vuejs/vue-router/commit/fda7067)), closes [#3293](https://github.com/vuejs/vue-router/issues/3293) +- **types:** fix VueRouter.NavigationFailureType ([ecc8e27](https://github.com/vuejs/vue-router/commit/ecc8e27)) + +### Features + +- **history:** Reset history.current when all apps are destroyed ([#3298](https://github.com/vuejs/vue-router/issues/3298)) ([c69ff7b](https://github.com/vuejs/vue-router/commit/c69ff7b)) + +## [3.4.3](https://github.com/vuejs/vue-router/compare/v3.4.2...v3.4.3) (2020-08-11) + +- Revert 4fbaa9f7880276e661227442ef5923131a589210: "fix: keep repeated params in query/hash relative locations" Closes #3289 + +## [3.4.2](https://github.com/vuejs/vue-router/compare/v3.4.1...v3.4.2) (2020-08-07) + +### Bug Fixes + +- **query:** leave object as is ([7b3328d](https://github.com/vuejs/vue-router/commit/7b3328d)), closes [#3282](https://github.com/vuejs/vue-router/issues/3282) +- keep repeated params in query/hash relative locations ([4fbaa9f](https://github.com/vuejs/vue-router/commit/4fbaa9f)) + +## [3.4.1](https://github.com/vuejs/vue-router/compare/v3.4.0...v3.4.1) (2020-08-06) + +### Bug Fixes + +- **query:** remove undefined values ([b952573](https://github.com/vuejs/vue-router/commit/b952573)), closes [#3276](https://github.com/vuejs/vue-router/issues/3276) +- **router:** properly check null and undefined in isSameRoute ([d6546d9](https://github.com/vuejs/vue-router/commit/d6546d9)) + +# [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) + +### 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) + +### Bug Fixes + +- **history:** initial redirect call onReady's onSuccess ([4d484bf](https://github.com/vuejs/vue-router/commit/4d484bf)), closes [#3225](https://github.com/vuejs/vue-router/issues/3225) +- update ja docs ([#3214](https://github.com/vuejs/vue-router/issues/3214)) ([c05f741](https://github.com/vuejs/vue-router/commit/c05f741)) + +### Features + +- better wording for navigation redirected failure ([1f3aea6](https://github.com/vuejs/vue-router/commit/1f3aea6)) +- **types:** RouterConfig for multiple components ([#3217](https://github.com/vuejs/vue-router/issues/3217)) ([#3218](https://github.com/vuejs/vue-router/issues/3218)) ([dab86c5](https://github.com/vuejs/vue-router/commit/dab86c5)) + +## [3.3.2](https://github.com/vuejs/vue-router/compare/v3.3.1...v3.3.2) (2020-05-29) + +### Bug Fixes + +- **errors:** NavigationCanceled with async components ([#3211](https://github.com/vuejs/vue-router/issues/3211)) ([be39ca3](https://github.com/vuejs/vue-router/commit/be39ca3)) +- remove error.stack modification ([#3212](https://github.com/vuejs/vue-router/issues/3212)) ([a0075ed](https://github.com/vuejs/vue-router/commit/a0075ed)) + +## [3.3.1](https://github.com/vuejs/vue-router/compare/v3.3.0...v3.3.1) (2020-05-27) + +### Bug Fixes + +- **errors:** avoid unnecessary log of errors ([2c77247](https://github.com/vuejs/vue-router/commit/2c77247)) + +# [3.3.0](https://github.com/vuejs/vue-router/compare/v3.2.0...v3.3.0) (2020-05-27) + +### Features + +- **errors:** create router errors ([#3047](https://github.com/vuejs/vue-router/issues/3047)) ([4c727f9](https://github.com/vuejs/vue-router/commit/4c727f9)) +- **history:** Remove event listeners when all apps are destroyed. ([#3172](https://github.com/vuejs/vue-router/issues/3172)) ([4c81be8](https://github.com/vuejs/vue-router/commit/4c81be8)), closes [#3152](https://github.com/vuejs/vue-router/issues/3152) [#2341](https://github.com/vuejs/vue-router/issues/2341) +- **url:** call afterEach hooks after url is ensured ([#2292](https://github.com/vuejs/vue-router/issues/2292)) ([1575a18](https://github.com/vuejs/vue-router/commit/1575a18)), closes [#2079](https://github.com/vuejs/vue-router/issues/2079) + +# [3.2.0](https://github.com/vuejs/vue-router/compare/v3.1.6...v3.2.0) (2020-05-19) + +### Bug Fixes + +- **html5:** make base case insensitive ([04a2143](https://github.com/vuejs/vue-router/commit/04a2143)), closes [#2154](https://github.com/vuejs/vue-router/issues/2154) +- check for pushState being a function ([bc41f67](https://github.com/vuejs/vue-router/commit/bc41f67)), closes [#3154](https://github.com/vuejs/vue-router/issues/3154) + +### Features + +- **link:** add aria-current to active links (close [#2116](https://github.com/vuejs/vue-router/issues/2116)) ([#3073](https://github.com/vuejs/vue-router/issues/3073)) ([6ec0ee5](https://github.com/vuejs/vue-router/commit/6ec0ee5)) +- **scroll:** use manual scrollRestoration with scrollBehavior ([#1814](https://github.com/vuejs/vue-router/issues/1814)) ([1261363](https://github.com/vuejs/vue-router/commit/1261363)) +- **types:** NavigationGuardNext ([#2497](https://github.com/vuejs/vue-router/issues/2497)) ([d18c497](https://github.com/vuejs/vue-router/commit/d18c497)) + +## [3.1.6](https://github.com/vuejs/vue-router/compare/v3.1.5...v3.1.6) (2020-02-26) + +### Bug Fixes + +- preserve history state when reloading ([a4ec3e2](https://github.com/vuejs/vue-router/commit/a4ec3e2)) +- **ts:** add null to Route.name ([#3117](https://github.com/vuejs/vue-router/issues/3117)) ([8f831f2](https://github.com/vuejs/vue-router/commit/8f831f2)) +- correctly calculate `path` when `pathMatch` is empty string ([#3111](https://github.com/vuejs/vue-router/issues/3111)) ([38e6ccd](https://github.com/vuejs/vue-router/commit/38e6ccd)), closes [#3106](https://github.com/vuejs/vue-router/issues/3106) + +## [3.1.5](https://github.com/vuejs/vue-router/compare/v3.1.4...v3.1.5) (2020-01-15) + +### Bug Fixes + +- **view:** add passing props to inactive component ([#2773](https://github.com/vuejs/vue-router/issues/2773)) ([0fb1343](https://github.com/vuejs/vue-router/commit/0fb1343)), closes [#2301](https://github.com/vuejs/vue-router/issues/2301) +- **view:** fix deeply nested keep-alive router-views displaying ([#2930](https://github.com/vuejs/vue-router/issues/2930)) ([0c2b1aa](https://github.com/vuejs/vue-router/commit/0c2b1aa)), closes [#2923](https://github.com/vuejs/vue-router/issues/2923) + +## [3.1.4](https://github.com/vuejs/vue-router/compare/v3.1.3...v3.1.4) (2020-01-14) + +### Bug Fixes + +- suppress warning if `pathMatch` is empty ([#3081](https://github.com/vuejs/vue-router/issues/3081)) ([ddc6bc7](https://github.com/vuejs/vue-router/commit/ddc6bc7)), closes [#3072](https://github.com/vuejs/vue-router/issues/3072) +- **link:** correctly warn wrong v-slot usage ([a150291](https://github.com/vuejs/vue-router/commit/a150291)), closes [#3091](https://github.com/vuejs/vue-router/issues/3091) +- **location:** add a copy for params with named locations ([#2802](https://github.com/vuejs/vue-router/issues/2802)) ([2b39f5a](https://github.com/vuejs/vue-router/commit/2b39f5a)), closes [#2800](https://github.com/vuejs/vue-router/issues/2800) [#2938](https://github.com/vuejs/vue-router/issues/2938) [#2938](https://github.com/vuejs/vue-router/issues/2938) + +### Features +- **history:** preserve existing history.state ([c0d3376](https://github.com/vuejs/vue-router/commit/c0d3376)), closes [#3006](https://github.com/vuejs/vue-router/issues/3006) ## [3.1.3](https://github.com/vuejs/vue-router/compare/v3.1.2...v3.1.3) (2019-08-30) diff --git a/README.md b/README.md index b375363a4..40cba38ce 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,95 @@ # vue-router [![Build Status](https://img.shields.io/circleci/project/github/vuejs/vue-router/dev.svg)](https://circleci.com/gh/vuejs/vue-router) -> This is vue-router 3.0 which works only with Vue 2.0. For the 1.x router see the [1.0 branch](https://github.com/vuejs/vue-router/tree/1.0). - -### Introduction - -`vue-router` is the official router for [Vue.js](http://vuejs.org). It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze. Features include: - -- Nested route/view mapping -- Modular, component-based router configuration -- Route params, query, wildcards -- View transition effects powered by Vue.js' transition system -- Fine-grained navigation control -- Links with automatic active CSS classes -- HTML5 history mode or hash mode, with auto-fallback in IE9 -- Customizable Scroll Behavior - -Get started with the [documentation](http://router.vuejs.org), or play with the [examples](https://github.com/vuejs/vue-router/tree/dev/examples) (see how to run them below). +> This is vue-router 3.0 which works only with Vue 2.0. +> - For the 1.x router see the [1.0 branch](https://github.com/vuejs/vue-router/tree/1.0). +> - For Vue Router 4 (for Vue 3) see [vuejs/router](https://github.com/vuejs/router). + +

Supporting Vue Router

+ +Vue Router is part of the Vue Ecosystem and is an MIT-licensed open source project with its ongoing development made possible entirely by the support of Sponsors. If you would like to become a sponsor, please consider: + +- [Become a Sponsor on GitHub](https://github.com/sponsors/posva) +- [One-time donation via PayPal](https://paypal.me/posva) + + + +

Gold Sponsors

+

+ + + + VueJobs + + +

+ +

Silver Sponsors

+

+ + + + VueMastery + + + + + + Prefect + + +

+ +

Bronze Sponsors

+

+ + + + Stanislas Ormières + + + + + + Antony Konstantinidis + + + + + + Storyblok + + + + + + NuxtJS + + +

+ + + +--- + +Get started with the [documentation](http://v3.router.vuejs.org), or play with the [examples](https://github.com/vuejs/vue-router/tree/dev/examples) (see how to run them below). ### Development Setup -``` bash +```bash # install deps -npm install +yarn # build dist files -npm run build +yarn build # serve examples at localhost:8080 -npm run dev +yarn dev # lint & run all tests -npm test +yarn test # serve docs at localhost:8080 -npm run docs +yarn docs ``` ## Releasing @@ -61,7 +117,7 @@ Please make sure to read the [Contributing Guide](https://github.com/vuejs/vue/b ## Changelog -Details changes for each release are documented in the [release notes](https://github.com/vuejs/vue-router/releases). +Details changes for each release are documented in the [`CHANGELOG.md file`](https://github.com/vuejs/vue-router/blob/dev/CHANGELOG.md). ## Stay In Touch diff --git a/build/configs.js b/build/configs.js index 67371b8e2..4fbfa6d24 100644 --- a/build/configs.js +++ b/build/configs.js @@ -1,12 +1,11 @@ const path = require('path') const buble = require('rollup-plugin-buble') const flow = require('rollup-plugin-flow-no-whitespace') -const cjs = require('rollup-plugin-commonjs') -const node = require('rollup-plugin-node-resolve') +const cjs = require('@rollup/plugin-commonjs') +const node = require('@rollup/plugin-node-resolve').nodeResolve const replace = require('rollup-plugin-replace') const version = process.env.VERSION || require('../package.json').version -const banner = -`/*! +const banner = `/*! * vue-router v${version} * (c) ${new Date().getFullYear()} Evan You * @license MIT @@ -31,27 +30,45 @@ module.exports = [ format: 'cjs' }, { + input: resolve('src/entries/esm.js'), file: resolve('dist/vue-router.esm.js'), format: 'es' }, { + input: resolve('src/entries/esm.js'), + file: resolve('dist/vue-router.mjs'), + format: 'es' + }, + { + input: resolve('src/entries/esm.js'), file: resolve('dist/vue-router.esm.browser.js'), format: 'es', env: 'development', transpile: false }, { + input: resolve('src/entries/esm.js'), file: resolve('dist/vue-router.esm.browser.min.js'), format: 'es', env: 'production', transpile: false + }, + { + input: resolve('src/composables/index.js'), + file: resolve('./composables.mjs'), + format: 'es' + }, + { + input: resolve('src/composables/index.js'), + file: resolve('./composables.js'), + format: 'cjs' } ].map(genConfig) function genConfig (opts) { const config = { input: { - input: resolve('src/index.js'), + input: opts.input || resolve('src/index.js'), plugins: [ flow(), node(), @@ -59,7 +76,8 @@ function genConfig (opts) { replace({ __VERSION__: version }) - ] + ], + external: ['vue'] }, output: { file: opts.file, @@ -70,9 +88,11 @@ function genConfig (opts) { } if (opts.env) { - config.input.plugins.unshift(replace({ - 'process.env.NODE_ENV': JSON.stringify(opts.env) - })) + config.input.plugins.unshift( + replace({ + 'process.env.NODE_ENV': JSON.stringify(opts.env) + }) + ) } if (opts.transpile !== false) { diff --git a/composables.d.ts b/composables.d.ts new file mode 100644 index 000000000..790fbfba8 --- /dev/null +++ b/composables.d.ts @@ -0,0 +1 @@ +export * from './types/composables' diff --git a/composables.js b/composables.js new file mode 100644 index 000000000..18d96d549 --- /dev/null +++ b/composables.js @@ -0,0 +1,257 @@ +/*! + * vue-router v3.6.5 + * (c) 2022 Evan You + * @license MIT + */ +'use strict' + +Object.defineProperty(exports, '__esModule', { value: true }) + +var vue = require('vue') + +// dev only warn if no current instance + +function throwNoCurrentInstance (method) { + if (!vue.getCurrentInstance()) { + throw new Error( + ('[vue-router]: Missing current instance. ' + method + '() must be called inside + + diff --git a/docs/.vuepress/components/HomeSponsorsGroup.vue b/docs/.vuepress/components/HomeSponsorsGroup.vue new file mode 100644 index 000000000..f8e2dab77 --- /dev/null +++ b/docs/.vuepress/components/HomeSponsorsGroup.vue @@ -0,0 +1,43 @@ + + + diff --git a/docs/.vuepress/components/sponsors.json b/docs/.vuepress/components/sponsors.json new file mode 100644 index 000000000..e9c704b39 --- /dev/null +++ b/docs/.vuepress/components/sponsors.json @@ -0,0 +1,44 @@ +{ + "platinum": [], + "gold": [], + "silver": [ + { + "href": "https://www.vuemastery.com/", + "alt": "VueMastery", + "imgSrcLight": "https://posva-sponsors.pages.dev/logos/vuemastery-light.svg", + "imgSrcDark": "https://posva-sponsors.pages.dev/logos/vuemastery-dark.png" + }, + { + "href": "https://www.prefect.io/", + "imgSrcLight": "https://posva-sponsors.pages.dev/logos/prefectlogo-light.svg", + "imgSrcDark": "https://posva-sponsors.pages.dev/logos/prefectlogo-dark.svg", + "alt": "Prefect" + } + ], + "bronze": [ + { + "alt": "Stanislas Ormières", + "href": "https://stormier.ninja", + "imgSrcDark": "https://avatars.githubusercontent.com/u/2486424?u=7b0c73ae5d090ce53bf59473094e9606fe082c59&v=4", + "imgSrcLight": "https://avatars.githubusercontent.com/u/2486424?u=7b0c73ae5d090ce53bf59473094e9606fe082c59&v=4" + }, + { + "alt": "Antony Konstantinidis", + "href": "https://www.vuejs.de", + "imgSrcDark": "https://avatars.githubusercontent.com/u/4183726?u=6b50a8ea16de29d2982f43c5640b1db9299ebcd1&v=4", + "imgSrcLight": "https://avatars.githubusercontent.com/u/4183726?u=6b50a8ea16de29d2982f43c5640b1db9299ebcd1&v=4" + }, + { + "href": "https://storyblok.com", + "imgSrcLight": "https://posva-sponsors.pages.dev/logos/storyblok.png", + "imgSrcDark": "https://posva-sponsors.pages.dev/logos/storyblok.png", + "alt": "Storyblok" + }, + { + "href": "https://nuxtjs.org", + "imgSrcLight": "https://posva-sponsors.pages.dev/logos/nuxt-light.svg", + "imgSrcDark": "https://posva-sponsors.pages.dev/logos/nuxt-dark.svg", + "alt": "Nuxt Labs" + } + ] +} diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index d4f6c8922..79c12f352 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -1,4 +1,4 @@ -module.exports = { +module.exports = ctx => ({ locales: { '/': { lang: 'en-US', @@ -26,20 +26,63 @@ 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' } }, - serviceWorker: true, - theme: 'vue', + head: [ + ['link', { rel: 'icon', href: `/logo.png` }], + [ + 'meta', + { name: 'wwads-cn-verify', content: '7e7757b1e12abcb736ab9a754ffb617a' } + ], + [ + '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' + } + ] + ], + // theme: '@vuepress/vue', + plugins: [ + [ + '@vuepress/pwa', + { + serviceWorker: true, + updatePopup: true + } + ] + ], themeConfig: { - algolia: { - apiKey: 'f854bb46d3de7eeb921a3b9173bd0d4c', - indexName: 'vue-router', + algolia: ctx.isProd + ? { + appId: 'LI3RW4C4QI', + apiKey: '08e7ef7cd3969442874f0dee9dec34be', + indexName: 'vue-router' + } + : null, + carbonAds: { + carbon: 'CEBICK3I', + custom: 'CEBICK3M', + placement: 'routervuejsorg' }, repo: 'vuejs/vue-router', docsDir: 'docs', + smoothScroll: true, locales: { '/': { label: 'English', @@ -54,6 +97,10 @@ module.exports = { text: 'API Reference', link: '/api/' }, + { + text: 'v3.x', + items: [{ text: 'v4.x', link: 'https://next.router.vuejs.org' }] + }, { text: 'Release Notes', link: 'https://github.com/vuejs/vue-router/releases' @@ -61,7 +108,6 @@ module.exports = { ], sidebar: [ '/installation.md', - '/', { title: 'Essentials', collapsable: false, @@ -86,7 +132,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' ] } ] @@ -104,6 +151,10 @@ module.exports = { text: 'API 参考', link: '/zh/api/' }, + { + text: 'v3.x', + items: [{ text: 'v4.x', link: 'https://next.router.vuejs.org/zh/' }] + }, { text: '更新记录', link: 'https://github.com/vuejs/vue-router/releases' @@ -111,7 +162,6 @@ module.exports = { ], sidebar: [ '/zh/installation.md', - '/zh/', { title: '基础', collapsable: false, @@ -136,7 +186,8 @@ module.exports = { '/zh/guide/advanced/transitions.md', '/zh/guide/advanced/data-fetching.md', '/zh/guide/advanced/scroll-behavior.md', - '/zh/guide/advanced/lazy-loading.md' + '/zh/guide/advanced/lazy-loading.md', + '/zh/guide/advanced/navigation-failures.md' ] } ] @@ -154,6 +205,10 @@ module.exports = { text: 'API リファレンス', link: '/ja/api/' }, + { + text: 'v3.x', + items: [{ text: 'v4.x', link: 'https://next.router.vuejs.org' }] + }, { text: 'リリースノート', link: 'https://github.com/vuejs/vue-router/releases' @@ -161,7 +216,6 @@ module.exports = { ], sidebar: [ '/ja/installation.md', - '/ja/', { title: '基本的な使い方', collapsable: false, @@ -186,7 +240,8 @@ module.exports = { '/ja/guide/advanced/transitions.md', '/ja/guide/advanced/data-fetching.md', '/ja/guide/advanced/scroll-behavior.md', - '/ja/guide/advanced/lazy-loading.md' + '/ja/guide/advanced/lazy-loading.md', + '/ja/guide/advanced/navigation-failures.md' ] } ] @@ -204,6 +259,10 @@ module.exports = { text: 'Справочник API', link: '/ru/api/' }, + { + text: 'v3.x', + items: [{ text: 'v4.x', link: 'https://next.router.vuejs.org' }] + }, { text: 'История изменений', link: 'https://github.com/vuejs/vue-router/releases' @@ -211,7 +270,6 @@ module.exports = { ], sidebar: [ '/ru/installation.md', - '/ru/', { title: 'Основы', collapsable: false, @@ -236,7 +294,8 @@ module.exports = { '/ru/guide/advanced/transitions.md', '/ru/guide/advanced/data-fetching.md', '/ru/guide/advanced/scroll-behavior.md', - '/ru/guide/advanced/lazy-loading.md' + '/ru/guide/advanced/lazy-loading.md', + '/ru/guide/advanced/navigation-failures.md' ] } ] @@ -254,6 +313,10 @@ module.exports = { text: 'API 레퍼런스', link: '/kr/api/' }, + { + text: 'v3.x', + items: [{ text: 'v4.x', link: 'https://next.router.vuejs.org' }] + }, { text: '릴리즈 노트', link: 'https://github.com/vuejs/vue-router/releases' @@ -261,7 +324,6 @@ module.exports = { ], sidebar: [ '/kr/installation.md', - '/kr/', { title: '기본 사용법', collapsable: false, @@ -292,55 +354,58 @@ 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: 'v3.x', + items: [{ text: 'v4.x', link: 'https://next.router.vuejs.org' }] + }, + { + text: 'Notes de version', + link: 'https://github.com/vuejs/vue-router/releases' + } + ], + sidebar: [ + '/fr/installation.md', + { + 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/.vuepress/public/icons/android-chrome-192x192.png b/docs/.vuepress/public/icons/android-chrome-192x192.png new file mode 100644 index 000000000..b02aa64d9 Binary files /dev/null and b/docs/.vuepress/public/icons/android-chrome-192x192.png differ diff --git a/docs/.vuepress/public/icons/android-chrome-512x512.png b/docs/.vuepress/public/icons/android-chrome-512x512.png new file mode 100644 index 000000000..06088b011 Binary files /dev/null and b/docs/.vuepress/public/icons/android-chrome-512x512.png differ diff --git a/docs/.vuepress/public/icons/apple-touch-icon-120x120.png b/docs/.vuepress/public/icons/apple-touch-icon-120x120.png new file mode 100644 index 000000000..1427cf627 Binary files /dev/null and b/docs/.vuepress/public/icons/apple-touch-icon-120x120.png differ diff --git a/docs/.vuepress/public/icons/apple-touch-icon-152x152.png b/docs/.vuepress/public/icons/apple-touch-icon-152x152.png new file mode 100644 index 000000000..f24d454a2 Binary files /dev/null and b/docs/.vuepress/public/icons/apple-touch-icon-152x152.png differ diff --git a/docs/.vuepress/public/icons/apple-touch-icon-180x180.png b/docs/.vuepress/public/icons/apple-touch-icon-180x180.png new file mode 100644 index 000000000..404e192a9 Binary files /dev/null and b/docs/.vuepress/public/icons/apple-touch-icon-180x180.png differ diff --git a/docs/.vuepress/public/icons/apple-touch-icon-60x60.png b/docs/.vuepress/public/icons/apple-touch-icon-60x60.png new file mode 100644 index 000000000..cf10a5602 Binary files /dev/null and b/docs/.vuepress/public/icons/apple-touch-icon-60x60.png differ diff --git a/docs/.vuepress/public/icons/apple-touch-icon-76x76.png b/docs/.vuepress/public/icons/apple-touch-icon-76x76.png new file mode 100644 index 000000000..c500769e3 Binary files /dev/null and b/docs/.vuepress/public/icons/apple-touch-icon-76x76.png differ diff --git a/docs/.vuepress/public/icons/apple-touch-icon.png b/docs/.vuepress/public/icons/apple-touch-icon.png new file mode 100644 index 000000000..03c0c5d5e Binary files /dev/null and b/docs/.vuepress/public/icons/apple-touch-icon.png differ diff --git a/docs/.vuepress/public/icons/favicon-16x16.png b/docs/.vuepress/public/icons/favicon-16x16.png new file mode 100644 index 000000000..42af00963 Binary files /dev/null and b/docs/.vuepress/public/icons/favicon-16x16.png differ diff --git a/docs/.vuepress/public/icons/favicon-32x32.png b/docs/.vuepress/public/icons/favicon-32x32.png new file mode 100644 index 000000000..46ca04dee Binary files /dev/null and b/docs/.vuepress/public/icons/favicon-32x32.png differ diff --git a/docs/.vuepress/public/icons/msapplication-icon-144x144.png b/docs/.vuepress/public/icons/msapplication-icon-144x144.png new file mode 100644 index 000000000..7808237a1 Binary files /dev/null and b/docs/.vuepress/public/icons/msapplication-icon-144x144.png differ diff --git a/docs/.vuepress/public/icons/mstile-150x150.png b/docs/.vuepress/public/icons/mstile-150x150.png new file mode 100644 index 000000000..3b37a43ae Binary files /dev/null and b/docs/.vuepress/public/icons/mstile-150x150.png differ diff --git a/docs/.vuepress/public/icons/safari-pinned-tab.svg b/docs/.vuepress/public/icons/safari-pinned-tab.svg new file mode 100644 index 000000000..732afd8eb --- /dev/null +++ b/docs/.vuepress/public/icons/safari-pinned-tab.svg @@ -0,0 +1,149 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/docs/.vuepress/public/images/vueschool/vs-close.svg b/docs/.vuepress/public/images/vueschool/vs-close.svg new file mode 100644 index 000000000..0e2f31fcd --- /dev/null +++ b/docs/.vuepress/public/images/vueschool/vs-close.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/docs/.vuepress/public/images/vueschool/vs-fw-bg-small.svg b/docs/.vuepress/public/images/vueschool/vs-fw-bg-small.svg new file mode 100644 index 000000000..a914f4005 --- /dev/null +++ b/docs/.vuepress/public/images/vueschool/vs-fw-bg-small.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/.vuepress/public/images/vueschool/vs-fw-bg.svg b/docs/.vuepress/public/images/vueschool/vs-fw-bg.svg new file mode 100644 index 000000000..8c5222062 --- /dev/null +++ b/docs/.vuepress/public/images/vueschool/vs-fw-bg.svg @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/.vuepress/public/images/vueschool/vs-iso.svg b/docs/.vuepress/public/images/vueschool/vs-iso.svg new file mode 100644 index 000000000..a95d92607 --- /dev/null +++ b/docs/.vuepress/public/images/vueschool/vs-iso.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/.vuepress/public/images/vueschool/vs-logo.svg b/docs/.vuepress/public/images/vueschool/vs-logo.svg new file mode 100644 index 000000000..b44c00458 --- /dev/null +++ b/docs/.vuepress/public/images/vueschool/vs-logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/.vuepress/public/logo.png b/docs/.vuepress/public/logo.png new file mode 100644 index 000000000..60e17006a Binary files /dev/null and b/docs/.vuepress/public/logo.png differ diff --git a/docs/.vuepress/public/sponsors/fincliplogo_black_svg.svg b/docs/.vuepress/public/sponsors/fincliplogo_black_svg.svg new file mode 100644 index 000000000..848f93d56 --- /dev/null +++ b/docs/.vuepress/public/sponsors/fincliplogo_black_svg.svg @@ -0,0 +1 @@ +五行代码,让你的APP拥有小程序运行能力 \ No newline at end of file diff --git a/docs/.vuepress/public/sponsors/fincliplogo_white_svg.svg b/docs/.vuepress/public/sponsors/fincliplogo_white_svg.svg new file mode 100644 index 000000000..3254e5384 --- /dev/null +++ b/docs/.vuepress/public/sponsors/fincliplogo_white_svg.svg @@ -0,0 +1 @@ +五行代码,让你的APP拥有小程序运行能力 \ No newline at end of file diff --git a/docs/.vuepress/public/sponsors/passionate-people-dark.png b/docs/.vuepress/public/sponsors/passionate-people-dark.png new file mode 100644 index 000000000..028baf65b Binary files /dev/null and b/docs/.vuepress/public/sponsors/passionate-people-dark.png differ diff --git a/docs/.vuepress/public/sponsors/passionate-people-light.png b/docs/.vuepress/public/sponsors/passionate-people-light.png new file mode 100644 index 000000000..61ac71af2 Binary files /dev/null and b/docs/.vuepress/public/sponsors/passionate-people-light.png differ diff --git a/docs/.vuepress/public/sponsors/vuejobs.png b/docs/.vuepress/public/sponsors/vuejobs.png new file mode 100644 index 000000000..50b7b7474 Binary files /dev/null and b/docs/.vuepress/public/sponsors/vuejobs.png differ diff --git a/docs/.vuepress/public/sponsors/vuetify-logo-dark-text.svg b/docs/.vuepress/public/sponsors/vuetify-logo-dark-text.svg new file mode 100644 index 000000000..6f4ff0708 --- /dev/null +++ b/docs/.vuepress/public/sponsors/vuetify-logo-dark-text.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/.vuepress/public/sponsors/vuetify-logo-light-text.svg b/docs/.vuepress/public/sponsors/vuetify-logo-light-text.svg new file mode 100644 index 000000000..33f442df8 --- /dev/null +++ b/docs/.vuepress/public/sponsors/vuetify-logo-light-text.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/.vuepress/style.styl b/docs/.vuepress/style.styl deleted file mode 100644 index de6fb8800..000000000 --- a/docs/.vuepress/style.styl +++ /dev/null @@ -1,14 +0,0 @@ -.bit-sponsor - font-weight 600 - background-color #f3f6f8 - padding 0.6em 1.2em - border-radius 8px - display inline-block - margin 1em 0 !important - a - color #999 - img - height 40px - margin-left 15px - img, span - vertical-align middle diff --git a/docs/.vuepress/styles/index.styl b/docs/.vuepress/styles/index.styl new file mode 100644 index 000000000..a8f9a8a68 --- /dev/null +++ b/docs/.vuepress/styles/index.styl @@ -0,0 +1,54 @@ +.bit-sponsor + font-weight 600 + background-color #f3f6f8 + padding 0.6em 1.2em + border-radius 8px + display inline-block + margin 1em 0 !important + a + color #999 + img + height 40px + margin-left 15px + img, span + vertical-align middle + + +.vueschool + background-color #e7ecf3 + padding 1em 1.25em + border-radius 2px + color #486491 + position relative + display flex + a + color #486491 !important + position relative + padding-left 36px + &:before + content '' + position absolute + display block + width 30px + height 30px + top calc(50% - 15px); + left -4px + border-radius 50% + background-color #73abfe + &:after + content '' + position absolute + display block + width 0 + height 0 + top calc(50% - 5px) + left 8px + border-top 5px solid transparent + border-bottom 5px solid transparent + border-left 8px solid #fff + +@media (max-width: 719px) { + .vueschool { + display: inline-flex; + } +} diff --git a/docs/.vuepress/theme/Layout.vue b/docs/.vuepress/theme/Layout.vue new file mode 100644 index 000000000..a27d64a62 --- /dev/null +++ b/docs/.vuepress/theme/Layout.vue @@ -0,0 +1,160 @@ + + + + + + + diff --git a/docs/.vuepress/theme/components/BuySellAds.vue b/docs/.vuepress/theme/components/BuySellAds.vue new file mode 100644 index 000000000..17df4bd16 --- /dev/null +++ b/docs/.vuepress/theme/components/BuySellAds.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/docs/.vuepress/theme/components/CarbonAds.vue b/docs/.vuepress/theme/components/CarbonAds.vue new file mode 100644 index 000000000..8864e44b1 --- /dev/null +++ b/docs/.vuepress/theme/components/CarbonAds.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/docs/.vuepress/theme/components/VueSchool/BannerTop.vue b/docs/.vuepress/theme/components/VueSchool/BannerTop.vue new file mode 100644 index 000000000..02a3b756a --- /dev/null +++ b/docs/.vuepress/theme/components/VueSchool/BannerTop.vue @@ -0,0 +1,244 @@ + + + diff --git a/docs/.vuepress/theme/index.js b/docs/.vuepress/theme/index.js new file mode 100644 index 000000000..b91b8a576 --- /dev/null +++ b/docs/.vuepress/theme/index.js @@ -0,0 +1,3 @@ +module.exports = { + extend: '@vuepress/theme-default' +} diff --git a/docs/README.md b/docs/README.md index 0fb596fdf..69f6215fb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,10 @@ -# Introduction - -:::tip VERSION NOTE -For TypeScript users, `vue-router@3.0+` requires `vue@2.5+`, and vice versa. -::: +--- +home: true +heroImage: /logo.png +actionText: Get Started → +actionLink: /installation.html +footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Morote +--- Vue Router is the official router for [Vue.js](http://vuejs.org). It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze. Features include: @@ -16,3 +18,5 @@ Vue Router is the official router for [Vue.js](http://vuejs.org). It deeply inte - Customizable Scroll Behavior [Get started](./guide/) or play with the [examples](https://github.com/vuejs/vue-router/tree/dev/examples) (see [`README.md`](https://github.com/vuejs/vue-router/) to run them). + + diff --git a/docs/api/README.md b/docs/api/README.md index 71aada72b..5c9dacc60 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -23,6 +23,7 @@ sidebar: auto ```html
  • + ``` + +### exact-path-active-class + +- type: `string` +- default: `"router-link-exact-path-active"` + + Configure the active CSS class applied when the link is active with exact path match. Note the default value can also be configured globally via the `linkExactPathActiveClass` router constructor option. + ### event - type: `string | Array` @@ -163,6 +186,13 @@ If you add a `target="_blank"` to your `a` element, you must omit the `@click="n Configure the active CSS class applied when the link is active with exact match. Note the default value can also be configured globally via the `linkExactActiveClass` router constructor option. +### aria-current-value + +- type: `'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false'` +- default: `"page"` + + 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. + ## `` The `` component is a functional component that renders the matched component for the given path. Components rendered in `` can also contain their own ``, which will render components for nested paths. @@ -261,11 +291,11 @@ Since it's just a component, it works with `` and ``. Wh Signature: - ``` + ```ts type PositionDescriptor = { x: number, y: number } | { selector: string } | - ?{} + void type scrollBehaviorHandler = ( to: Route, @@ -312,6 +342,26 @@ Since it's just a component, it works with `` and ``. Wh The current route represented as a [Route Object](#the-route-object). +### router.START_LOCATION (3.5.0+) + +- type: `Route` + + Initial route location represented as a [Route Object](#the-route-object) where the router starts at. Can be used in navigation guards to differentiate the initial navigation. + + ```js + import VueRouter from 'vue-router' + + const router = new VueRouter({ + // ... + }) + + router.beforeEach((to, from) => { + if (from === VueRouter.START_LOCATION) { + // initial navigation + } + }) + ``` + ## Router Instance Methods ### router.beforeEach @@ -362,6 +412,8 @@ router.forward() Programmatically navigate to a new URL. See [Programmatic Navigation](../guide/essentials/navigation.md) for more details. +These functions can only be called after installing the Router plugin and passing it to the root Vue instance as shown in the [Getting Started](../guide/README.md). + ### router.getMatchedComponents Signature: @@ -391,14 +443,52 @@ Reverse URL resolving. Given location in form same as used in ``. ### router.addRoutes +_DEPRECATED_: use [`router.addRoute()`](#router-addroute) instead. + Signature: -```js +```ts router.addRoutes(routes: Array) ``` Dynamically add more routes to the router. The argument must be an Array using the same route config format with the `routes` constructor option. +### router.addRoute + +> New in 3.5.0 + +Add a new route to the router. If the route has a `name` and there is already an existing one with the same one, it overwrites it. + +Signature: + +```ts +addRoute(route: RouteConfig): () => void +``` + +### router.addRoute + +> New in 3.5.0 + +Add a new route record as the child of an existing route. If the route has a `name` and there is already an existing one with the same one, it overwrites it. + +Signature: + +```ts +addRoute(parentName: string, route: RouteConfig): () => void +``` + +### router.getRoutes + +> New in 3.5.0 + +Get the list of all the active route records. **Note only documented properties are considered Public API**, avoid using any other property e.g. `regex` as it doesn't exist on Vue Router 4. + +Signature: + +```ts +getRoutes(): RouteRecord[] +``` + ### router.onReady Signature: @@ -481,6 +571,12 @@ The route object can be found in multiple places: An object that contains key/value pairs of the query string. For example, for a path `/foo?user=1`, we get `$route.query.user == 1`. If there is no query the value will be an empty object. +- **\$route.meta** + + - type: `Object` + + An object that contains key/value pairs of the route meta object. If there are no meta properties the value will be an empty object. + - **\$route.hash** - type: `string` diff --git a/docs/fr/README.md b/docs/fr/README.md index deb4edcf7..b46196db2 100644 --- a/docs/fr/README.md +++ b/docs/fr/README.md @@ -1,8 +1,10 @@ -# Introduction - -:::tip VERSION NOTE -Pour les utilisateurs de TypeScript, `vue-router@3.0+` requière `vue@2.5+`, et vice versa. -::: +--- +home: true +heroImage: /logo.png +actionText: Get Started → +actionLink: /fr/installation.html +footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Morote +--- Vue Router est le router officiel pour [Vue.js](http://vuejs.org). Il s'intègre aisément avec Vue.js pour faire des applications mono page avec Vue.js. Fonctionnalités incluses: @@ -16,3 +18,5 @@ Vue Router est le router officiel pour [Vue.js](http://vuejs.org). Il s'intègre - Gestion du scroll [Pour commencer](./guide/) ou jouer avec les [exemples](https://github.com/vuejs/vue-router/tree/dev/examples) (voir [`README.md`](https://github.com/vuejs/vue-router/) pour les faire fonctionner). + + diff --git a/docs/fr/api/README.md b/docs/fr/api/README.md index a259f9aec..0006b0dbc 100644 --- a/docs/fr/api/README.md +++ b/docs/fr/api/README.md @@ -14,7 +14,31 @@ sidebar: auto - Dans le mode historique HTML5, `router-link` interceptera l'évènement du clic, comme ça le navigateur n'essaiera pas de rafraichir la page. -- En utilisant l'option `base` dans le mode historique HTML5, vous n'avez pas besoin +- En utilisant l'option `base` dans le mode historique HTML5, vous n'avez pas besoin de l'inclure dans la prop `to` + +### `v-slot` API (3.1.0+) + +`router-link` est fortement personnalisable via une [slot avec portée](https://fr.vuejs.org/v2/guide/components-slots.html#Slots-avec-portee). C'est une API qui cible principalement les créateurs de bibliothèque, mais elle peut aussi servir pour les développeurs, le plus souvent dans des composants personnalisés tel qu'un _NavLink_ et autre. + +**Lors de l'utilisation `v-slot` API, il faut obligatoirement passer un composant enfant unique au `router-link`**. Si vous ne le faites pas, `router-link` va entourer ses enfants avec une balise `span`. + +```html + + {{ route.fullPath }} + +``` + +- `href`: URL résolue. Ce serait l'attribut `href` d'une balise `a` +- `route`: localisation normalisée et résolue +- `navigate`: fonction pour lancer la navigation. **En cas de nécessité il va automatiquement empêcher les événements**, de la même façon que `router-link` +- `isActive`: `true` si [active class](#active-class) devrait être appliqué. Permet d'appliquer une classe arbitraire +- `isExactActive`: `true` si [exact active class](#exact-active-class) devrait être appliqué. Permet d'appliquer une classe arbitraire ### Appliquer la classe active à l'élément extérieur @@ -221,11 +245,11 @@ Le composant `` est un composant fonctionnel qui fait le rendu du c Signature : - ``` + ```ts type PositionDescriptor = { x: number, y: number } | { selector: string } | - ?{} + void type scrollBehaviorHandler = ( to: Route, diff --git a/docs/fr/guide/README.md b/docs/fr/guide/README.md index 35756ef40..c58572205 100644 --- a/docs/fr/guide/README.md +++ b/docs/fr/guide/README.md @@ -10,8 +10,8 @@ Créer une application monopage avec Vue + Vue Router est vraiment simple. Avec ## HTML ```html - - + +

    Bonjour l'application !

    diff --git a/docs/fr/guide/advanced/lazy-loading.md b/docs/fr/guide/advanced/lazy-loading.md index 9324281fa..ae138cf72 100644 --- a/docs/fr/guide/advanced/lazy-loading.md +++ b/docs/fr/guide/advanced/lazy-loading.md @@ -6,13 +6,16 @@ En combinant la [fonctionnalité de composant asynchrone](https://fr.vuejs.org/v Premièrement, un composant asynchrone peut définir une fonction fabrique qui retourne une Promesse (qui devrait résoudre le composant lui-même) : -``` js -const Foo = () => Promise.resolve({ /* définition du composant */ }) +```js +const Foo = () => + Promise.resolve({ + /* définition du composant */ + }) ``` Deuxièmement, avec webpack 2, nous pouvons utiliser la syntaxe d'[import dynamique](https://github.com/tc39/proposal-dynamic-import) pour indiquer un point de scission de code : -``` js +```js import('./Foo.vue') // returns a Promise ``` @@ -22,25 +25,23 @@ si vous utilisez Babel, vous aurez besoin d'ajouter le plugin [syntax-dynamic-im En combinant les deux, on définit un composant asynchrone qui sera automatiquement scindé par webpack : -``` js +```js const Foo = () => import('./Foo.vue') ``` Rien n'a besoin d'être modifié dans la configuration de la route, utilisez `Foo` comme d'habitude. -``` js +```js const router = new VueRouter({ - routes: [ - { path: '/foo', component: Foo } - ] + routes: [{ path: '/foo', component: Foo }] }) ``` ## Grouper des composants dans le même fragment -Parfois on aimerait grouper tous les composants imbriqués sous la même route, dans un seul et même fragment asynchrone. Pour arriver à cela, nous avons besoin d'utiliser les [fragments nommés](https://webpack.js.org/guides/code-splitting-async/#chunk-names) en donnant un nom au fragment en utilisant une syntaxe de commentaire spéciale (requires webpack > 2.4) : +Parfois on aimerait grouper tous les composants imbriqués sous la même route, dans un seul et même fragment asynchrone. Pour arriver à cela, nous avons besoin d'utiliser les [fragments nommés](https://webpack.js.org/api/module-methods/#magic-comments) en donnant un nom au fragment en utilisant une syntaxe de commentaire spéciale (requires webpack > 2.4) : -``` js +```js const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') diff --git a/docs/fr/guide/advanced/navigation-guards.md b/docs/fr/guide/advanced/navigation-guards.md index 9bcb01cb9..4ee3d4fad 100644 --- a/docs/fr/guide/advanced/navigation-guards.md +++ b/docs/fr/guide/advanced/navigation-guards.md @@ -102,7 +102,7 @@ const Foo = { } ``` -L'interception `beforeRouteEnter` **n'**a **PAS** accès à `this`, car l'interception est appelée avant que la navigation soit confirmée, et le nouveau composant entrant n'a même pas encore été créé. +L'interception `beforeRouteEnter` **n'a PAS** accès à `this`, car l'interception est appelée avant que la navigation soit confirmée, et le nouveau composant entrant n'a même pas encore été créé. Cependant, vous pouvez accéder à l'instance en passant dans la fonction de rappel `next`. Cette fonction de rappel va être appelée quand la navigation sera confirmée, et l'instance du composant sera passée à la fonction de rappel en tant qu'argument : diff --git a/docs/fr/guide/essentials/dynamic-matching.md b/docs/fr/guide/essentials/dynamic-matching.md index 82ec94354..76d1fbf5a 100644 --- a/docs/fr/guide/essentials/dynamic-matching.md +++ b/docs/fr/guide/essentials/dynamic-matching.md @@ -29,9 +29,9 @@ Vous pouvez regarder un exemple en ligne [ici](https://jsfiddle.net/yyx990803/4x Vous pouvez avoir plusieurs segments dynamiques pour une même route, et ils seront associés aux champs associés dans `$route.params`. Des exemples : -| motif | chemin concordant | $route.params | -|---------|------|--------| -| /utilisateur/:username | /utilisateur/evan | `{ username: 'evan' }` | +| motif | chemin concordant | $route.params | +| -------------------------------------- | ---------------------------- | ------------------------------------ | +| /utilisateur/:username | /utilisateur/evan | `{ username: 'evan' }` | | /utilisateur/:username/billet/:post_id | /utilisateur/evan/billet/123 | `{ username: 'evan', post_id: 123 }` | En plus de `$route.params`, l'objet `$route` expose également d'autres informations utiles comme la `$route.query` (s'il y a une requête dans l'URL), `$route.hash`, etc. Vous pouvez accéder à tous les détails de cela dans la [référence de l'API](../../api/#the-route-object). diff --git a/docs/fr/guide/essentials/history-mode.md b/docs/fr/guide/essentials/history-mode.md index 2cd21c043..8337db43a 100644 --- a/docs/fr/guide/essentials/history-mode.md +++ b/docs/fr/guide/essentials/history-mode.md @@ -4,7 +4,7 @@ Le mode par défaut de `vue-router` est le _mode hash_. Il utilise la partie has Pour nous passer du hash, nous pouvons utiliser le **mode historique** qui utilisera l'API `history.pushState` afin de permettre une navigation sans rechargement de page : -``` js +```js const router = new VueRouter({ mode: 'history', routes: [...] @@ -22,6 +22,9 @@ Ne vous inquiétez pas. Pour résoudre ce problème, il vous suffit d'ajouter un ### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / @@ -32,7 +35,7 @@ Ne vous inquiétez pas. Pour résoudre ce problème, il vous suffit d'ajouter un ``` -Instead of `mod_rewrite`, you could also use [`FallbackResource`](https://httpd.apache.org/docs/2.2/mod/mod_dir.html#fallbackresource). +Au lieu de `mod_rewrite`, vous pouvez également utiliser [`FallbackResource`](https://httpd.apache.org/docs/2.2/mod/mod_dir.html#fallbackresource). ### nginx @@ -49,21 +52,23 @@ const http = require('http') const fs = require('fs') const httpPort = 80 -http.createServer((req, res) => { - fs.readFile('index.htm', 'utf-8', (err, content) => { - if (err) { -      console.log(`Impossible d'ouvrir le fichier "index.htm"`) - } +http + .createServer((req, res) => { + fs.readFile('index.html', 'utf-8', (err, content) => { + if (err) { + console.log(`Impossible d'ouvrir le fichier "index.html"`) + } - res.writeHead(200, { - 'Content-Type': 'text/html; charset=utf-8' - }) + res.writeHead(200, { + 'Content-Type': 'text/html; charset=utf-8' + }) - res.end(content) + res.end(content) + }) + }) + .listen(httpPort, () => { + console.log('Le serveur écoute à : http://localhost:%s', httpPort) }) -}).listen(httpPort, () => { - console.log('Le serveur écoute à : http://localhost:%s', httpPort) -}) ``` ### Node.js avec Express @@ -126,12 +131,10 @@ Ajouter ceci à votre fichier `firebase.json` : Il y a une limitation a tout ceci. Votre serveur ne renverra plus les erreurs 404 des chemins qui ne sont pas trouvés puisqu'il va servir à présent le fichier `index.html`. Pour contourner ce problème, vous pouvez implémenter une route concordant avec toutes les adresses en 404 dans votre application Vue : -``` js +```js const router = new VueRouter({ mode: 'history', - routes: [ - { path: '*', component: NotFoundComponent } - ] + routes: [{ path: '*', component: NotFoundComponent }] }) ``` diff --git a/docs/fr/guide/essentials/navigation.md b/docs/fr/guide/essentials/navigation.md index 76367579c..dbb747621 100644 --- a/docs/fr/guide/essentials/navigation.md +++ b/docs/fr/guide/essentials/navigation.md @@ -14,8 +14,8 @@ Pour naviguer vers un URL différent, utilisez `router.push`. Cette méthode ajo Cette méthode est appelée en interne quand vous cliquez sur ``, donc cliquer sur `` est équivalent à appeler `router.push(...)`. -| Déclarative | Programmatique | -|-------------|--------------| +| Déclarative | Programmatique | +| ------------------------- | ------------------ | | `` | `router.push(...)` | L'argument peut être une chaine de caractère représentant un chemin, ou un objet de description de destination. Des exemples : @@ -54,8 +54,8 @@ Dans la version 2.2.0+, vous pouvez optionnellement fournir les fonctions de rap Il agit comme `router.push`. La seule différence est que la navigation se fait sans ajouter de nouvelle entrée dans la pile de l'historique. Comme son nom l'indique, il remplace l'entrée courante. -| Déclarative | Programmatique | -|-------------|--------------| +| Déclarative | Programmatique | +| --------------------------------- | --------------------- | | `` | `router.replace(...)` | diff --git a/docs/fr/guide/essentials/nested-routes.md b/docs/fr/guide/essentials/nested-routes.md index be0fc9cfb..fa513fb64 100644 --- a/docs/fr/guide/essentials/nested-routes.md +++ b/docs/fr/guide/essentials/nested-routes.md @@ -58,13 +58,13 @@ const router = new VueRouter({ { // `UserProfile` va être rendu à l'intérieur du `` de `User` // quand `/utilisateur/:id/profil` concorde - path: 'profile', + path: 'profil', component: UserProfile }, { // `UserPosts` va être rendu à l'intérieur du `` de `User` // quand `/utilisateur/:id/billets` concorde - path: 'posts', + path: 'billets', component: UserPosts } ] @@ -85,7 +85,7 @@ const router = new VueRouter({ { path: '/utilisateur/:id', component: User, children: [ - // `UserProfile` va être rendu à l'intérieur du `` de `User` + // `UserHome` va être rendu à l'intérieur du `` de `User` // quand `/utilisateur/:id` concorde { path: '', component: UserHome }, diff --git a/docs/fr/guide/essentials/redirect-and-alias.md b/docs/fr/guide/essentials/redirect-and-alias.md index eb9ef163a..863f2c940 100644 --- a/docs/fr/guide/essentials/redirect-and-alias.md +++ b/docs/fr/guide/essentials/redirect-and-alias.md @@ -43,7 +43,7 @@ Pour d'autres utilisations avancées, jetez un œil à cet [exemple](https://git Une redirection signifie que si l'utilisateur visite `/a`, l'URL va être remplacé par `/b` et concordé avec `/b`. Mais qu'est-ce qu'un alias ? -** Un alias de `/a` en tant que `/b` signifie que lorsque l'utilisateur va visiter `/b`, l'URL va rester `/b`, mais la concordance va se faire comme si l'utilisateur visitait `/a`.** +**Un alias de `/a` en tant que `/b` signifie que lorsque l'utilisateur va visiter `/b`, l'URL va rester `/b`, mais la concordance va se faire comme si l'utilisateur visitait `/a`.** La phase du dessus peut être exprimée dans la configuration de la route de la manière suivante : diff --git a/docs/fr/installation.md b/docs/fr/installation.md index 63a4fdccb..0fc456b1f 100644 --- a/docs/fr/installation.md +++ b/docs/fr/installation.md @@ -2,10 +2,10 @@ ## Téléchargement direct / CDN -[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js) +[https://unpkg.com/vue-router@3/dist/vue-router.js](https://unpkg.com/vue-router@3/dist/vue-router.js) -[Unpkg.com](https://unpkg.com) fournit des liens CDN basés sur npm. Le lien ci-dessus pointera toujours vers la dernière version sur npm. Vous pouvez aussi utiliser un tag ou une version spécifique via un URL comme `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js`. +[Unpkg.com](https://unpkg.com) fournit des liens CDN basés sur npm. Le lien ci-dessus pointera toujours vers la dernière version sur npm. Vous pouvez aussi utiliser un tag ou une version spécifique via un URL comme `https://unpkg.com/vue-router@3.0.0/dist/vue-router.js`. Incluez `vue-router` après Vue et l'installation sera automatique : diff --git a/docs/guide/README.md b/docs/guide/README.md index 00c537027..017faa45f 100644 --- a/docs/guide/README.md +++ b/docs/guide/README.md @@ -1,18 +1,22 @@ # Getting Started ::: tip Note +You are reading the documentation of Vue Router 3 **for Vue 2**. If you are working with Vue 3, use the [Vue Router 4 documentation](https://next.router.vuejs.org) instead. + We will be using [ES2015](https://github.com/lukehoban/es6features) in the code samples in the guide. Also, all examples will be using the full version of Vue to make on-the-fly template compilation possible. See more details [here](https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only). ::: -Creating a Single-page Application with Vue + Vue Router is dead simple. With Vue.js, we are already composing our application with components. When adding Vue Router to the mix, all we need to do is map our components to the routes and let Vue Router know where to render them. Here's a basic example: + + +Creating a Single-page Application with Vue + Vue Router feels natural: with Vue.js, we are already composing our application with components. When adding Vue Router to the mix, all we need to do is map our components to the routes and let Vue Router know where to render them. Here's a basic example: ## HTML ```html - - + +

    Hello App!

    diff --git a/docs/guide/advanced/data-fetching.md b/docs/guide/advanced/data-fetching.md index b8559c291..bde314f66 100644 --- a/docs/guide/advanced/data-fetching.md +++ b/docs/guide/advanced/data-fetching.md @@ -55,8 +55,11 @@ export default { fetchData () { this.error = this.post = null this.loading = true + const fetchedId = this.$route.params.id // replace `getPost` with your data fetching util / API wrapper - getPost(this.$route.params.id, (err, post) => { + getPost(fetchedId, (err, post) => { + // make sure this request is the last one we did, discard otherwise + if (this.$route.params.id !== fetchedId) return this.loading = false if (err) { this.error = err.toString() diff --git a/docs/guide/advanced/lazy-loading.md b/docs/guide/advanced/lazy-loading.md index 7735a471b..db059d8a0 100644 --- a/docs/guide/advanced/lazy-loading.md +++ b/docs/guide/advanced/lazy-loading.md @@ -1,18 +1,23 @@ # Lazy Loading Routes + + When building apps with a bundler, the JavaScript bundle can become quite large, and thus affect the page load time. It would be more efficient if we can split each route's components into a separate chunk, and only load them when the route is visited. Combining Vue's [async component feature](https://vuejs.org/guide/components.html#Async-Components) and webpack's [code splitting feature](https://webpack.js.org/guides/code-splitting-async/), it's trivially easy to lazy-load route components. First, an async component can be defined as a factory function that returns a Promise (which should resolve to the component itself): -``` js -const Foo = () => Promise.resolve({ /* component definition */ }) +```js +const Foo = () => + Promise.resolve({ + /* component definition */ + }) ``` Second, in webpack 2, we can use the [dynamic import](https://github.com/tc39/proposal-dynamic-import) syntax to indicate a code-split point: -``` js +```js import('./Foo.vue') // returns a Promise ``` @@ -22,25 +27,23 @@ if you are using Babel, you will need to add the [syntax-dynamic-import](https:/ Combining the two, this is how to define an async component that will be automatically code-split by webpack: -``` js +```js const Foo = () => import('./Foo.vue') ``` Nothing needs to change in the route config, just use `Foo` as usual: -``` js +```js const router = new VueRouter({ - routes: [ - { path: '/foo', component: Foo } - ] + routes: [{ path: '/foo', component: Foo }] }) ``` ## Grouping Components in the Same Chunk -Sometimes we may want to group all the components nested under the same route into the same async chunk. To achieve that we need to use [named chunks](https://webpack.js.org/guides/code-splitting-async/#chunk-names) by providing a chunk name using a special comment syntax (requires webpack > 2.4): +Sometimes we may want to group all the components nested under the same route into the same async chunk. To achieve that we need to use [named chunks](https://webpack.js.org/api/module-methods/#magic-comments) by providing a chunk name using a special comment syntax (requires webpack > 2.4): -``` js +```js const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') diff --git a/docs/guide/advanced/meta.md b/docs/guide/advanced/meta.md index 79845b28d..8cbd97449 100644 --- a/docs/guide/advanced/meta.md +++ b/docs/guide/advanced/meta.md @@ -1,8 +1,8 @@ # Route Meta Fields -You can include a `meta` field when defining a route: +Sometimes, you might want to attach arbitrary information to routes like transition names, who can access the route, etc. This can be achieved through the `meta` property which accepts an object of properties and can be accessed on the route location and navigation guards. You can define `meta` properties like this: -``` js +```js const router = new VueRouter({ routes: [ { @@ -31,7 +31,7 @@ All route records matched by a route are exposed on the `$route` object (and als An example use case is checking for a meta field in the global navigation guard: -``` js +```js router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { // this route requires auth, check if logged in diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md new file mode 100644 index 000000000..ff6e2cb36 --- /dev/null +++ b/docs/guide/advanced/navigation-failures.md @@ -0,0 +1,61 @@ +# Navigation Failures + +> New in 3.4.0 + +When using `router-link`, Vue Router calls `router.push` to trigger a navigation. While the expected behavior for most links is to navigate a user to a new page, there are a few situations where users will remain on the same page: + +- Users are already on the page that they are trying to navigate to +- A [navigation guard](./navigation-guards.md) aborts the navigation by calling `next(false)` +- A [navigation guard](./navigation-guards.md) throws an error or calls `next(new Error())` + +When using a `router-link` component, **none of these failures will log an error**. However, if you are using `router.push` or `router.replace`, you might come across an _"Uncaught (in promise) Error"_ message followed by a more specific message in your console. Let's understand how to differentiate _Navigation Failures_. + +::: tip Background story +In v3.2.0, _Navigation Failures_ were exposed through the two optional callbacks of `router.push`: `onComplete` and `onAbort`. Since version 3.1.0, `router.push` and `router.replace` return a _Promise_ if no `onComplete`/`onAbort` callback is provided. This _Promise_ resolves instead of invoking `onComplete` and rejects instead of invoking `onAbort`. +::: + +## Detecting Navigation Failures + +_Navigation Failures_ are `Error` instances with a few extra properties. To check if an error comes from the Router, use the `isNavigationFailure` function: + +```js +import VueRouter from 'vue-router' +const { isNavigationFailure, NavigationFailureType } = VueRouter + +// trying to access the admin page +router.push('/admin').catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + // show a small notification to the user + showToast('Login in order to access the admin panel') + } +}) +``` + +::: tip +If you omit the second parameter: `isNavigationFailure(failure)`, it will only check if the error is a _Navigation Failure_. +::: + +## `NavigationFailureType` + +`NavigationFailureType` help developers to differentiate between the various types of _Navigation Failures_. There are four different types: + +- `redirected`: `next(newLocation)` was called inside of a navigation guard to redirect somewhere else. +- `aborted`: `next(false)` was called inside of a navigation guard to the navigation. +- `cancelled`: A new navigation completely took place before the current navigation could finish. e.g. `router.push` was called while waiting inside of a navigation guard. +- `duplicated`: The navigation was prevented because we are already at the target location. + +## _Navigation Failures_'s properties + +All navigation failures expose `to` and `from` properties to reflect the target and current location respectively for the navigation that failed: + +```js +// trying to access the admin page +router.push('/admin').catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + failure.to.path // '/admin' + failure.from.path // '/' + } +}) +``` + +In all cases, `to` and `from` are normalized route locations. diff --git a/docs/guide/advanced/navigation-guards.md b/docs/guide/advanced/navigation-guards.md index 61d9bf13c..8c132c267 100644 --- a/docs/guide/advanced/navigation-guards.md +++ b/docs/guide/advanced/navigation-guards.md @@ -6,6 +6,8 @@ Remember that **params or query changes won't trigger enter/leave navigation gua ## Global Before Guards + + You can register global before guards using `router.beforeEach`: ```js @@ -39,7 +41,7 @@ Every guard function receives three arguments: ```js // BAD router.beforeEach((to, from, next) => { - if (!isAuthenticated) next('/login') + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) // if the user is not authenticated, `next` is called twice next() }) @@ -48,7 +50,7 @@ router.beforeEach((to, from, next) => { ```js // GOOD router.beforeEach((to, from, next) => { - if (!isAuthenticated) next('/login') + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) else next() }) ``` @@ -98,20 +100,20 @@ Finally, you can directly define route navigation guards inside route components ```js const Foo = { template: `...`, - beforeRouteEnter (to, from, next) { + beforeRouteEnter(to, from, next) { // called before the route that renders this component is confirmed. // does NOT have access to `this` component instance, // because it has not been created yet when this guard is called! }, - beforeRouteUpdate (to, from, next) { - // called when the route that renders this component has changed, - // but this component is reused in the new route. + beforeRouteUpdate(to, from, next) { + // called when the route that renders this component has changed. + // This component being reused (by using an explicit `key`) in the new route or not doesn't change anything. // For example, for a route with dynamic params `/foo/:id`, when we // navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance - // will be reused, and this hook will be called when that happens. + // will be reused (unless you provided a `key` to ``), and this hook will be called when that happens. // has access to `this` component instance. }, - beforeRouteLeave (to, from, next) { + beforeRouteLeave(to, from, next) { // called when the route that renders this component is about to // be navigated away from. // has access to `this` component instance. @@ -131,7 +133,7 @@ beforeRouteEnter (to, from, next) { } ``` -Note that `beforeRouteEnter` is the only guard that supports passing a callback to `next`. For `beforeRouteUpdate` and `beforeRouteLeave`, `this` is already available, so passing a callback is unnecessary and therefore *not supported*: +Note that `beforeRouteEnter` is the only guard that supports passing a callback to `next`. For `beforeRouteUpdate` and `beforeRouteLeave`, `this` is already available, so passing a callback is unnecessary and therefore _not supported_: ```js beforeRouteUpdate (to, from, next) { @@ -154,6 +156,18 @@ beforeRouteLeave (to, from, next) { } ``` +If you are using mixins that add in-component navigation guards, make sure to add the mixin **after installing the router plugin**: + +```js +Vue.use(Router) + +Vue.mixin({ + beforeRouteUpdate(to, from, next) { + // ... + } +}) +``` + ## The Full Navigation Resolution Flow 1. Navigation triggered. diff --git a/docs/guide/advanced/scroll-behavior.md b/docs/guide/advanced/scroll-behavior.md index d58d797b2..037329426 100644 --- a/docs/guide/advanced/scroll-behavior.md +++ b/docs/guide/advanced/scroll-behavior.md @@ -1,12 +1,14 @@ # Scroll Behavior + + When using client-side routing, we may want to scroll to top when navigating to a new route, or preserve the scrolling position of history entries just like real page reload does. `vue-router` allows you to achieve these and even better, allows you to completely customize the scroll behavior on route navigation. **Note: this feature only works if the browser supports `history.pushState`.** When creating the router instance, you can provide the `scrollBehavior` function: -``` js +```js const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { @@ -26,7 +28,7 @@ If a falsy value or an empty object is returned, no scrolling will happen. For example: -``` js +```js scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } } @@ -36,7 +38,7 @@ This will simply make the page scroll to top for all route navigations. Returning the `savedPosition` will result in a native-like behavior when navigating with back/forward buttons: -``` js +```js scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition @@ -48,7 +50,7 @@ scrollBehavior (to, from, savedPosition) { If you want to simulate the "scroll to anchor" behavior: -``` js +```js scrollBehavior (to, from, savedPosition) { if (to.hash) { return { @@ -67,7 +69,7 @@ We can also use [route meta fields](meta.md) to implement fine-grained scroll be You can also return a Promise that resolves to the desired position descriptor: -``` js +```js scrollBehavior (to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { @@ -78,3 +80,18 @@ scrollBehavior (to, from, savedPosition) { ``` It's possible to hook this up with events from a page-level transition component to make the scroll behavior play nicely with your page transitions, but due to the possible variance and complexity in use cases, we simply provide this primitive to enable specific userland implementations. + +## Smooth Scrolling + +You can enable native smooth scrolling for [browsers supporting it](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior) by simply adding the `behavior` option to the object returned inside `scrollBehavior`: + +```js +scrollBehavior (to, from, savedPosition) { + if (to.hash) { + return { + selector: to.hash, + behavior: 'smooth', + } + } +} +``` diff --git a/docs/guide/advanced/transitions.md b/docs/guide/advanced/transitions.md index 269d3cc23..0b8a937df 100644 --- a/docs/guide/advanced/transitions.md +++ b/docs/guide/advanced/transitions.md @@ -1,20 +1,22 @@ # Transitions + + Since the `` is essentially a dynamic component, we can apply transition effects to it the same way using the `` component: -``` html +```html ``` -[All transition APIs](https://vuejs.org/guide/transitions.html) work the same here. +[All transition APIs](https://v2.vuejs.org/v2/guide/transitions.html) work the same here. ## Per-Route Transition The above usage will apply the same transition for all routes. If you want each route's component to have different transitions, you can instead use `` with different names inside each route component: -``` js +```js const Foo = { template: ` @@ -36,14 +38,14 @@ const Bar = { It is also possible to determine the transition to use dynamically based on the relationship between the target route and current route: -``` html +```html ``` -``` js +```js // then, in the parent component, // watch the `$route` to determine the transition to use watch: { diff --git a/docs/guide/essentials/dynamic-matching.md b/docs/guide/essentials/dynamic-matching.md index 12807125b..c8ced5875 100644 --- a/docs/guide/essentials/dynamic-matching.md +++ b/docs/guide/essentials/dynamic-matching.md @@ -1,5 +1,7 @@ # Dynamic Route Matching + + Very often we will need to map routes with the given pattern to the same component. For example we may have a `User` component which should be rendered for all users but with different user IDs. In `vue-router` we can use a dynamic segment in the path to achieve that: ```js @@ -58,7 +60,7 @@ Or, use the `beforeRouteUpdate` [navigation guard](../advanced/navigation-guards ```js const User = { template: '...', - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { // react to route changes... // don't forget to call next() } diff --git a/docs/guide/essentials/history-mode.md b/docs/guide/essentials/history-mode.md index 5eeaad033..ed27c1baa 100644 --- a/docs/guide/essentials/history-mode.md +++ b/docs/guide/essentials/history-mode.md @@ -24,6 +24,9 @@ Not to worry: To fix the issue, all you need to do is add a simple catch-all fal #### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / @@ -52,9 +55,9 @@ const fs = require('fs') const httpPort = 80 http.createServer((req, res) => { - fs.readFile('index.htm', 'utf-8', (err, content) => { + fs.readFile('index.html', 'utf-8', (err, content) => { if (err) { -      console.log('We cannot open "index.htm" file.') +      console.log('We cannot open "index.html" file.') } res.writeHead(200, { @@ -97,7 +100,13 @@ For Node.js/Express, consider using [connect-history-api-fallback middleware](ht ``` -#### Caddy +#### Caddy v2 + +``` +try_files {path} / +``` + +#### Caddy v1 ``` rewrite { @@ -132,7 +141,11 @@ There is a caveat to this: Your server will no longer report 404 errors as all n const router = new VueRouter({ mode: 'history', routes: [ - { path: '*', component: NotFoundComponent } + { + path: '/:catchAll(.*)', + component: NotFoundComponent, + name: 'NotFound' + } ] }) ``` diff --git a/docs/guide/essentials/named-routes.md b/docs/guide/essentials/named-routes.md index 2268f1425..2e77ed7cf 100644 --- a/docs/guide/essentials/named-routes.md +++ b/docs/guide/essentials/named-routes.md @@ -1,8 +1,10 @@ # Named Routes + + Sometimes it is more convenient to identify a route with a name, especially when linking to a route or performing navigations. You can give a route a name in the `routes` options while creating the Router instance: -``` js +```js const router = new VueRouter({ routes: [ { @@ -16,14 +18,14 @@ const router = new VueRouter({ To link to a named route, you can pass an object to the `router-link` component's `to` prop: -``` html +```html User ``` This is the exact same object used programmatically with `router.push()`: -``` js -router.push({ name: 'user', params: { userId: 123 }}) +```js +router.push({ name: 'user', params: { userId: 123 } }) ``` In both cases, the router will navigate to the path `/user/123`. diff --git a/docs/guide/essentials/nested-routes.md b/docs/guide/essentials/nested-routes.md index 8242d7526..c4692b5a1 100644 --- a/docs/guide/essentials/nested-routes.md +++ b/docs/guide/essentials/nested-routes.md @@ -1,5 +1,7 @@ # Nested Routes + + Real app UIs are usually composed of components that are nested multiple levels deep. It is also very common that the segments of a URL corresponds to a certain structure of nested components, for example: ``` @@ -17,27 +19,25 @@ With `vue-router`, it is very simple to express this relationship using nested r Given the app we created in the last chapter: -``` html +```html
    ``` -``` js +```js const User = { template: '
    User {{ $route.params.id }}
    ' } const router = new VueRouter({ - routes: [ - { path: '/user/:id', component: User } - ] + routes: [{ path: '/user/:id', component: User }] }) ``` The `` here is a top-level outlet. It renders the component matched by a top level route. Similarly, a rendered component can also contain its own, nested ``. For example, if we add one inside the `User` component's template: -``` js +```js const User = { template: `
    @@ -50,10 +50,12 @@ const User = { To render components into this nested outlet, we need to use the `children` option in `VueRouter` constructor config: -``` js +```js const router = new VueRouter({ routes: [ - { path: '/user/:id', component: User, + { + path: '/user/:id', + component: User, children: [ { // UserProfile will be rendered inside User's @@ -79,15 +81,16 @@ As you can see the `children` option is just another Array of route configuratio At this point, with the above configuration, when you visit `/user/foo`, nothing will be rendered inside `User`'s outlet, because no sub route is matched. Maybe you do want to render something there. In such case you can provide an empty subroute path: -``` js +```js const router = new VueRouter({ routes: [ { - path: '/user/:id', component: User, + path: '/user/:id', + component: User, children: [ // UserHome will be rendered inside User's // when /user/:id is matched - { path: '', component: UserHome }, + { path: '', component: UserHome } // ...other sub routes ] diff --git a/docs/guide/essentials/passing-props.md b/docs/guide/essentials/passing-props.md index ce0f84a2d..b065f0ad1 100644 --- a/docs/guide/essentials/passing-props.md +++ b/docs/guide/essentials/passing-props.md @@ -1,25 +1,25 @@ # Passing Props to Route Components + + Using `$route` in your component creates a tight coupling with the route which limits the flexibility of the component as it can only be used on certain URLs. To decouple this component from the router use option `props`: **Instead of coupling to `$route`:** -``` js +```js const User = { template: '
    User {{ $route.params.id }}
    ' } const router = new VueRouter({ - routes: [ - { path: '/user/:id', component: User } - ] + routes: [{ path: '/user/:id', component: User }] }) ``` **Decouple it by using `props`** -``` js +```js const User = { props: ['id'], template: '
    User {{ id }}
    ' @@ -31,8 +31,15 @@ const router = new VueRouter({ // for routes with named views, you have to define the `props` option for each named view: { path: '/user/:id', - components: { default: User, sidebar: Sidebar }, - props: { default: true, sidebar: false } + components: { + default: User, + sidebar: Sidebar + }, + props: { + default: true, + // function mode, more about it below + sidebar: route => ({ search: route.query.q }) + } } ] }) @@ -48,10 +55,14 @@ When `props` is set to `true`, the `route.params` will be set as the component p When `props` is an object, this will be set as the component props as-is. Useful for when the props are static. -``` js +```js const router = new VueRouter({ routes: [ - { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } } + { + path: '/promotion/from-newsletter', + component: Promotion, + props: { newsletterPopup: false } + } ] }) ``` @@ -60,10 +71,14 @@ const router = new VueRouter({ You can create a function that returns props. This allows you to cast parameters into other types, combine static values with route-based values, etc. -``` js +```js const router = new VueRouter({ routes: [ - { path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) } + { + path: '/search', + component: SearchUser, + props: route => ({ query: route.query.q }) + } ] }) ``` diff --git a/docs/installation.md b/docs/installation.md index b64ecace3..d3dd4f4c3 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -2,10 +2,10 @@ ## Direct Download / CDN -[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js) +[https://unpkg.com/vue-router@3/dist/vue-router.js](https://unpkg.com/vue-router@3/dist/vue-router.js) -[Unpkg.com](https://unpkg.com) provides npm-based CDN links. The above link will always point to the latest release on npm. You can also use a specific version/tag via URLs like `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js`. +[Unpkg.com](https://unpkg.com) provides npm-based CDN links. The above link will always point to the latest release on npm. You can also use a specific version/tag via URLs like `https://unpkg.com/vue-router@3.0.0/dist/vue-router.js`. Include `vue-router` after Vue and it will install itself automatically: @@ -32,6 +32,14 @@ Vue.use(VueRouter) You don't need to do this when using global script tags. +## Vue CLI + +If you have a project using [Vue CLI](https://cli.vuejs.org/) you can add Vue Router as a plugin. You can let the CLI generate the code above for you as well as two sample routes. **It will also overwrite your `App.vue`** so make sure to backup the file before running the following command inside your project: + +```sh +vue add router +``` + ## Dev Build You will have to clone directly from GitHub and build `vue-router` yourself if @@ -43,3 +51,4 @@ cd node_modules/vue-router npm install npm run build ``` + diff --git a/docs/ja/README.md b/docs/ja/README.md index a43edf928..fdab568b5 100644 --- a/docs/ja/README.md +++ b/docs/ja/README.md @@ -1,8 +1,12 @@ -# 紹介 +--- +home: true +heroImage: /logo.png +actionText: Get Started → +actionLink: /ja/installation.html +footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Morote +--- -:::tip VERSION NOTE -TypeScriptユーザの場合、 `vue-router@3.0+` は `vue@2.5+` を必須とし、逆もまた同様です。 -::: + Vue Router は [Vue.js](http://vuejs.org) 公式ルータです。これは Vue.js のコアと深く深く統合されており、Vue.js でシングルページアプリケーションを構築します。機能は次の通りです: @@ -16,3 +20,5 @@ Vue Router は [Vue.js](http://vuejs.org) 公式ルータです。これは Vue. - カスタマイズ可能なスクロール動作 [はじめに](./guide/) または [例](https://github.com/vuejs/vue-router/tree/dev/examples) から試してください。 ([`README.md`](https://github.com/vuejs/vue-router/)を参照してください。). + + diff --git a/docs/ja/api/README.md b/docs/ja/api/README.md index 657d251fe..996a3ada3 100644 --- a/docs/ja/api/README.md +++ b/docs/ja/api/README.md @@ -16,17 +16,49 @@ sidebar: auto - HTML5 history モードで `base` オプションを使っている時に、 `to` プロパティの URL にそれを含める必要がありません。 -### 外側の要素へのアクティブクラスの適用 +### `v-slot` API (3.1.0 以降) -アクティブクラスを `` タグ自身よりも、外側の要素に対して適用したいことがあるでしょう。その場合、 `` を使って外側の要素を描画して、その内側に生の `` タグをラップすることができます。 +`router-link` は[スコープ付きスロット](https://jp.vuejs.org/v2/guide/components-slots.html#%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97%E4%BB%98%E3%81%8D%E3%82%B9%E3%83%AD%E3%83%83%E3%83%88)を通して低レベルなカスタマイズを提供しています。これは、主にライブラリ作者をターゲットにした高度な API ですが、ほとんどの場合 _NavLink_ などのようなカスタムコンポーネントでも同様に開発者にとっても大変便利です。 -``` html - - /foo +**`v-slot` API を使うとき、それは単一の子を `router-link` に通す必要があります。** そうしない場合は、 `router-link` は `span` 要素で子をラップします。 + +```html + + {{ route.fullPath }} + +``` + +- `href`: 解決された url。これは、`a` 要素の `href` 属性になります +- `route`: 解決された正規化済みロケーション +- `navigate`: ナビゲーションをトリガーするための関数。`router-link` と同じように、**必要なときに自動的にイベントが起こらないようにします。** +- `isActive`: [アクティブクラス (active class)](#active-class) が適用されるとき、`true` になります。任意のクラスを適用できます。 +- `isExactActive`: `true` if the [正確なアクティブクラス (exact active class)](#exact-active-class) が適用されるとき、`true` になります。 任意のクラスを適用できます。 + +#### 例: アクティブクラスを外部要素へ適用 + +アクティブクラスを `` タグ自身よりも、外側の要素に対して適用したいことがあるでしょう。その場合、`` 内の要素を `v-slot` を使ってリンクを作成することでラップできます。 + +```html + +
  • + {{ route.fullPath }} +
  • ``` -この時、 `` は実際のリンクになります (そして正しい `href` が得られます)。しかし、アクティブクラスは外側の `
  • ` に適用されます。 +:::tip +`target="_blank"` を `a` 要素に追加する場合、`@click="navigate"` ハンドラを省略しなければなりません。 +::: ## `` Props @@ -112,7 +144,7 @@ sidebar: auto ``` html - + ``` アクティブリンククラスをより説明している例としてこちらの [動作](https://jsfiddle.net/8xrk1n9f/) を確認してください。 @@ -131,6 +163,13 @@ sidebar: auto 完全一致によってリンクがアクティブになっているときに適用されるアクティブな CSS クラスを設定します。デフォルト値は `linkExactActiveClass` ルーターコンストラクタのオプション経由でグローバルに設定することもできます。 +### aria-current-value + +- 型: `'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false'` +- デフォルト: `"page"` + + 完全一致によってリンクがアクティブになっているときに `aria-current` の値を設定します。ARIA spec において[aria-current で許可されている値](https://www.w3.org/TR/wai-aria-1.2/#aria-current)の1つでなければなりません。ほとんどの場合、デフォルト`page` が最適です。 + ## `` `` コンポーネントは与えられたパスに対してマッチしたコンポーネントを描画する関数型コンポーネントです。`` の中で描画されるコンポーネント自身もまた、ネストされたパスに対してコンポーネントを描画するための `` を持つことができます。 @@ -164,8 +203,8 @@ name ではないプロパティも描画されるコンポーネントに渡さ `RouteConfig` の型宣言: - ``` js - declare type RouteConfig = { + ``` ts + interface RouteConfig = { path: string; component?: Component; name?: string; // 名前付きルート用 @@ -229,11 +268,11 @@ name ではないプロパティも描画されるコンポーネントに渡さ シグネチャ: - ``` + ```ts type PositionDescriptor = { x: number, y: number } | { selector: string } | - ?{} + void type scrollBehaviorHandler = ( to: Route, @@ -313,7 +352,9 @@ router.afterEach((to, from) => {}) ``` js router.push(location, onComplete?, onAbort?) +router.push(location).then(onComplete).catch(onAbort) router.replace(location, onComplete?, onAbort?) +router.replace(location).then(onComplete).catch(onAbort) router.go(n) router.back() router.forward() @@ -462,7 +503,8 @@ router.onError(callback) const router = new VueRouter({ routes: [ // 以下のオブジェクトがルートレコード - { path: '/foo', component: Foo, + { path: '/foo', + component: Foo, children: [ // こちらもルートレコード { path: 'bar', component: Bar } diff --git a/docs/ja/guide/README.md b/docs/ja/guide/README.md index 3baef4c13..a01b4da18 100644 --- a/docs/ja/guide/README.md +++ b/docs/ja/guide/README.md @@ -1,18 +1,20 @@ # はじめに ::: tip Note - ガイド内のコードのサンプルは [ES2015](https://github.com/lukehoban/es6features) を使っています。 +ガイド内のコードのサンプルは [ES2015](https://github.com/lukehoban/es6features) を使っています。 すべての example では、vue の完全バージョンを使用してテンプレートを解析可能にしています。詳細は[こちら](https://jp.vuejs.org/v2/guide/installation.html#ランタイム-コンパイラとランタイム限定の違い)を参照してください。 ::: + + Vue.js と vue-router を使ったシングルページアプリケーションの構築は驚くほど簡単です。Vue.js のコンポーネントを使ってアプリケーションを既に構成しています。vue-router を混ぜ込むには、コンポーネントとルートをマッピングさせて vue-router にどこで描画するかを知らせるだけです。以下が基本的な例です。 ## HTML -``` html - - +```html + +

    Hello App!

    @@ -31,7 +33,7 @@ Vue.js と vue-router を使ったシングルページアプリケーション ## JavaScript -``` js +```js // 0. モジュールシステムを使っている場合 (例: vue-cli 経由で)、Vue と VueRouter をインポートし、`Vue.use(VueRouter)` を呼び出します。 // 1. ルートコンポーネントを定義します @@ -72,7 +74,7 @@ const app = new Vue({ // Home.vue export default { computed: { - username () { + username() { // `params` が表示される return this.$route.params.username } diff --git a/docs/ja/guide/advanced/data-fetching.md b/docs/ja/guide/advanced/data-fetching.md index 22244af65..d767bdab1 100644 --- a/docs/ja/guide/advanced/data-fetching.md +++ b/docs/ja/guide/advanced/data-fetching.md @@ -1,5 +1,7 @@ # データ取得 + + ルートが有効化された時にサーバーからデータを取得する必要がしばしばあります。例えば、ユーザープロフィールを描画する前に、サーバーからユーザーデータを取得する必要があります。これを実現するためには 2 種類の方法があります。 - **ナビゲーション後の取得**: ナビゲーションを先に実行し、その後次に入ってくるコンポーネントのライフサイクルフック内でデータを取得します。データ取得中にローディングを表示します。 @@ -14,10 +16,10 @@ `$route.params.id` を元にポストのデータを取得する必要がある `Post` コンポーネントを想定してみましょう。 -``` html +```html ``` -``` js +```js export default { - data () { + data() { return { loading: false, post: null, error: null } }, - created () { + created() { // view が作られた時にデータを取得し、 // そのデータは既に監視されています this.fetchData() }, watch: { // ルートが変更されたらこのメソッドを再び呼び出します - '$route': 'fetchData' + $route: 'fetchData' }, methods: { - fetchData () { + fetchData() { this.error = this.post = null this.loading = true // `getPost` をあなたのデータ取得用 util や API ラッパーに置き換えてください @@ -71,24 +73,24 @@ export default { ## ナビゲーション前の取得 -こちらのアプローチでは新しいルートへ実際にナビゲーションする前にデータを取得します。次に入ってくるコンポーネント内の `beforeRouteEnter` ガードでデータ取得を実行できます。データ取得が完了したら `next` を呼ぶだけです。 +こちらのアプローチでは新しいルートへ実際にナビゲーションする前にデータを取得します。次に入ってくるコンポーネント内の `beforeRouteEnter` ガードでデータ取得を実行できます。データ取得が完了したら `next` を呼ぶだけです。 -``` js +```js export default { - data () { + data() { return { post: null, error: null } }, - beforeRouteEnter (route, redirect, next) { + beforeRouteEnter(route, redirect, next) { getPost(route.params.id, (err, post) => { next(vm => vm.setData(err, post)) }) }, // コンポーネントが既に描画されている際のルート変更時は // ロジックが少し異なります - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { this.post = null getPost(to.params.id, (err, post) => { this.setData(err, post) @@ -96,7 +98,7 @@ export default { }) }, methods: { - setData (err, post) { + setData(err, post) { if (err) { this.error = err.toString() } else { diff --git a/docs/ja/guide/advanced/lazy-loading.md b/docs/ja/guide/advanced/lazy-loading.md index fee21f2a5..e4d6936ef 100644 --- a/docs/ja/guide/advanced/lazy-loading.md +++ b/docs/ja/guide/advanced/lazy-loading.md @@ -1,18 +1,23 @@ # 遅延ローディングルート + + バンドラーを使ってアプリケーションを構築している時、バンドルされる JavaScript が非常に大きいものになり得ます。結果的にページのロード時間に影響を与えてしまいます。もし各ルートコンポーネントごとに別々のチャンクにして、訪れたルートの時だけロードできればより効率的でしょう。 Vue の [非同期コンポーネント機能](http://jp.vuejs.org/guide/components.html#非同期コンポーネント) と webpack の [コード分割機能](https://webpack.js.org/guides/code-splitting-async/) を組み合わせることでとても簡単に遅延ロードするルートコンポーネントができます。 最初に、非同期コンポーネントは Promise (コンポーネント自身解決する必要がある) を返すファクトリ関数として定義できます: -``` js -const Foo = () => Promise.resolve({ /* component definition */ }) +```js +const Foo = () => + Promise.resolve({ + /* component definition */ + }) ``` 次に、webpack 2 において [動的 import](https://github.com/tc39/proposal-dynamic-import) 構文を使用して、コード分割ポイントを示すことができます: -``` js +```js import('./Foo.vue') // returns a Promise ``` @@ -20,15 +25,15 @@ import('./Foo.vue') // returns a Promise 2 つを組み合わせることで、これは、webpack によって自動的にコード分割される非同期コンポーネントを定義する方法です: -``` js +```js const Foo = () => import('./Foo.vue') ``` ## 同じチャンク内でのコンポーネントグループ化 -しばしば同じ非同期のチャンクに、そのルート配下のネストされた全てのコンポーネントをグループ化したいと思うかもしれません。それを実現するためには、 特別なコメント構文 (webpack > 2.4 必須)を使用してチャンクの名前を提供する [名前付きチャンク](https://webpack.js.org/guides/code-splitting-async/#chunk-names) を使う必要があります。 +しばしば同じ非同期のチャンクに、そのルート配下のネストされた全てのコンポーネントをグループ化したいと思うかもしれません。それを実現するためには、 特別なコメント構文 (webpack > 2.4 必須)を使用してチャンクの名前を提供する [名前付きチャンク](https://webpack.js.org/api/module-methods/#magic-comments) を使う必要があります。 -``` js +```js const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') diff --git a/docs/ja/guide/advanced/meta.md b/docs/ja/guide/advanced/meta.md index b7f876e65..4ea38484f 100644 --- a/docs/ja/guide/advanced/meta.md +++ b/docs/ja/guide/advanced/meta.md @@ -1,8 +1,10 @@ # ルートメタフィールド + + ルートの定義をする際に `meta` フィールドを含めることができます。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -31,7 +33,7 @@ const router = new VueRouter({ メタフィールドをグローバルナビゲーションガードで確認するユースケースの例: -``` js +```js router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { // このルートはログインされているかどうか認証が必要です。 @@ -48,5 +50,4 @@ router.beforeEach((to, from, next) => { next() // next() を常に呼び出すようにしてください! } }) - ``` diff --git a/docs/ja/guide/advanced/navigation-failures.md b/docs/ja/guide/advanced/navigation-failures.md new file mode 100644 index 000000000..b43f3ee75 --- /dev/null +++ b/docs/ja/guide/advanced/navigation-failures.md @@ -0,0 +1,61 @@ +# ナビゲーションの失敗 + +> 3.4.0の新機能 + +`router-link` を使用すると、ナビゲーションを開始するためにVue Routerは `router.push` を呼び出します。ほとんどのリンクはユーザーを新しいページに移動させますが、ユーザーが同じページに留まる状況もいくつかあります。 + +- すでにユーザーが移動したいページにいる +- [ナビゲーションガード](./navigation-guards.md)が `next(false)` の呼び出しによって移動を中止した +- [ナビゲーションガード](./navigation-guards.md)がエラーを投げた、または `next(new Error())` を呼び出した + +`router-link` コンポーネントを使用している場合、**これらの失敗はエラーとして記録されません**。しかし、 `router.push` または `router.replace` を使用している場合は、 _"Uncaught (in promise) Error"_ に続いて具体的なメッセージがコンソールに表示されることがあります。_ナビゲーションの失敗_ を区別する方法を理解しましょう。 + +::: tip 背景 +v3.2.0では、router.pushの2つのオプションのコールバック( `onComplete` と `onAbort` )を通して _ナビゲーションの失敗_ が明らかになっていました。バージョン3.1.0以降、 `onComplete` / `onAbort` コールバックが提供されていない場合、 `router.push` と `router.replace` は _Promise_ を返します。この _Promise_ は `onComplete` の代わりにResolvedとなり、 `onAbort` の代わりにRejectedとなります。 +::: + +## ナビゲーションの失敗を検出する + +_Navigation Failures_ はいくつかの追加プロパティをもつ `Error` インスタンスです。ルーターからエラーが発生したかを確認するには、 `isNavigationFailure` 関数を使用します。 + +```js +import VueRouter from 'vue-router' +const { isNavigationFailure, NavigationFailureType } = VueRouter + +// 管理画面にアクセス +router.push('/admin').catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + // ユーザーに小さな通知を表示 + showToast('Login in order to access the admin panel') + } +}) +``` + +::: tip +`isNavigationFailure(failure)` のように2番目のパラメータを省略すると、エラーが _Navigation Failure_ かどうかのチェックのみが行われます。 +::: + +## `NavigationFailureType` + +`NavigationFailureType` は開発者がさまざまな種類の _Navigation Failures_ を区別するのに役立ちます。次の4つの種類があります。 + +- `redirected`: 別の場所にリダイレクトするために、ナビゲーションガードの中で `next(newLocation)` が呼び出された +- `aborted`: ナビゲーションガードの中で `next(false)` が呼び出された +- `cancelled`: 現在のナビゲーションが終了する前に、新しいナビゲーションが実行された。例:ナビゲーションガード内で待機中に `router.push` が呼び出された +- `duplicated`: すでに目的の場所にいるため、ナビゲーションが妨げられた + +## _Navigation Failures_ のプロパティ + +全てのナビゲーションの失敗は、そのナビゲーションのターゲットと現在地を反映した `to` と `from` のプロパティを公開します。 + +```js +// 管理画面にアクセス +router.push('/admin').catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + failure.to.path // '/admin' + failure.from.path // '/' + } +}) +``` + +全ての場合において、 `to` と `from` は正規化されたルートの場所です。 diff --git a/docs/ja/guide/advanced/navigation-guards.md b/docs/ja/guide/advanced/navigation-guards.md index 13add0f21..b7e588f61 100644 --- a/docs/ja/guide/advanced/navigation-guards.md +++ b/docs/ja/guide/advanced/navigation-guards.md @@ -1,5 +1,7 @@ # ナビゲーションガード + + この名前が示すように、 `vue-router` によって提供されるナビゲーションガードは、リダイレクトもしくはキャンセルによって遷移をガードするために主に使用されます。ルートナビゲーション処理 (グローバル、ルート単位、コンポーネント内) をフックする多くの方法があります。 **パラメータまたはクエリの変更は enter/leave ナビゲーションガードをトリガーしない** ということを覚えておいてください。それらの変更に対応するために [`$route` オブジェクトを監視する](../essentials/dynamic-matching.md#reacting-to-params-changes)、またはコンポーネント内ガード `beforeRouteUpdate` を使用するかの、どちらかができます。 @@ -8,7 +10,7 @@ `router.beforeEach` を使ってグローバル before ガードを登録できます。 -``` js +```js const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { @@ -20,21 +22,38 @@ router.beforeEach((to, from, next) => { 全てのガード関数は 3 つの引数を受け取ります。 - - **`to: Route`**: 次にナビゲーションされる対象の [ルートオブジェクト](../../api/#ルートオブジェクト)。 - - - **`from: Route`**: ナビゲーションされる前の現在のルートです。 - - - **`next: Function`**: フックを **解決** するためにこの関数を呼ぶ必要があります。この振る舞いは `next` に渡される引数に依存します: +- **`to: Route`**: 次にナビゲーションされる対象の [ルートオブジェクト](../../api/#ルートオブジェクト)。 + +- **`from: Route`**: ナビゲーションされる前の現在のルートです。 + +- **`next: Function`**: フックを **解決** するためにこの関数を呼ぶ必要があります。この振る舞いは `next` に渡される引数に依存します: + +- **`next()`**: パイプラインの次のフックに移動します。もしフックが残っていない場合は、このナビゲーションは **確立** されます。 - - **`next()`**: パイプラインの次のフックに移動します。もしフックが残っていない場合は、このナビゲーションは **確立** されます。 +- **`next(false)`**: 現在のナビゲーションを中止します。もしブラウザの URL が変化した場合は(ユーザーが手動で変更した場合でも、戻るボタンの場合でも)、 `from` ルートの URL にリセットされます。 - - **`next(false)`**: 現在のナビゲーションを中止します。もしブラウザのURLが変化した場合は(ユーザーが手動で変更した場合でも、戻るボタンの場合でも)、 `from` ルートのURLにリセットされます。 +- **`next('/')` または `next({ path: '/' })`**: 異なる場所へリダイレクトします。現在のナビゲーションは中止され、あたらしいナビゲーションが始まります。任意のロケーションオブジェクトを `next` に渡すことができます。この `next` には、`replace: true`、 `name: 'home'` のようなオプション、そして [`router-link`、`to` プロパティ](../../api/#router-link)または [`router.push`](../../api/#ルーターインスタンスプロパティ)で使用される任意のオプションを指定することができます。 - - **`next('/')` または `next({ path: '/' })`**: 異なる場所へリダイレクトします。現在のナビゲーションは中止され、あたらしいナビゲーションが始まります。任意のロケーションオブジェクトを `next` に渡すことができます。この `next` には、`replace: true`、 `name: 'home'` のようなオプション、そして [`router-link`、`to` プロパティ](../../api/#router-link)または [`router.push`](../../api/#ルーターインスタンスプロパティ)で使用される任意のオプションを指定することができます。 +- **`next(error)`**: (2.4.0+) `next` に渡された引数が `Error` インスタンスである場合、ナビゲーションは中止され、エラーは `router.onError()` を介して登録されたコールバックに渡されます。 - - **`next(error)`**: (2.4.0+) `next` に渡された引数が `Error` インスタンスである場合、ナビゲーションは中止され、エラーは `router.onError()` を介して登録されたコールバックに渡されます。 +**与えられたナビゲーションガードを通過する任意のパスにおいて、常に 1 回だけ `next` 関数が呼び出されるようにしてください。それは 1 回以上出現することがありますが、論理パスが重ならないときだけで、そうしないないとフックは決して解決されない、またはエラーが発生します。** 以下は、ユーザーが認証されていない場合、`/login` にリダレクトするための例です: + +```js +// BAD +router.beforeEach((to, from, next) => { + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) + // ユーザーが認証されていない場合、 `next` は2回呼ばれる + next() +}) +``` - **常に `next` 関数を呼び出すようにしてください。そうでなければ、フックは決して解決されません。** +```js +// GOOD +router.beforeEach((to, from, next) => { + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) + else next() +}) +``` ## グローバル解決ガード @@ -46,7 +65,7 @@ router.beforeEach((to, from, next) => { グローバル after フックを登録することもできます。しかしながら、ガードとは異なり、これらのフックは `next` 関数を受け取らず、ナビゲーションに影響しません。 -``` js +```js router.afterEach((to, from) => { // ... }) @@ -56,7 +75,7 @@ router.afterEach((to, from) => { 直接ルート設定オブジェクトの `beforeEnter` ガードを定義することができます。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -80,22 +99,22 @@ const router = new VueRouter({ - `beforeRouteUpdate` (2.2 で追加) - `beforeRouteLeave` -``` js +```js const Foo = { template: `...`, - beforeRouteEnter (to, from, next) { + beforeRouteEnter(to, from, next) { // このコンポーネントを描画するルートが確立する前に呼ばれます。 // `this` でのこのコンポーネントへのアクセスはできません。 // なぜならばこのガードが呼び出される時にまだ作られていないからです! }, - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { // このコンポーネントを描画するルートが変更されたときに呼び出されますが、 // このコンポーネントは新しいルートで再利用されます。 // たとえば、動的な引数 `/foo/:id` を持つルートの場合、`/foo/1` と `/foo/2` の間を移動すると、 // 同じ `Foo` コンポーネントインスタンスが再利用され、そのときにこのフックが呼び出されます。 // `this` でコンポーネントインスタンスにアクセスできます。 }, - beforeRouteLeave (to, from, next) { + beforeRouteLeave(to, from, next) { // このコンポーネントを描画するルートが間もなく // ナビゲーションから離れていく時に呼ばれます。 // `this` でのコンポーネントインスタンスへのアクセスができます。 @@ -107,7 +126,7 @@ const Foo = { しかしながら、 `next` にコールバックを渡すことでインスタンスにアクセスすることができます。このコールバックはナビゲーションが確立した時に呼ばれ、コンポーネントインスタンスはそのコールバックの引数として渡されます。 -``` js +```js beforeRouteEnter (to, from, next) { next(vm => { // `vm` を通じてコンポーネントインスタンスにアクセス @@ -115,7 +134,7 @@ beforeRouteEnter (to, from, next) { } ``` -コールバックを `next` に渡すことをサポートするのは、`beforeRouteEnter` ガードだけであるということに注意してください。`beforeRouteUpdate` と `beforeRouteLeave` の場合、 `this` は既に利用可能です。したがって、コールバックを渡す必要はないので、*サポートされません*: +コールバックを `next` に渡すことをサポートするのは、`beforeRouteEnter` ガードだけであるということに注意してください。`beforeRouteUpdate` と `beforeRouteLeave` の場合、 `this` は既に利用可能です。したがって、コールバックを渡す必要はないので、_サポートされません_: ```js beforeRouteUpdate (to, from, next) { @@ -139,8 +158,9 @@ beforeRouteLeave (to, from, next) { ``` ## 完全なナビゲーション解決フロー + 1. ナビゲーションがトリガされる -2. 非アクティブ化されたコンポーネントで leave ガードを呼ぶ +2. 非アクティブ化されたコンポーネントで `beforeRouteLeave` ガードを呼ぶ 3. グローバル `beforeEach` ガードを呼ぶ 4. 再利用されるコンポーネントで `beforeRouteUpdate` ガードを呼ぶ (2.2 以降) 5. ルート設定内の `beforeEnter` を呼ぶ diff --git a/docs/ja/guide/advanced/scroll-behavior.md b/docs/ja/guide/advanced/scroll-behavior.md index ed6035599..2e0d6f1ba 100644 --- a/docs/ja/guide/advanced/scroll-behavior.md +++ b/docs/ja/guide/advanced/scroll-behavior.md @@ -1,12 +1,14 @@ # スクロールの振る舞い + + クライアントサイドのルーティングを使っている時に、新しいルートに対してスクロールをトップへ移動させたいかもしれません、もしくは実際のページリロードがしているように history 要素のスクロールポジションを保持したいこともあるかもしれません。 `vue-router` ではこれらをさらによく実現できます。ルートナビゲーションにおけるスクロールの挙動を完全にカスタマイズすることができます。 **注意: この機能は ブラウザが `history.pushState` をサポートしている場合のみ動作します。** ルーターインスタンスを作る時に、 `scrollBehavior` 関数を提供できます。 -``` js +```js const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { @@ -15,7 +17,7 @@ const router = new VueRouter({ }) ``` -`scrollBehavior` 関数は `to` と `from` のルートオブジェクトを受け取ります。第 3 引数の `savedPosition` は `popstate` ナビゲーション (ブラウザの戻る/進むボタンがトリガーされた) 時のみ利用可能です。 +`scrollBehavior` 関数は `to` と `from` のルートオブジェクトを受け取ります。第 3 引数の `savedPosition` は `popstate` ナビゲーション (ブラウザの戻る/進むボタンがトリガーされた) 時のみ利用可能です。 この関数はスクロールポジションオブジェクトを返すことができます。そのオブジェクトは以下のような形式です。 @@ -27,7 +29,7 @@ const router = new VueRouter({ 例: -``` js +```js scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } } @@ -37,7 +39,7 @@ scrollBehavior (to, from, savedPosition) { `savedPosition` を返すことは結果的に戻る/進むボタンを押してナビゲーションした時にネイティブのような挙動になります。 -``` js +```js scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition @@ -49,7 +51,7 @@ scrollBehavior (to, from, savedPosition) { もし"アンカーへスクロール"の振る舞いをシミュレートしたい場合は以下のようにしてください。 -``` js +```js scrollBehavior (to, from, savedPosition) { if (to.hash) { return { @@ -68,7 +70,7 @@ scrollBehavior (to, from, savedPosition) { 期待する位置記述子 (position descriptor) に解決されるプロミスを返すこともできます: -``` js +```js scrollBehavior (to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { diff --git a/docs/ja/guide/advanced/transitions.md b/docs/ja/guide/advanced/transitions.md index bb76c1577..350429959 100644 --- a/docs/ja/guide/advanced/transitions.md +++ b/docs/ja/guide/advanced/transitions.md @@ -1,8 +1,10 @@ # トランジション + + 基本的に `` は動的コンポーネントなので、 `` コンポーネントを使うのと同じ方法でトランジションを適用することができます。 -``` html +```html @@ -14,7 +16,7 @@ 上記の使い方では全てのトランジションが全てのルートに対して適用されます。もし各ルートコンポーネントにそれぞれ違うトランジションを持たせたい場合は、代わりにルーターコンポーネント内で異なる名前で `` を使うことができます。 -``` js +```js const Foo = { template: ` @@ -36,14 +38,14 @@ const Bar = { 対象のルートと現在のルートの関係を元に動的にトランジションを決定することも可能です。 -``` html +```html ``` -``` js +```js // そして親コンポーネントの中で、 // `$route` を watch して使用するトランジションを決定します watch: { diff --git a/docs/ja/guide/essentials/dynamic-matching.md b/docs/ja/guide/essentials/dynamic-matching.md index f1b0d1efa..7c72d8ff3 100644 --- a/docs/ja/guide/essentials/dynamic-matching.md +++ b/docs/ja/guide/essentials/dynamic-matching.md @@ -1,8 +1,10 @@ # 動的ルートマッチング + + パターンを使って同じコンポーネントにルートをマップする必要がしばしばあるでしょう。例えば、 `User` コンポーネントは全てのユーザーに対して描画されるべきであるが、それぞれ異なるユーザー ID を持つ場合などです。`vue-router` ではパスの中の動的なセグメントを使用して実現できます。 -``` js +```js const User = { template: '
    User
    ' } @@ -19,7 +21,7 @@ const router = new VueRouter({ 動的セグメントはコロン `:` を使って表されます。ルートがマッチした時、この動的セグメントの値は全てのコンポーネント内で `this.$route.params` として利用可能になります。したがって、現在の `User` のテンプレートを次のように更新することで現在のユーザー ID を表示することができます。 -``` js +```js const User = { template: '
    User {{ $route.params.id }}
    ' } @@ -29,9 +31,9 @@ const User = { 1 つのルートが複数の動的なセグメントを持つこともできます。そして、それらは `$route.params` の一致したフィールドとマップされます。例: -| パターン | マッチしたパス | $route.params | -|---------|------|--------| -| /user/:username | /user/evan | `{ username: 'evan' }` | +| パターン | マッチしたパス | \$route.params | +| ----------------------------- | ------------------- | -------------------------------------- | +| /user/:username | /user/evan | `{ username: 'evan' }` | | /user/:username/post/:post_id | /user/evan/post/123 | `{ username: 'evan', post_id: '123' }` | `$route.params` に加えて、`$route` オブジェクトでは `$route.query` (もし URL 上にクエリがあるなら) や `$route.hash` など便利な情報も利用可能です。それらの詳細については [API リファレンス](../../api/#the-route-object) でご確認ください。 @@ -42,11 +44,11 @@ const User = { 同じコンポーネントでパラメーター変更を検知するためには、 `$route` オブジェクトを watch するだけです。 -``` js +```js const User = { template: '...', watch: { - '$route' (to, from) { + $route(to, from) { // ルートの変更の検知... } } @@ -55,10 +57,10 @@ const User = { または、2.2 で導入された `beforeRouteUpdate` [ナビゲーションガード](../advanced/navigation-guards.html)を使用します: -``` js +```js const User = { template: '...', - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { // ルート変更に反応する... // next() を呼び出すのを忘れないでください } @@ -81,7 +83,7 @@ const User = { ``` _アスタリスク_ ルートを使用するときは、_アスタリスク_ ルートが最後になるようにルートを正しく順序付けてください。 -`{ path: '*' }` ルートは、通常クライアントサイドの404ページで使われます。_History モード_ を使用する場合は、[正しいサーバの設定](./history-mode.md)も同様にしっかりしてください。 +`{ path: '*' }` ルートは、通常クライアントサイドの 404 ページで使われます。_History モード_ を使用する場合は、[正しいサーバの設定](./history-mode.md)も同様にしっかりしてください。 _アスタリスク_ を使用するときは、 `pathMatch` と名付けられたパラメータは、自動的に `$route.params` に追加されます。_アスタリスク_ によってマッチされた url の残りを含みます: @@ -100,4 +102,4 @@ this.$route.params.pathMatch // '/non-existing' ## マッチングの優先度 -しばしば同じURLで複数のルートがマッチすることがあります。そのようなケースではマッチングの優先度はルートの定義された順番によって決定されます。先に定義されたルートほど優先度が高くなります。 +しばしば同じ URL で複数のルートがマッチすることがあります。そのようなケースではマッチングの優先度はルートの定義された順番によって決定されます。先に定義されたルートほど優先度が高くなります。 diff --git a/docs/ja/guide/essentials/history-mode.md b/docs/ja/guide/essentials/history-mode.md index 26081d159..5bf831441 100644 --- a/docs/ja/guide/essentials/history-mode.md +++ b/docs/ja/guide/essentials/history-mode.md @@ -1,10 +1,12 @@ # HTML5 History モード + + `vue-router` のデフォルトは _hash モード_ です - 完全な URL を hash を使ってシミュレートし、 URL が変更された時にページのリロードが起きません。 その hash を取り除くために、ページのリロード無しに URL 遷移を実現する `history.pushState` API を利用したルーターの **history モード** を使うことができます。 -``` js +```js const router = new VueRouter({ mode: 'history', routes: [...] @@ -22,6 +24,9 @@ history モードを使用する時は、URL は "普通" に見えます e.g. ` #### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / @@ -49,21 +54,23 @@ const http = require('http') const fs = require('fs') const httpPort = 80 -http.createServer((req, res) => { - fs.readFile('index.htm', 'utf-8', (err, content) => { - if (err) { - console.log('We cannot open "index.htm" file.') - } +http + .createServer((req, res) => { + fs.readFile('index.html', 'utf-8', (err, content) => { + if (err) { + console.log('We cannot open "index.html" file.') + } - res.writeHead(200, { - 'Content-Type': 'text/html; charset=utf-8' - }) + res.writeHead(200, { + 'Content-Type': 'text/html; charset=utf-8' + }) - res.end(content) + res.end(content) + }) + }) + .listen(httpPort, () => { + console.log('Server listening on: http://localhost:%s', httpPort) }) -}).listen(httpPort, () => { - console.log('Server listening on: http://localhost:%s', httpPort) -}) ``` #### Node.js (Express) @@ -75,7 +82,7 @@ Node.js/Express では [connect-history-api-fallback middleware](https://github. 1. [IIS UrlRewrite](https://www.iis.net/downloads/microsoft/url-rewrite) をインストール 2. 以下によるサイトのルートディレクトリに `web.config` ファイルを作成 -``` xml +```xml @@ -126,12 +133,10 @@ rewrite { この点に関して注意があります。全ての not-found パスが `index.html` を提供するため、もはや 404 エラーをサーバーがレポートしなくなります。回避策として、Vue アプリケーション内で 404 ページを表示するために catch-all ルートを実装すべきです。 -``` js +```js const router = new VueRouter({ mode: 'history', - routes: [ - { path: '*', component: NotFoundComponent } - ] + routes: [{ path: '*', component: NotFoundComponent }] }) ``` diff --git a/docs/ja/guide/essentials/named-routes.md b/docs/ja/guide/essentials/named-routes.md index 24946a609..4169fd3b6 100644 --- a/docs/ja/guide/essentials/named-routes.md +++ b/docs/ja/guide/essentials/named-routes.md @@ -1,8 +1,10 @@ # 名前付きルート + + しばしば、名前を使ってルートを特定できるとより便利です。特にルートにリンクするときやナビゲーションを実行するときなどです。Router インスタンスを作成するときに `routes` オプションの中でルートに名前を付けることができます。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -16,14 +18,14 @@ const router = new VueRouter({ 名前を付けたルートにリンクするには、 `router-link` コンポーネントの `to` プロパティにオブジェクトを渡します。 -``` html +```html User ``` これはプログラムで `router.push()` を呼び出すときに使われるオブジェクトと全く同じです。 -``` js -router.push({ name: 'user', params: { userId: 123 }}) +```js +router.push({ name: 'user', params: { userId: 123 } }) ``` どちらのケースもルーターは `/user/123` のパスにナビゲーションします。 diff --git a/docs/ja/guide/essentials/named-views.md b/docs/ja/guide/essentials/named-views.md index 7cc117cab..3a1a11c74 100644 --- a/docs/ja/guide/essentials/named-views.md +++ b/docs/ja/guide/essentials/named-views.md @@ -1,8 +1,10 @@ # 名前付きビュー + + しばしば、ネストをさせずに同時に複数の view を表示する必要があるでしょう。例えば、`sidebar` view と `main` view を使ったレイアウトを作成する時です。そんな時に名前付きビューは便利です。あなたの view に 1 つのアウトレットを持つのではなく、複数のそれぞれが名前付きの view を持つことができます。名前を持たない `router-view` はその名前として `default` が付与されます。 -``` html +```html @@ -10,7 +12,7 @@ 1 つの view は 1 つのコンポーネントを使って描画されます。したがって、同じルートに対する複数の view には複数のコンポーネントが必須になります。この `components` (s が付いている) オプションに注意してください。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -25,7 +27,7 @@ const router = new VueRouter({ }) ``` -この例の動作しているデモは +この例の動作しているデモは [こちら](https://jsfiddle.net/posva/6du90epg/) です。 ## ネストされた名前付きビュー @@ -56,9 +58,9 @@ const router = new VueRouter({

    User Settings

    - - - + + +
    ``` diff --git a/docs/ja/guide/essentials/navigation.md b/docs/ja/guide/essentials/navigation.md index ac048742a..abe202225 100644 --- a/docs/ja/guide/essentials/navigation.md +++ b/docs/ja/guide/essentials/navigation.md @@ -1,5 +1,7 @@ # プログラムによるナビゲーション + + 宣言的なナビゲーションとしてアンカータグを作成する `` がありますが、ルーターのインスタンスメソッドを使ったプログラムによる方法でもそれは可能です。 #### `router.push(location, onComplete?, onAbort?)` @@ -8,16 +10,15 @@ 異なる URL へ遷移するときに `router.push` が使えます。このメソッドは history スタックに新しいエントリを追加します。それによってユーザーがブラウザの戻るボタンをクリックした時に前の URL に戻れるようになります。 - このメソッドは `` をクリックした時に内部的に呼ばれています。つまり `` をクリックすることは `router.push(...)` を呼ぶことと等価です。 -| 宣言的 | プログラム的 | -|-------------|--------------| +| 宣言的 | プログラム的 | +| ------------------------- | ------------------ | | `` | `router.push(...)` | 引数は文字列のパス、もしくは、location を記述するオブジェクトが使えます。例: -``` js +```js // 文字列パス router.push('home') @@ -51,18 +52,17 @@ router.push({ path: '/user', params: { userId } }) // -> /user これは `router.push` のように動作しますが、異なる点は新しい history エントリを追加しないで遷移することです。この名前から推定されるように、現在のエントリを置換します。 -| 宣言的 | プログラム的 | -|-------------|--------------| +| 宣言的 | プログラム的 | +| --------------------------------- | --------------------- | | `` | `router.replace(...)` | - #### `router.go(n)` このメソッドは、history スタックの中でどのくらいステップを進めるか、もしくは戻るのか、を表す 1 つの integer をパラメーターとして受け取ります。`window.history.go(n)` と類似しています。 例 -``` js +```js // 1 つレコードを進める。history.forward() と同じ router.go(1) diff --git a/docs/ja/guide/essentials/nested-routes.md b/docs/ja/guide/essentials/nested-routes.md index da153569a..f62afa99b 100644 --- a/docs/ja/guide/essentials/nested-routes.md +++ b/docs/ja/guide/essentials/nested-routes.md @@ -1,5 +1,7 @@ # ネストされたルート + + 実際のアプリケーションの UI では通常複数のレベルの階層的にネストしたコンポーネントで構成されます。ネストされたコンポーネントの特定の構造に対して URL のセグメントを対応させることはよくあります。例: ``` @@ -17,27 +19,25 @@ 前の章で作ったアプリケーションを考えてみましょう。 -``` html +```html
    ``` -``` js +```js const User = { template: '
    User {{ $route.params.id }}
    ' } const router = new VueRouter({ - routes: [ - { path: '/user/:id', component: User } - ] + routes: [{ path: '/user/:id', component: User }] }) ``` ここでの `` はトップレベルのアウトレットです。トップレベルのルートによってマッチしたコンポーネントが描画されます。同様に描画されたコンポーネントもまた自身のネストされた `` を持つことができます。`User` コンポーネントのテンプレート内部に 1 つ追加する例です。 -``` js +```js const User = { template: `
    @@ -50,10 +50,12 @@ const User = { このネストされたアウトレットに対してコンポーネントを描画するためには、 `VueRouter` のコンストラクタの設定で `children` オプションを使用する必要があります。 -``` js +```js const router = new VueRouter({ routes: [ - { path: '/user/:id', component: User, + { + path: '/user/:id', + component: User, children: [ { // /user/:id/profile がマッチした時に @@ -72,21 +74,23 @@ const router = new VueRouter({ ] }) ``` + **`/` から始まるネストされたパスは root パスとして扱われることに注意してください。これによってネストされた URL を指定しなくてもコンポーネントをネストすることができます。** `children` オプションを見るとわかる通り、これは `routes` 自身と同じようなルート設定オブジェクトの配列です。したがって、ネストしている view を必要なだけ保持することができます。 ここまでの点では、上記の設定で `/user/foo` に訪れた時に `User` アウトレット内部で何も描画されません。なぜならば、サブルートにマッチしていないからです。そこに何か描画したい場合は、空のサブルートパスを設定できます。 -``` js +```js const router = new VueRouter({ routes: [ { - path: '/user/:id', component: User, + path: '/user/:id', + component: User, children: [ // /user/:id がマッチした時に // UserHome は User の 内部で描画されます - { path: '', component: UserHome }, + { path: '', component: UserHome } // 他のサブルートも同様に... ] diff --git a/docs/ja/guide/essentials/passing-props.md b/docs/ja/guide/essentials/passing-props.md index 11d35aa5b..301bf083d 100644 --- a/docs/ja/guide/essentials/passing-props.md +++ b/docs/ja/guide/essentials/passing-props.md @@ -1,25 +1,25 @@ # ルートコンポーネントにプロパティを渡す -コンポーネントで `$route` を使うとコンポーネントとルートの間に密結合が生まれ、コンポーネントが特定のURLでしか使用できないなど柔軟性が制限されます。 + + +コンポーネントで `$route` を使うとコンポーネントとルートの間に密結合が生まれ、コンポーネントが特定の URL でしか使用できないなど柔軟性が制限されます。 コンポーネントをルーターから分離するために `props` オプションを使います: **`$route` に結合** -``` js +```js const User = { template: '
    User {{ $route.params.id }}
    ' } const router = new VueRouter({ - routes: [ - { path: '/user/:id', component: User } - ] + routes: [{ path: '/user/:id', component: User }] }) ``` **`props` による分離** -``` js +```js const User = { props: ['id'], template: '
    User {{ id }}
    ' @@ -48,10 +48,14 @@ const router = new VueRouter({ `props` がオブジェクトの場合、これはコンポーネントプロパティとしてそのまま設定されます。プロパティが静的なときに便利です。 -``` js +```js const router = new VueRouter({ routes: [ - { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } } + { + path: '/promotion/from-newsletter', + component: Promotion, + props: { newsletterPopup: false } + } ] }) ``` @@ -60,10 +64,14 @@ const router = new VueRouter({ プロパティを返す関数を作成することができます。これにより、パラメータを他のタイプにキャストし、静的な値をルートベースの値などと組み合わせることができます。 -``` js +```js const router = new VueRouter({ routes: [ - { path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) } + { + path: '/search', + component: SearchUser, + props: route => ({ query: route.query.q }) + } ] }) ``` diff --git a/docs/ja/guide/essentials/redirect-and-alias.md b/docs/ja/guide/essentials/redirect-and-alias.md index cd182a776..3631c2d08 100644 --- a/docs/ja/guide/essentials/redirect-and-alias.md +++ b/docs/ja/guide/essentials/redirect-and-alias.md @@ -1,41 +1,42 @@ # リダイレクトとエイリアス + + ### リダイレクト `routes` 設定でリダイレクトが可能です。`/a` から `/b` へリダイレクトする例: -``` js +```js const router = new VueRouter({ - routes: [ - { path: '/a', redirect: '/b' } - ] + routes: [{ path: '/a', redirect: '/b' }] }) ``` 名前付きルートに対してリダイレクトすることもできます。 -``` js +```js const router = new VueRouter({ - routes: [ - { path: '/a', redirect: { name: 'foo' }} - ] + routes: [{ path: '/a', redirect: { name: 'foo' } }] }) ``` また、function を使った動的なリダイレクトもできます。 -``` js +```js const router = new VueRouter({ routes: [ - { path: '/a', redirect: to => { - // この function は対象のルートを引数として受け取ります - // ここではリダイレクト先の path もしくは location を返します - }} + { + path: '/a', + redirect: to => { + // この function は対象のルートを引数として受け取ります + // ここではリダイレクト先の path もしくは location を返します + } + } ] }) ``` -[ナビゲーションガード](../advanced/navigation-guards.md)はリダイレクトするルートに提供されず、ターゲット上のみに適用されるということに注意してください。例では、`beforeEnter` または `beforeLeave` ガードを `/a` ルートに追加しても効果がありません。 +[ナビゲーションガード](../advanced/navigation-guards.md)はリダイレクトするルートに提供されず、ターゲット上のみに適用されるということに注意してください。例では、`beforeEnter` ガードを `/a` ルートに追加しても効果がありません。 その他の高度な使い方として、[例](https://github.com/vuejs/vue-router/blob/dev/examples/redirect/app.js) をご参照ください。 @@ -47,11 +48,9 @@ const router = new VueRouter({ 上記はルートの設定で以下のように表現されます。 -``` js +```js const router = new VueRouter({ - routes: [ - { path: '/a', component: A, alias: '/b' } - ] + routes: [{ path: '/a', component: A, alias: '/b' }] }) ``` diff --git a/docs/ja/installation.md b/docs/ja/installation.md index 2db7585bd..f25bc261c 100644 --- a/docs/ja/installation.md +++ b/docs/ja/installation.md @@ -2,10 +2,10 @@ ### 直接ダウンロード / CDN -[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js) +[https://unpkg.com/vue-router@3/dist/vue-router.js](https://unpkg.com/vue-router@3/dist/vue-router.js) -[Unpkg.com](https://unpkg.com) は npm ベースの CDN リンクです。 上記のリンクは常に NPM 上の最新のリリースを指します。 `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js` のような URL を利用することで特定のバージョンやタグを指定することもできます。 +[Unpkg.com](https://unpkg.com) は npm ベースの CDN リンクです。 上記のリンクは常に NPM 上の最新のリリースを指します。 `https://unpkg.com/vue-router@3.0.0/dist/vue-router.js` のような URL を利用することで特定のバージョンやタグを指定することもできます。 Vue の後に `vue-router` を含めると自動的にインストールされます。 @@ -32,6 +32,14 @@ Vue.use(VueRouter) グローバルな script タグを使っている場合は必要ありません。 +## Vue CLI + +[Vue CLI](https://cli.vuejs.org/) を使用している場合、 Vue Router をプラグインで追加することができます。2つのサンプルルートと同様、上記のコードを CLI に生成させることができます。**`App.vue`もまた上書きされるため、** プロジェクト内で以下のコマンドを実行する前にファイルをバックアップしておいてください: + +```sh +vue add router +``` + ### 開発用ビルド もし最新の開発用ビルドを使用したい場合は、GitHub から直接クローンして `vue-router` をご自身でビルドしてください。 diff --git a/docs/kr/README.md b/docs/kr/README.md index 9c44f352b..6b90e1174 100644 --- a/docs/kr/README.md +++ b/docs/kr/README.md @@ -1,8 +1,10 @@ -# 소개 - -:::tip 버전 정보 -TypeScript 사용자는 `vue-router@3.0+`와 `vue@2.5+`를 사용하세요. -::: +--- +home: true +heroImage: /logo.png +actionText: Get Started → +actionLink: /kr/installation.html +footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Morote +--- Vue 라우터는 [Vue.js](http://vuejs.org)의 공식 라우터입니다. Vue.js를 사용한 싱글 페이지 앱을 쉽게 만들 수 있도록 Vue.js의 코어와 긴밀히 통합되어 있습니다. @@ -19,3 +21,5 @@ Vue.js를 사용한 싱글 페이지 앱을 쉽게 만들 수 있도록 Vue.js - 사용자 정의 가능한 스크롤 동작 [시작하기](./guide/) 또는 [예제](https://github.com/vuejs/vue-router/tree/dev/examples)를 참고하세요. ([`README.md`](https://github.com/vuejs/vue-router/)에서 사용법을 확인할 수 있습니다). + + diff --git a/docs/kr/guide/README.md b/docs/kr/guide/README.md index 21cdcd027..3a9cae8cc 100644 --- a/docs/kr/guide/README.md +++ b/docs/kr/guide/README.md @@ -13,8 +13,8 @@ Vue와 Vue 라우터를 이용해 싱글 페이지 앱을 만드는 것은 매 ## HTML ``` html - - + +

    Hello App!

    diff --git a/docs/kr/guide/advanced/lazy-loading.md b/docs/kr/guide/advanced/lazy-loading.md index dba772649..fb82b381c 100644 --- a/docs/kr/guide/advanced/lazy-loading.md +++ b/docs/kr/guide/advanced/lazy-loading.md @@ -6,39 +6,40 @@ Vue의 [비동기 컴포넌트](http://vuejs.org/guide/components.html#Async-Com 첫째, 비동기 컴포넌트는 Promise를 반환하는 팩토리 함수로 정의할 수 있습니다 (컴포넌트가 resolve 되어야함). -``` js -const Foo = () => Promise.resolve({ /* 컴포넌트 정의 */ }) +```js +const Foo = () => + Promise.resolve({ + /* 컴포넌트 정의 */ + }) ``` 둘째, Webpack 2에서 [dynamic import](https://github.com/tc39/proposal-dynamic-import)를 사용하여 코드 분할 포인트를 지정할 수 있습니다. -``` js +```js import('./Foo.vue') // returns a Promise - ``` +``` - > 참고: Babel을 사용하고 있는 경우 올바른 구문 분석을 위해 [syntax-dynamic-import](http://babeljs.io/docs/plugins/syntax-dynamic-import/) 플러그인을 추가해야합니다. +> 참고: Babel을 사용하고 있는 경우 올바른 구문 분석을 위해 [syntax-dynamic-import](http://babeljs.io/docs/plugins/syntax-dynamic-import/) 플러그인을 추가해야합니다. - 이 두 가지를 결합하여 Webpack에 의해 자동으로 코드 분할될 비동기 컴포넌트를 정의하는 방법입니다. +이 두 가지를 결합하여 Webpack에 의해 자동으로 코드 분할될 비동기 컴포넌트를 정의하는 방법입니다. - ``` js - const Foo = () => import('./Foo.vue') +```js +const Foo = () => import('./Foo.vue') ``` 라우트 설정에서 아무것도 바꿀 필요가 없습니다. `Foo`만 사용하면 됩니다. -``` js +```js const router = new VueRouter({ - routes: [ - { path: '/foo', component: Foo } - ] + routes: [{ path: '/foo', component: Foo }] }) ``` ### 같은 묶음으로 컴포넌트 그룹화하기 -때로는 동일한 라우트 아래에 중첩된 모든 컴포넌트를 동일한 비동기 묶음으로 그룹화 할 수 있습니다. 이를 위해 특수 주석 문법을 사용하는 이름(Webpack 2.4 이상 지원)을 제공하여 [이름을 가진 묶음](https://webpack.js.org/guides/code-splitting-async/#chunk-names)을 사용해야합니다. +때로는 동일한 라우트 아래에 중첩된 모든 컴포넌트를 동일한 비동기 묶음으로 그룹화 할 수 있습니다. 이를 위해 특수 주석 문법을 사용하는 이름(Webpack 2.4 이상 지원)을 제공하여 [이름을 가진 묶음](https://webpack.js.org/api/module-methods/#magic-comments)을 사용해야합니다. -``` js +```js const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') diff --git a/docs/kr/guide/essentials/getting-started.md b/docs/kr/guide/essentials/getting-started.md index 4e6ca1d61..91919aa1b 100644 --- a/docs/kr/guide/essentials/getting-started.md +++ b/docs/kr/guide/essentials/getting-started.md @@ -9,8 +9,8 @@ Vue.js와 vue-router로 단일 페이지 애플리케이션을 만드는 것은 ### HTML ``` html - - + +

    Hello App!

    diff --git a/docs/kr/guide/essentials/history-mode.md b/docs/kr/guide/essentials/history-mode.md index 8645c45e1..5b71b53d3 100644 --- a/docs/kr/guide/essentials/history-mode.md +++ b/docs/kr/guide/essentials/history-mode.md @@ -4,7 +4,7 @@ 해시를 제거하기 위해 라우터의 **history 모드** 를 사용할 수 있습니다. `history.pushState` API를 활용하여 페이지를 다시 로드하지 않고도 URL 탐색을 할 수 있습니다. -``` js +```js const router = new VueRouter({ mode: 'history', routes: [...] @@ -22,6 +22,9 @@ const router = new VueRouter({ #### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / @@ -43,28 +46,29 @@ location / { #### Native Node.js ```js -const http = require("http") -const fs = require("fs") +const http = require('http') +const fs = require('fs') const httpPort = 80 -http.createServer((req, res) => { - fs.readFile("index.htm", "utf-8", (err, content) => { - if (err) { - console.log('We cannot open "index.htm" file.') - } +http + .createServer((req, res) => { + fs.readFile('index.html', 'utf-8', (err, content) => { + if (err) { + console.log('We cannot open "index.html" file.') + } - res.writeHead(200, { - "Content-Type": "text/html; charset=utf-8" - }) + res.writeHead(200, { + 'Content-Type': 'text/html; charset=utf-8' + }) - res.end(content) + res.end(content) + }) + }) + .listen(httpPort, () => { + console.log('Server listening on: http://localhost:%s', httpPort) }) -}).listen(httpPort, () => { - console.log("Server listening on: http://localhost:%s", httpPort) -}) ``` - #### Express와 Node.js Node.js/Express의 경우 [connect-history-api-fallback 미들웨어](https://github.com/bripkens/connect-history-api-fallback)를 고려해보세요. @@ -87,10 +91,10 @@ Node.js/Express의 경우 [connect-history-api-fallback 미들웨어](https://gi - - + + - + @@ -98,18 +102,14 @@ Node.js/Express의 경우 [connect-history-api-fallback 미들웨어](https://gi ``` - ## 주의 사항 주의 사항이 있습니다. 여러분의 서버는 404 에러를 보고하지 않을 것입니다. 왜냐하면 모든 발견되지 않은 경로가 이제 `index.html` 파일을 제공하기 때문입니다. 이 문제를 해결하려면 Vue 앱에서 catch-all 라우트를 구현하여 404 페이지를 표시해야합니다. - -``` js +```js const router = new VueRouter({ mode: 'history', - routes: [ - { path: '*', component: NotFoundComponent } - ] + routes: [{ path: '*', component: NotFoundComponent }] }) ``` diff --git a/docs/kr/guide/essentials/passing-props.md b/docs/kr/guide/essentials/passing-props.md index f4c8379ee..0188fd3f2 100644 --- a/docs/kr/guide/essentials/passing-props.md +++ b/docs/kr/guide/essentials/passing-props.md @@ -4,7 +4,7 @@ 컴포넌트와 라우터 속성을 분리하려면 다음과 같이 하십시오. -** $route에 의존성 추가** +**$route에 의존성 추가** ``` js const User = { @@ -17,7 +17,7 @@ const router = new VueRouter({ }) ``` -** 속성에 의존성 해제** +**속성에 의존성 해제** ``` js const User = { diff --git a/docs/kr/installation.md b/docs/kr/installation.md index 5187b078d..1def91f0c 100644 --- a/docs/kr/installation.md +++ b/docs/kr/installation.md @@ -2,10 +2,10 @@ ### 직접 다운로드 / CDN -[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js) +[https://unpkg.com/vue-router@3/dist/vue-router.js](https://unpkg.com/vue-router@3/dist/vue-router.js) -[Unpkg.com](https://unpkg.com)은 NPM 기반 CDN 링크를 제공합니다. 위의 링크는 항상 NPM의 최신 릴리스를 가리킵니다. `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js`와 같이 URL을 통해 특정 버전 / 태그를 사용할 수도 있습니다. +[Unpkg.com](https://unpkg.com)은 NPM 기반 CDN 링크를 제공합니다. 위의 링크는 항상 NPM의 최신 릴리스를 가리킵니다. `https://unpkg.com/vue-router@3.0.0/dist/vue-router.js`와 같이 URL을 통해 특정 버전 / 태그를 사용할 수도 있습니다. Vue 다음에 `vue-router`를 포함하면 자동으로 설치됩니다. diff --git a/docs/ru/README.md b/docs/ru/README.md index 93ab8691e..c21e5cb0c 100644 --- a/docs/ru/README.md +++ b/docs/ru/README.md @@ -1,8 +1,10 @@ -# Введение - -:::tip ПРИМЕЧАНИЕ К ВЕРСИИ -Для пользователей TypeScript, `vue-router@3.0+` требуется `vue@2.5+`, и наоборот. -::: +--- +home: true +heroImage: /logo.png +actionText: Начать знакомство → +actionLink: /ru/installation.html +footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Morote +--- Vue Router — официальная библиотека маршрутизации для [Vue.js](https://ru.vuejs.org/). Она глубоко интегрируется с Vue.js и позволяет легко создавать SPA-приложения. Включает следующие возможности: @@ -16,3 +18,5 @@ Vue Router — официальная библиотека маршрутиза - Настраиваемое поведение прокрутки страницы [Начать знакомство](./guide/) или поиграться с [примерами](https://github.com/vuejs/vue-router/tree/dev/examples) (см. [`README.md`](https://github.com/vuejs/vue-router/) для их запуска). + + diff --git a/docs/ru/api/README.md b/docs/ru/api/README.md index aec352514..6a00d2531 100644 --- a/docs/ru/api/README.md +++ b/docs/ru/api/README.md @@ -23,6 +23,7 @@ sidebar: auto ```html @@ -44,6 +45,7 @@ sidebar: auto ```html
  • ` будет получать класс активности, когда текущий путь начинается с `/a/` или `/a`. + Стандартное поведение сопоставления, когда выставляется активный класс, основывается на **совпадениях по включению**. Например, `` будет получать класс активности и когда текущий путь начинается с `/a/` и когда с `/a`. Обратите внимание, поэтому `` будет активным для каждого маршрута! Для «режима точного соответствия» укажите в ссылке входной параметр `exact`: @@ -149,6 +151,27 @@ sidebar: auto Ознакомьтесь с другими примерами активных классов ссылок [вживую](https://jsfiddle.net/8xrk1n9f/). +### exact-path + +> Добавлено в версии 3.5.0 + +- тип: `boolean` +- по умолчанию: `false` + + Позволяет использовать сопоставление только на секции `path` в URL, позволяя эффективно игнорировать секции `query` и `hash`. + + ```html + + + ``` + +### exact-path-active-class + +- тип: `string` +- по умолчанию: `"router-link-exact-path-active"` + + Указание активного CSS класса, который применяется когда ссылка активна по сопоставлению `path`. Обратите внимание, что значение по умолчанию можно задать глобально в опции `linkExactPathActiveClass` конструктора маршрутизатора. + ### event - тип: `string | Array` @@ -163,6 +186,13 @@ sidebar: auto Указание активного CSS класса, который применяется когда ссылка активна с точным соответствием пути. Обратите внимание, что значение по умолчанию можно задать глобально в опции `linkExactActiveClass` конструктора маршрутизатора. +### aria-current-value + +- тип: `'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false'` +- по умолчанию: `"page"` + + Настройка значения `aria-current` когда ссылка активна по точному (exact) совпадению. Это должно быть одно из [разрешённых значений для aria-current](https://www.w3.org/TR/wai-aria-1.2/#aria-current) спецификации ARIA. В большинстве случаев наиболее подходящим значением будет `page`. + ## `` Функциональный компонент `` отображает компонент соответствующий данному маршруту. Компоненты внутри `` также могут содержать в шаблоне собственный `` (он будет использован для отображения компонентов вложенных маршрутов). @@ -261,11 +291,11 @@ sidebar: auto Сигнатура: - ``` + ```ts type PositionDescriptor = { x: number, y: number } | { selector: string } | - ?{} + void type scrollBehaviorHandler = ( to: Route, @@ -312,6 +342,26 @@ sidebar: auto Текущий маршрут в виде [объекта Route](#объект-route). +### router.START_LOCATION + +- тип: `Route` + + Первоначальная навигация будет [объектом Route](#объект-route) с которого запускается маршрутизатор. Можно использовать в навигационных хуках для определения стартовой навигации. + + ```js + import VueRouter from 'vue-router' + + const router = new VueRouter({ + // ... + }) + + router.beforeEach((to, from) => { + if (from === VueRouter.START_LOCATION) { + // первоначальная навигация + } + }) + ``` + ## Методы экземпляра Router ### router.beforeEach @@ -391,14 +441,52 @@ const resolved: { ### router.addRoutes +_УСТАРЕВШИЙ_: используйте вместо метод [`router.addRoute()`](#router-addroute). + Сигнатура: -```js +```ts router.addRoutes(routes: Array) ``` Динамически добавляет дополнительные маршруты в маршрутизатор. Аргумент должен быть массивом маршрутов в таком же формате, как и в опции `routes` конструктора. +### router.addRoute + +> Добавлено в версии 3.5.0 + +Добавляет новый маршрут в маршрутизатор. Если у маршрута указан `name` и уже существует маршрут с таким же именем, то он будет перезаписан. + +Сигнатура: + +```ts +addRoute(route: RouteConfig): () => void +``` + +### router.addRoute + +> Добавлено в версии 3.5.0 + +Добавляет новый маршрут в качестве дочернего для существующего маршрута. Если у маршрута указан `name` и уже существует маршрут с таким же именем, то он будет перезаписан. + +Сигнатура: + +```ts +addRoute(parentName: string, route: RouteConfig): () => void +``` + +### router.getRoutes + +> Добавлено в версии 3.5.0 + +Получение списка записей всех активных маршрутов. **Обратите внимание, что только задокументированные свойства считаются публичным API**, поэтому следует избегать использования любых других свойств, например `regex`, так как их уже не будет в Vue Router 4. + +Сигнатура: + +```ts +getRoutes(): RouteRecord[] +``` + ### router.onReady Сигнатура: @@ -481,6 +569,12 @@ router.onError(callback) Объект, который содержит пары ключ/значение строки запроса (query string). Например, для пути `/foo?user=1` получим `$route.query.user == 1`. Если строки запроса нет, то значением будет пустой объект. +- **\$route.meta** + + - тип: `Object` + + Объект, который содержит пары ключ/значение объекта meta для маршрута. Если у объекте meta нет свойств, то значением будет пустой объект. + - **\$route.hash** - тип: `string` diff --git a/docs/ru/guide/README.md b/docs/ru/guide/README.md index bf767f17a..cf31e84d5 100644 --- a/docs/ru/guide/README.md +++ b/docs/ru/guide/README.md @@ -6,13 +6,13 @@ Кроме того, все примеры будут использовать полную сборку Vue, чтобы позволить компиляцию шаблонов на лету. Подробнее о различиях сборок читайте [здесь](https://ru.vuejs.org/v2/guide/installation.html#Runtime-Компилятор-vs-Runtime-only). ::: -Создавать одностраничные приложения (SPA) используя Vue + Vue Router очень просто. С помощью Vue.js, мы уже компонуем своё приложение из компонентов. Добавляя Vue Router, мы просто сопоставляем наши компоненты с маршрутами и объясняем Vue Router где их отображать. Вот простой пример: +Создавать одностраничные приложения (SPA) используя Vue + Vue Router очень просто: с помощью Vue.js, мы уже компонуем своё приложение из компонентов. Добавляя Vue Router, мы просто сопоставляем наши компоненты с маршрутами и объясняем Vue Router где их отображать. Вот простой пример: ## HTML ```html - - + +

    Первое приложение!

    diff --git a/docs/ru/guide/advanced/data-fetching.md b/docs/ru/guide/advanced/data-fetching.md index c0582cd04..cec068029 100644 --- a/docs/ru/guide/advanced/data-fetching.md +++ b/docs/ru/guide/advanced/data-fetching.md @@ -81,14 +81,14 @@ export default { error: null } }, - beforeRouteEnter (to, from, next) { + beforeRouteEnter(to, from, next) { getPost(to.params.id, (err, post) => { next(vm => vm.setData(err, post)) }) }, // если путь изменяется, а компонент уже отображён, // то логика будет немного иной - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { this.post = null getPost(to.params.id, (err, post) => { this.setData(err, post) diff --git a/docs/ru/guide/advanced/lazy-loading.md b/docs/ru/guide/advanced/lazy-loading.md index b256c762c..ebaeffc1b 100644 --- a/docs/ru/guide/advanced/lazy-loading.md +++ b/docs/ru/guide/advanced/lazy-loading.md @@ -7,7 +7,10 @@ Во-первых, асинхронный компонент можно определить как функцию-фабрику, которая возвращает Promise (который должен разрешиться самим компонентом): ```js -const Foo = () => Promise.resolve({ /* определение компонента */ }) +const Foo = () => + Promise.resolve({ + /* определение компонента */ + }) ``` Во-вторых, с Webpack 2 мы можем использовать синтаксис [динамических импортов](https://github.com/tc39/proposal-dynamic-import) для указания точек разделения кода: @@ -30,15 +33,13 @@ const Foo = () => import('./Foo.vue') ```js const router = new VueRouter({ - routes: [ - { path: '/foo', component: Foo } - ] + routes: [{ path: '/foo', component: Foo }] }) ``` ## Группировка компонентов в одном фрагменте -Иногда может понадобиться объединить в одном фрагменте все компоненты, расположенные по определённому маршруту. Для этого можно указывать [имена фрагментов Webpack](https://webpack.js.org/guides/code-splitting-async/#chunk-names), используя специальный синтаксис комментариев (в версиях Webpack > 2.4): +Иногда может понадобиться объединить в одном фрагменте все компоненты, расположенные по определённому маршруту. Для этого можно указывать [имена фрагментов Webpack](https://webpack.js.org/api/module-methods/#magic-comments), используя специальный синтаксис комментариев (в версиях Webpack > 2.4): ```js const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') diff --git a/docs/ru/guide/advanced/meta.md b/docs/ru/guide/advanced/meta.md index 18eb2430a..298789a59 100644 --- a/docs/ru/guide/advanced/meta.md +++ b/docs/ru/guide/advanced/meta.md @@ -1,6 +1,6 @@ # Метаданные маршрутов -Вы можете добавить поле `meta` при определении маршрута: +Иногда может быть удобным добавить дополнительную информацию к маршрутам, например имена анимаций переходов, кто может получить доступ к маршруту и т.д. Этого можно достичь с помощью свойства `meta`, которое принимает объект свойств и к которому можно получить доступ на странице маршрута или в навигационных хуках. Свойства `meta` можно объявить так: ```js const router = new VueRouter({ diff --git a/docs/ru/guide/advanced/navigation-failures.md b/docs/ru/guide/advanced/navigation-failures.md new file mode 100644 index 000000000..b7bcf9a7f --- /dev/null +++ b/docs/ru/guide/advanced/navigation-failures.md @@ -0,0 +1,65 @@ +# Сбои при навигации + +> Добавлено в версии 3.4.0 + +При использовании `router-link` Vue Router вызывает `router.push` для запуска навигации. В большинстве случаев ожидаемое поведение ссылок заключается в переходе на новую страницу, но есть несколько ситуаций при которых пользователь остаётся на той же странице: + +- Пользователь уже находится на странице, на которую пытается перейти +- [Навигационный хук](./navigation-guards.md) прерывает навигацию вызовом `next(false)` +- [Навигационный хук](./navigation-guards.md) выбрасывает ошибку или вызывает `next(new Error())` + +При использовании компонента `router-link` **ни один из этих случаев не будет логироваться как ошибка**. Однако при использовании `router.push` или `router.replace` можно столкнуться с сообщением _"Uncaught (in promise) Error"_ после которого в консоли последует более конкретное сообщение. Давайте разберемся как отличать _сбои навигации_. + +::: tip История вопроса +В версии 3.2.0, _навигационные сбои_ доступны через два необязательных коллбэка `router.push`: `onComplete` и `onAbort`. Начиная с версии 3.1.0, `router.push` и `router.replace` возвращают _Promise_ если не указаны коллбэки `onComplete`/`onAbort`. Этот _Promise_ разрешается вместо вызова `onComplete` и отклоняется вместо вызова `onAbort`. + ::: + +## Обнаружение сбоев навигации + +_Сбой навигации_ будет экземпляром `Error` с парой дополнительных свойств. Проверить произошла ли ошибка в маршрутизаторе можно с помощью функции `isNavigationFailure`: + +```js +import VueRouter from 'vue-router' +const { isNavigationFailure, NavigationFailureType } = VueRouter + +// попытка перехода к странице администрирования +router + .push('/admin') + .catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + // отображение уведомления пользователю + showToast('Необходимо авторизоваться для доступа к панели администрирования') + } + }) +``` + +::: tip СОВЕТ +Если опустить второй параметр в `isNavigationFailure(failure)`, то будет проверяться только, является ли ошибка _сбоем навигации_. +::: + +## Тип `NavigationFailureType` + +Тип `NavigationFailureType` поможет разработчикам определять тип _навигационного сбоя_. Существует 4 различных типа: + +- `redirected`: внутри навигационного хука был вызван `next(newLocation)` для перенаправления в другое место. +- `aborted`: внутри навигационного хука был вызван `next(false)` для отмены навигации. +- `cancelled`: произошла полностью новая навигация до того, как текущая могла закончиться. Например, во время ожидания внутри навигационного хука был вызван `router.push`. +- `duplicated`: навигация была предотвращена, потому что уже находимся в месте назначения. + +## Свойства _ошибок навигации_ + +Все сбои навигации предоставляют доступ к свойствам `to` и `from`, отображающие для навигации в которой произошёл сбой местоположение места назначения и текущее местоположение соответственно: + +```js +// попытка получения доступа к странице администрирования +router + .push('/admin') + .catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + console.log(failure.to.path) // '/admin' + console.log(failure.from.path) // '/' + } + }) +``` + +Во всех случаях значения `to` и `from` будут объектами нормализованных маршрутов. diff --git a/docs/ru/guide/advanced/navigation-guards.md b/docs/ru/guide/advanced/navigation-guards.md index e5555d2e3..a1a380237 100644 --- a/docs/ru/guide/advanced/navigation-guards.md +++ b/docs/ru/guide/advanced/navigation-guards.md @@ -39,7 +39,7 @@ router.beforeEach((to, from, next) => { ```js // ПЛОХО router.beforeEach((to, from, next) => { - if (!isAuthenticated) next('/login') + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) // если пользователь не авторизован, то `next` будет вызываться дважды next() }) @@ -48,7 +48,7 @@ router.beforeEach((to, from, next) => { ```js // ХОРОШО router.beforeEach((to, from, next) => { - if (!isAuthenticated) next('/login') + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) else next() }) ``` @@ -98,12 +98,12 @@ const router = new VueRouter({ ```js const Foo = { template: `...`, - beforeRouteEnter (to, from, next) { + beforeRouteEnter(to, from, next) { // вызывается до подтверждения пути, соответствующего этому компоненту. // НЕ ИМЕЕТ доступа к контексту экземпляра компонента `this`, // так как к моменту вызова экземпляр ещё не создан! }, - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { // вызывается когда маршрут, что рендерит этот компонент изменился, // но этот компонент будет повторно использован в новом маршруте. // Например, для маршрута с динамическими параметрами `/foo/:id`, когда мы @@ -111,7 +111,7 @@ const Foo = { // будет использован повторно, и этот хук будет вызван когда это случится. // Также имеется доступ в `this` к экземпляру компонента. }, - beforeRouteLeave (to, from, next) { + beforeRouteLeave(to, from, next) { // вызывается перед переходом от пути, соответствующего текущему компоненту; // имеет доступ к контексту экземпляра компонента `this`. } @@ -123,17 +123,17 @@ const Foo = { Тем не менее, доступ к экземпляру можно получить, передав коллбэк в `next`. Эта функция будет вызвана после подтверждения навигации, а экземпляр компонента будет передан в неё в качестве параметра: ```js -beforeRouteEnter (to, from, next) { +beforeRouteEnter(to, from, next) { next(vm => { // экземпляр компонента доступен как `vm` }) } ``` -Обратите внимание, что `beforeRouteEnter` — единственный хук, который поддерживает передачу коллбэка в `next`. Для `beforeRouteUpdate` и `beforeRouteLeave`, `this` уже доступен, поэтому передача коллбэка не требуется и поэтому *не поддерживается*: +Обратите внимание, что `beforeRouteEnter` — единственный хук, который поддерживает передачу коллбэка в `next`. Для `beforeRouteUpdate` и `beforeRouteLeave`, `this` уже доступен, поэтому передача коллбэка не требуется и поэтому _не поддерживается_: ```js -beforeRouteUpdate (to, from, next) { +beforeRouteUpdate(to, from, next) { // просто используйте `this` this.name = to.params.name next() @@ -143,7 +143,7 @@ beforeRouteUpdate (to, from, next) { **Навигационный хук ухода со страницы** обычно используется для предотвращения случайного ухода пользователя со страницы с несохранёнными изменениями. Навигацию можно отменить вызовом `next(false)`. ```js -beforeRouteLeave (to, from, next) { +beforeRouteLeave(to, from, next) { const answer = window.confirm('Вы хотите уйти? У вас есть несохранённые изменения!') if (answer) { next() @@ -153,6 +153,18 @@ beforeRouteLeave (to, from, next) { } ``` +При использовании примесей, которые будут добавлять навигационные хуки для компонента, убедитесь, что объявляете примесь **после установки плагина vue-router**: + +```js +Vue.use(Router) + +Vue.mixin({ + beforeRouteUpdate(to, from, next) { + // ... + } +}) +``` + ## Полная цепочка обработки навигации 1. Срабатывание навигации. diff --git a/docs/ru/guide/advanced/scroll-behavior.md b/docs/ru/guide/advanced/scroll-behavior.md index 87ccac72f..7b1d8957c 100644 --- a/docs/ru/guide/advanced/scroll-behavior.md +++ b/docs/ru/guide/advanced/scroll-behavior.md @@ -9,7 +9,7 @@ ```js const router = new VueRouter({ routes: [...], - scrollBehavior (to, from, savedPosition) { + scrollBehavior(to, from, savedPosition) { // возвращаем требуемую позицию прокрутки } }) @@ -27,7 +27,7 @@ const router = new VueRouter({ Например: ```js -scrollBehavior (to, from, savedPosition) { +scrollBehavior(to, from, savedPosition) { return { x: 0, y: 0 } } ``` @@ -37,7 +37,7 @@ scrollBehavior (to, from, savedPosition) { Возврат `savedPosition` позволяет эмулировать нативное поведение браузера при использовании кнопок назад/вперёд: ```js -scrollBehavior (to, from, savedPosition) { +scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition } else { @@ -49,7 +49,7 @@ scrollBehavior (to, from, savedPosition) { Эмулировать поведение "прокрутки к якорю" на странице можно так: ```js -scrollBehavior (to, from, savedPosition) { +scrollBehavior(to, from, savedPosition) { if (to.hash) { return { selector: to.hash @@ -68,7 +68,7 @@ scrollBehavior (to, from, savedPosition) { Можно также вернуть Promise, который разрешится объектом с желаемой позицией прокрутки: ```js -scrollBehavior (to, from, savedPosition) { +scrollBehavior(to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ x: 0, y: 0 }) @@ -78,3 +78,18 @@ scrollBehavior (to, from, savedPosition) { ``` Это можно связать с событиями компонента transition на уровне страницы, чтобы реализовать такое поведение прокрутки, которое сочетается с анимациями перехода между страницами, но из-за множества возможных вариантов и комплексности примеров, мы просто предоставляем этот простой пример, чтобы показать где можно разместить собственную реализацию. + +## Плавная прокрутка + +Можно включить нативную плавную прокрутку для [браузеров, которые поддерживают её](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior), просто добавив опцию `behavior` к объекту, возвращаемому из `scrollBehavior`: + +```js +scrollBehavior(to, from, savedPosition) { + if (to.hash) { + return { + selector: to.hash, + behavior: 'smooth', + } + } +} +``` diff --git a/docs/ru/guide/essentials/dynamic-matching.md b/docs/ru/guide/essentials/dynamic-matching.md index 3e86a5925..ad7c5bcba 100644 --- a/docs/ru/guide/essentials/dynamic-matching.md +++ b/docs/ru/guide/essentials/dynamic-matching.md @@ -29,9 +29,9 @@ const User = { Может быть несколько динамических сегментов в одном маршруте. Для каждого сегмента появится соответствующее свойство в `$route.params`. Например: -| Шаблон | Совпадающий путь | $route.params | -|---------|------|--------| -| /user/:username | /user/evan | `{ username: 'evan' }` | +| Шаблон | Совпадающий путь | $route.params | +| ----------------------------- | ------------------- | -------------------------------------- | +| /user/:username | /user/evan | `{ username: 'evan' }` | | /user/:username/post/:post_id | /user/evan/post/123 | `{ username: 'evan', post_id: '123' }` | Кроме `$route.params`, объект `$route` предоставляют и другую полезную информацию, например `$route.query` (если URL содержит строку запроса), `$route.hash`, и т.д. Подробнее в [справочнике API](../../api/#объект-route). @@ -58,7 +58,7 @@ const User = { ```js const User = { template: '...', - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { // обрабатываем изменение параметров маршрута... // не забываем вызвать next() } diff --git a/docs/ru/guide/essentials/history-mode.md b/docs/ru/guide/essentials/history-mode.md index 0f42bf028..289f08b78 100644 --- a/docs/ru/guide/essentials/history-mode.md +++ b/docs/ru/guide/essentials/history-mode.md @@ -24,6 +24,9 @@ const router = new VueRouter({ #### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / @@ -52,9 +55,9 @@ const fs = require('fs') const httpPort = 80 http.createServer((req, res) => { - fs.readFile('index.htm', 'utf-8', (err, content) => { + fs.readFile('index.html', 'utf-8', (err, content) => { if (err) { -      console.log('Невозможно открыть файл "index.htm".') +      console.log('Невозможно открыть файл "index.html".') } res.writeHead(200, { diff --git a/docs/ru/guide/essentials/navigation.md b/docs/ru/guide/essentials/navigation.md index f7e294b88..1056b9dce 100644 --- a/docs/ru/guide/essentials/navigation.md +++ b/docs/ru/guide/essentials/navigation.md @@ -15,7 +15,7 @@ sidebarDepth: 0 При клике на `` этот метод вызывается автоматически. Клик по `` эквивалентен программному вызову `router.push(...)`. | Декларативная запись | Программная запись | -|---------------------------|--------------------| +| ------------------------- | ------------------ | | `` | `router.push(...)` | В качестве аргумента можно передать строку или объект, описывающий маршрут. Например: @@ -57,7 +57,7 @@ router.push({ path: '/user', params: { userId } }) // -> /user Действует как `router.push`, с той лишь разницей, что переход осуществляется без добавления новой записи в историю навигации, а заменяет текущую запись в нём. | Декларативная запись | Программная запись | -|-----------------------------------|-----------------------| +| --------------------------------- | --------------------- | | `` | `router.replace(...)` | ## `router.go(n)` diff --git a/docs/ru/guide/essentials/nested-routes.md b/docs/ru/guide/essentials/nested-routes.md index efc89452b..9423b3074 100644 --- a/docs/ru/guide/essentials/nested-routes.md +++ b/docs/ru/guide/essentials/nested-routes.md @@ -53,7 +53,9 @@ const User = { ```js const router = new VueRouter({ routes: [ - { path: '/user/:id', component: User, + { + path: '/user/:id', + component: User, children: [ { // при совпадении пути с шаблоном /user/:id/profile @@ -83,11 +85,12 @@ const router = new VueRouter({ const router = new VueRouter({ routes: [ { - path: '/user/:id', component: User, + path: '/user/:id', + component: User, children: [ // при совпадении пути с шаблоном /user/:id // в компонента User будет показан UserHome - { path: '', component: UserHome }, + { path: '', component: UserHome } // ...остальные вложенные маршруты ] diff --git a/docs/ru/installation.md b/docs/ru/installation.md index 58c82ceb5..5ac66914f 100644 --- a/docs/ru/installation.md +++ b/docs/ru/installation.md @@ -2,10 +2,10 @@ ## Скачивание напрямую / CDN -[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js) +[https://unpkg.com/vue-router@3/dist/vue-router.js](https://unpkg.com/vue-router@3/dist/vue-router.js) -[Unpkg.com](https://unpkg.com) предоставляет CDN-ссылки для NPM-пакетов. Ссылка выше всегда указывает на самую последнюю версию Vue-router на NPM. Вы можете также использовать конкретную версию, используя ссылки вида `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js`. +[Unpkg.com](https://unpkg.com) предоставляет CDN-ссылки для NPM-пакетов. Ссылка выше всегда указывает на самую последнюю версию Vue-router на NPM. Вы можете также использовать конкретную версию, используя ссылки вида `https://unpkg.com/vue-router@3.0.0/dist/vue-router.js`. Подключите `vue-router` после Vue, и установка произойдёт автоматически: @@ -32,6 +32,14 @@ Vue.use(VueRouter) Это не требуется при подключении через глобальный тег `script`. +## Vue CLI + +Если проект развернут с использованием [Vue CLI](https://cli.vuejs.org/ru/), то можно добавить Vue Router в качестве плагина. Это позволит CLI сгенерировать код подключения, приведённый выше, а также добавить два маршрута для примера. **Операция установки перезапишет `App.vue`** в проекте, поэтому убедитесь что сделали резервную копию перед запуском команды: + +```sh +vue add router +``` + ## Версия для разработки Если вы хотите использовать самую новую dev-сборку `vue-router`, то придётся вручную склонировать репозиторий с GitHub и запустить сборку: diff --git a/docs/zh/README.md b/docs/zh/README.md index d790fdb42..394bb9a88 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -1,8 +1,12 @@ -# 介绍 +--- +home: true +heroImage: /logo.png +actionText: Get Started → +actionLink: /zh/installation.html +footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Morote +--- -:::tip 版本说明 -对于 TypeScript 用户来说,`vue-router@3.0+` 依赖 `vue@2.5+`,反之亦然。 -::: + Vue Router 是 [Vue.js](http://cn.vuejs.org) 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有: @@ -16,3 +20,5 @@ Vue Router 是 [Vue.js](http://cn.vuejs.org) 官方的路由管理器。它和 V - 自定义的滚动条行为 现在开始[起步](./guide/)或尝试一下我们的[示例](https://github.com/vuejs/vue-router/tree/dev/examples)吧 (查看仓库的 [`README.md`](https://github.com/vuejs/vue-router/) 来运行它们)。 + + diff --git a/docs/zh/api/README.md b/docs/zh/api/README.md index 3cd404cc0..61540b45c 100644 --- a/docs/zh/api/README.md +++ b/docs/zh/api/README.md @@ -17,13 +17,14 @@ sidebar: auto ### `v-slot` API (3.1.0 新增) -`router-link` 通过一个[作用域插槽](https://cn.vuejs.org/v2/guide/components-slots.html#作用域插槽)暴露底层的定制能力。这是一个更高阶的 API,主要面向库作者,但也可以为开发者提供便利,多数情况用在一个类似 _NavLink_ 这样的组件里。 +`router-link` 通过一个[作用域插槽](https://cn.vuejs.org/v2/guide/components-slots.html#作用域插槽)暴露底层的定制能力。这是一个更高阶的 API,主要面向库作者,但也可以为开发者提供便利,多数情况用在一个类似 _NavLink_ 这样的自定义组件里。 **在使用 `v-slot` API 时,需要向 `router-link` 传入一个单独的子元素**。否则 `router-link` 将会把子元素包裹在一个 `span` 元素内。 ```html
  • ` `` 组件是一个 functional 组件,渲染路径匹配到的视图组件。`` 渲染的组件还可以内嵌自己的 ``,根据嵌套路径,渲染嵌套组件。 @@ -263,11 +272,11 @@ sidebar: auto 签名: - ``` + ```ts type PositionDescriptor = { x: number, y: number } | { selector: string } | - ?{} + void type scrollBehaviorHandler = ( to: Route, @@ -288,6 +297,8 @@ sidebar: auto - 类型: `boolean` +- 默认值: `true` + 当浏览器不支持 `history.pushState` 控制路由是否应该回退到 `hash` 模式。默认值为 `true`。 在 IE9 中,设置为 `false` 会使得每个 `router-link` 导航都触发整页刷新。它可用于工作在 IE9 下的服务端渲染应用,因为一个 hash 模式的 URL 并不支持服务端渲染。 @@ -312,6 +323,26 @@ sidebar: auto 当前路由对应的[路由信息对象](#路由对象)。 +### router.START_LOCATION + +- 类型:`Route` + + 以[路由对象](#路由对象)的格式展示初始路由地址,即路由开始的地方。可用在导航守卫中以区分初始导航。 + + ```js + import VueRouter from 'vue-router' + + const router = new VueRouter({ + // ... + }) + + router.beforeEach((to, from) => { + if (from === VueRouter.START_LOCATION) { + // 初始导航 + } + }) + ``` + ## Router 实例方法 ### router.beforeEach @@ -362,6 +393,8 @@ router.forward() 动态的导航到一个新 URL。参考[编程式导航](../guide/essentials/navigation.md)。 +这些函数仅在安装路由插件并将其传递给 Vue 根实例后调用,如[起步](../guide/README.md)所示。 + ### router.getMatchedComponents 函数签名: @@ -391,6 +424,8 @@ const resolved: { ### router.addRoutes +*已废弃*:使用 [`router.addRoute()`](#router-addroute) 代替。 + 函数签名: ```js @@ -399,6 +434,36 @@ router.addRoutes(routes: Array) 动态添加更多的路由规则。参数必须是一个符合 `routes` 选项要求的数组。 +### router.addRoute + +添加一条新路由规则。如果该路由规则有 `name`,并且已经存在一个与之相同的名字,则会覆盖它。 + +函数签名: + +```ts +addRoute(route: RouteConfig): () => void +``` + +### router.addRoute + +添加一条新的路由规则记录作为现有路由的子路由。如果该路由规则有 `name`,并且已经存在一个与之相同的名字,则会覆盖它。 + +函数签名: + +```ts +addRoute(parentName: string, route: RouteConfig): () => void +``` + +### router.getRoutes + +获取所有活跃的路由记录列表。**注意只有文档中记录下来的 property 才被视为公共 API**,避免使用任何其它 property,例如 `regex`,因为它在 Vue Router 4 中不存在。 + +函数签名: + +```ts +getRoutes(): RouteRecord[] +``` + ### router.onReady 函数签名: diff --git a/docs/zh/guide/README.md b/docs/zh/guide/README.md index b5d2ac53f..f03de3092 100644 --- a/docs/zh/guide/README.md +++ b/docs/zh/guide/README.md @@ -6,13 +6,15 @@ 同时,所有的例子都将使用完整版的 Vue 以解析模板。更多细节请[移步这里](https://cn.vuejs.org/v2/guide/installation.html#运行时-编译器-vs-只包含运行时)。 ::: -用 Vue.js + Vue Router 创建单页应用,是非常简单的。使用 Vue.js ,我们已经可以通过组合组件来组成应用程序,当你要把 Vue Router 添加进来,我们需要做的是,将组件 (components) 映射到路由 (routes),然后告诉 Vue Router 在哪里渲染它们。下面是个基本例子: + + +用 Vue.js + Vue Router 创建单页应用,感觉很自然:使用 Vue.js ,我们已经可以通过组合组件来组成应用程序,当你要把 Vue Router 添加进来,我们需要做的是,将组件 (components) 映射到路由 (routes),然后告诉 Vue Router 在哪里渲染它们。下面是个基本例子: ## HTML ```html - - + +

    Hello App!

    diff --git a/docs/zh/guide/advanced/lazy-loading.md b/docs/zh/guide/advanced/lazy-loading.md index f87191ad7..8d55a1172 100644 --- a/docs/zh/guide/advanced/lazy-loading.md +++ b/docs/zh/guide/advanced/lazy-loading.md @@ -1,18 +1,23 @@ # 路由懒加载 + + 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。 结合 Vue 的[异步组件](https://cn.vuejs.org/v2/guide/components-dynamic-async.html#异步组件)和 Webpack 的[代码分割功能](https://doc.webpack-china.org/guides/code-splitting-async/#require-ensure-/),轻松实现路由组件的懒加载。 首先,可以将异步组件定义为返回一个 Promise 的工厂函数 (该函数返回的 Promise 应该 resolve 组件本身): -``` js -const Foo = () => Promise.resolve({ /* 组件定义对象 */ }) +```js +const Foo = () => + Promise.resolve({ + /* 组件定义对象 */ + }) ``` 第二,在 Webpack 2 中,我们可以使用[动态 import](https://github.com/tc39/proposal-dynamic-import)语法来定义代码分块点 (split point): -``` js +```js import('./Foo.vue') // 返回 Promise ``` @@ -22,17 +27,15 @@ import('./Foo.vue') // 返回 Promise 结合这两者,这就是如何定义一个能够被 Webpack 自动代码分割的异步组件。 -``` js +```js const Foo = () => import('./Foo.vue') ``` 在路由配置中什么都不需要改变,只需要像往常一样使用 `Foo`: -``` js +```js const router = new VueRouter({ - routes: [ - { path: '/foo', component: Foo } - ] + routes: [{ path: '/foo', component: Foo }] }) ``` @@ -40,7 +43,7 @@ const router = new VueRouter({ 有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 [命名 chunk](https://webpack.js.org/guides/code-splitting-require/#chunkname),一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。 -``` js +```js const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') diff --git a/docs/zh/guide/advanced/navigation-failures.md b/docs/zh/guide/advanced/navigation-failures.md new file mode 100644 index 000000000..9f7288b1a --- /dev/null +++ b/docs/zh/guide/advanced/navigation-failures.md @@ -0,0 +1,66 @@ +# 导航故障 + +> 3.4.0中新增 + +::: tip 译者注 +*导航故障*,或者叫*导航失败*,表示一次失败的导航,原文叫 navigation failures,本文统一采用*导航故障*。 +::: + +当使用 `router-link` 组件时,Vue Router 会自动调用 `router.push` 来触发一次导航。 虽然大多数链接的预期行为是将用户导航到一个新页面,但也有少数情况下用户将留在同一页面上: + +- 用户已经位于他们正在尝试导航到的页面 +- 一个[导航守卫](./navigation-guards.md)通过调用 `next(false)` 中断了这次导航 +- 一个[导航守卫](./navigation-guards.md)抛出了一个错误,或者调用了 `next(new Error())` + +当使用 `router-link` 组件时,**这些失败都不会打印出错误**。然而,如果你使用 `router.push` 或者 `router.replace` 的话,可能会在控制台看到一条 _"Uncaught (in promise) Error"_ 这样的错误,后面跟着一条更具体的消息。让我们来了解一下如何区分*导航故障*。 + +::: tip 背景故事 +在 v3.2.0 中,可以通过使用 `router.push` 的两个可选的回调函数:`onComplete` 和 `onAbort` 来暴露*导航故障*。从版本 3.1.0 开始,`router.push` 和 `router.replace` 在没有提供 `onComplete`/`onAbort` 回调的情况下会返回一个 *Promise*。这个 *Promise* 的 resolve 和 reject 将分别用来代替 `onComplete` 和 `onAbort` 的调用。 +::: + + +## 检测导航故障 + +*导航故障*是一个 `Error` 实例,附带了一些额外的属性。要检查一个错误是否来自于路由器,可以使用 `isNavigationFailure` 函数: + +```js +import VueRouter from 'vue-router' +const { isNavigationFailure, NavigationFailureType } = VueRouter + +// 正在尝试访问 admin 页面 +router.push('/admin').catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + // 向用户显示一个小通知 + showToast('Login in order to access the admin panel') + } +}) +``` + +::: tip +如果你忽略第二个参数:`isNavigationFailure(failure)`,那么就只会检查这个错误是不是一个*导航故障*。 +::: + +## `NavigationFailureType` + +`NavigationFailureType` 可以帮助开发者来区分不同类型的*导航故障*。有四种不同的类型: + +- `redirected`:在导航守卫中调用了 `next(newLocation)` 重定向到了其他地方。 +- `aborted`:在导航守卫中调用了 `next(false)` 中断了本次导航。 +- `cancelled`:在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了 `router.push`。 +- `duplicated`:导航被阻止,因为我们已经在目标位置了。 + +## *导航故障*的属性 + +所有的导航故障都会有 `to` 和 `from` 属性,分别用来表达这次失败的导航的目标位置和当前位置。 + +```js +// 正在尝试访问 admin 页面 +router.push('/admin').catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + failure.to.path // '/admin' + failure.from.path // '/' + } +}) +``` + +在所有情况下,`to` 和 `from` 都是规范化的路由位置。 diff --git a/docs/zh/guide/advanced/navigation-guards.md b/docs/zh/guide/advanced/navigation-guards.md index 28ff3908b..f87993d86 100644 --- a/docs/zh/guide/advanced/navigation-guards.md +++ b/docs/zh/guide/advanced/navigation-guards.md @@ -8,11 +8,13 @@ 记住**参数或查询的改变并不会触发进入/离开的导航守卫**。你可以通过[观察 `$route` 对象](../essentials/dynamic-matching.md#响应路由参数的变化)来应对这些变化,或使用 `beforeRouteUpdate` 的组件内守卫。 + + ## 全局前置守卫 你可以使用 `router.beforeEach` 注册一个全局前置守卫: -``` js +```js const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { @@ -38,7 +40,24 @@ router.beforeEach((to, from, next) => { - **`next(error)`**: (2.4.0+) 如果传入 `next` 的参数是一个 `Error` 实例,则导航会被终止且该错误会被传递给 [`router.onError()`](../../api/#router-onerror) 注册过的回调。 -**确保要调用 `next` 方法,否则钩子就不会被 resolved。** +**确保 `next` 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错**。这里有一个在用户未能验证身份时重定向到 `/login` 的示例: + +```js +// BAD +router.beforeEach((to, from, next) => { + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) + // 如果用户未能验证身份,则 `next` 会被调用两次 + next() +}) +``` + +```js +// GOOD +router.beforeEach((to, from, next) => { + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) + else next() +}) +``` ## 全局解析守卫 @@ -50,7 +69,7 @@ router.beforeEach((to, from, next) => { 你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 `next` 函数也不会改变导航本身: -``` js +```js router.afterEach((to, from) => { // ... }) @@ -60,7 +79,7 @@ router.afterEach((to, from) => { 你可以在路由配置上直接定义 `beforeEnter` 守卫: -``` js +```js const router = new VueRouter({ routes: [ { @@ -84,32 +103,32 @@ const router = new VueRouter({ - `beforeRouteUpdate` (2.2 新增) - `beforeRouteLeave` -``` js +```js const Foo = { template: `...`, - beforeRouteEnter (to, from, next) { + beforeRouteEnter(to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例 `this` // 因为当守卫执行前,组件实例还没被创建 }, - beforeRouteUpdate (to, from, next) { -    // 在当前路由改变,但是该组件被复用时调用 + beforeRouteUpdate(to, from, next) { + // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` }, - beforeRouteLeave (to, from, next) { + beforeRouteLeave(to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` } } ``` -`beforeRouteEnter` 守卫 **不能** 访问 `this`,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。 +`beforeRouteEnter` 守卫 **不能** 访问 `this`,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。 不过,你可以通过传一个回调给 `next`来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。 -``` js +```js beforeRouteEnter (to, from, next) { next(vm => { // 通过 `vm` 访问组件实例 @@ -143,7 +162,7 @@ beforeRouteLeave (to, from, next) { ## 完整的导航解析流程 1. 导航被触发。 -2. 在失活的组件里调用离开守卫。 +2. 在失活的组件里调用 `beforeRouteLeave` 守卫。 3. 调用全局的 `beforeEach` 守卫。 4. 在重用的组件里调用 `beforeRouteUpdate` 守卫 (2.2+)。 5. 在路由配置里调用 `beforeEnter`。 @@ -153,4 +172,4 @@ beforeRouteLeave (to, from, next) { 9. 导航被确认。 10. 调用全局的 `afterEach` 钩子。 11. 触发 DOM 更新。 -12. 用创建好的实例调用 `beforeRouteEnter` 守卫中传给 `next` 的回调函数。 +12. 调用 `beforeRouteEnter` 守卫中传给 `next` 的回调函数,创建好的组件实例会作为回调函数的参数传入。 diff --git a/docs/zh/guide/advanced/scroll-behavior.md b/docs/zh/guide/advanced/scroll-behavior.md index c30670362..a2eac879c 100644 --- a/docs/zh/guide/advanced/scroll-behavior.md +++ b/docs/zh/guide/advanced/scroll-behavior.md @@ -1,12 +1,14 @@ # 滚动行为 + + 使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 `vue-router` 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。 **注意: 这个功能只在支持 `history.pushState` 的浏览器中可用。** 当创建一个 Router 实例,你可以提供一个 `scrollBehavior` 方法: -``` js +```js const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { @@ -26,7 +28,7 @@ const router = new VueRouter({ 举例: -``` js +```js scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } } @@ -36,7 +38,7 @@ scrollBehavior (to, from, savedPosition) { 返回 `savedPosition`,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样: -``` js +```js scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition @@ -48,7 +50,7 @@ scrollBehavior (to, from, savedPosition) { 如果你要模拟“滚动到锚点”的行为: -``` js +```js scrollBehavior (to, from, savedPosition) { if (to.hash) { return { @@ -66,7 +68,7 @@ scrollBehavior (to, from, savedPosition) { 你也可以返回一个 Promise 来得出预期的位置描述: -``` js +```js scrollBehavior (to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { @@ -77,3 +79,18 @@ scrollBehavior (to, from, savedPosition) { ``` 将其挂载到从页面级别的过渡组件的事件上,令其滚动行为和页面过渡一起良好运行是可能的。但是考虑到用例的多样性和复杂性,我们仅提供这个原始的接口,以支持不同用户场景的具体实现。 + +## 平滑滚动 + +只需将 `behavior` 选项添加到 `scrollBehavior` 内部返回的对象中,就可以为[支持它的浏览器](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior)启用原生平滑滚动: + +```js +scrollBehavior (to, from, savedPosition) { + if (to.hash) { + return { + selector: to.hash, + behavior: 'smooth', + } + } +} +``` diff --git a/docs/zh/guide/advanced/transitions.md b/docs/zh/guide/advanced/transitions.md index f71869df5..2039bb6a9 100644 --- a/docs/zh/guide/advanced/transitions.md +++ b/docs/zh/guide/advanced/transitions.md @@ -1,20 +1,22 @@ # 过渡动效 + + `` 是基本的动态组件,所以我们可以用 `` 组件给它添加一些过渡效果: -``` html +```html ``` -[Transition 的所有功能](https://cn.vuejs.org/guide/transitions.html) 在这里同样适用。 +[Transition 的所有功能](https://v2.cn.vuejs.org/v2/guide/transitions.html) 在这里同样适用。 ## 单个路由的过渡 上面的用法会给所有路由设置一样的过渡效果,如果你想让每个路由组件有各自的过渡效果,可以在各路由组件内使用 `` 并设置不同的 name。 -``` js +```js const Foo = { template: ` @@ -36,14 +38,14 @@ const Bar = { 还可以基于当前路由与目标路由的变化关系,动态设置过渡效果: -``` html +```html ``` -``` js +```js // 接着在父组件内 // watch $route 决定使用哪种过渡 watch: { diff --git a/docs/zh/guide/essentials/dynamic-matching.md b/docs/zh/guide/essentials/dynamic-matching.md index a5297a64f..831f3db0e 100644 --- a/docs/zh/guide/essentials/dynamic-matching.md +++ b/docs/zh/guide/essentials/dynamic-matching.md @@ -1,8 +1,10 @@ # 动态路由匹配 + + 我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 `User` 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 `vue-router` 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果: -``` js +```js const User = { template: '
    User
    ' } @@ -20,7 +22,7 @@ const router = new VueRouter({ 一个“路径参数”使用冒号 `:` 标记。当匹配到一个路由时,参数值会被设置到 `this.$route.params`,可以在每个组件内使用。于是,我们可以更新 `User` 的模板,输出当前用户的 ID: -``` js +```js const User = { template: '
    User {{ $route.params.id }}
    ' } @@ -30,9 +32,9 @@ const User = { 你可以在一个路由中设置多段“路径参数”,对应的值都会设置到 `$route.params` 中。例如: -| 模式 | 匹配路径 | $route.params | -|---------|------|--------| -| /user/:username | /user/evan | `{ username: 'evan' }` | +| 模式 | 匹配路径 | \$route.params | +| ----------------------------- | ------------------- | -------------------------------------- | +| /user/:username | /user/evan | `{ username: 'evan' }` | | /user/:username/post/:post_id | /user/evan/post/123 | `{ username: 'evan', post_id: '123' }` | 除了 `$route.params` 外,`$route` 对象还提供了其它有用的信息,例如,`$route.query` (如果 URL 中有查询参数)、`$route.hash` 等等。你可以查看 [API 文档](../../api/#路由对象) 的详细说明。 @@ -43,11 +45,11 @@ const User = { 复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) `$route` 对象: -``` js +```js const User = { template: '...', watch: { - '$route' (to, from) { + $route(to, from) { // 对路由变化作出响应... } } @@ -56,10 +58,10 @@ const User = { 或者使用 2.2 中引入的 `beforeRouteUpdate` [导航守卫](../advanced/navigation-guards.html): -``` js +```js const User = { template: '...', - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { // react to route changes... // don't forget to call next() } @@ -96,8 +98,8 @@ this.$route.params.pathMatch // '/non-existing' ## 高级匹配模式 -`vue-router` 使用 [path-to-regexp](https://github.com/pillarjs/path-to-regexp/tree/v1.7.0) 作为路径匹配引擎,所以支持很多高级的匹配模式,例如:可选的动态路径参数、匹配零个或多个、一个或多个,甚至是自定义正则匹配。查看它的 [文档](https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#parameters) 学习高阶的路径匹配,还有 [这个例子 ](https://github.com/vuejs/vue-router/blob/dev/examples/route-matching/app.js) 展示 `vue-router` 怎么使用这类匹配。 +`vue-router` 使用 [path-to-regexp](https://github.com/pillarjs/path-to-regexp/tree/v1.7.0) 作为路径匹配引擎,所以支持很多高级的匹配模式,例如:可选的动态路径参数、匹配零个或多个、一个或多个,甚至是自定义正则匹配。查看它的[文档](https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#parameters)学习高阶的路径匹配,还有[这个例子 ](https://github.com/vuejs/vue-router/blob/dev/examples/route-matching/app.js)展示 `vue-router` 怎么使用这类匹配。 ## 匹配优先级 -有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。 +有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:路由定义得越早,优先级就越高。 diff --git a/docs/zh/guide/essentials/history-mode.md b/docs/zh/guide/essentials/history-mode.md index ee27f0c56..0f6a5e6ec 100644 --- a/docs/zh/guide/essentials/history-mode.md +++ b/docs/zh/guide/essentials/history-mode.md @@ -19,9 +19,14 @@ const router = new VueRouter({ ## 后端配置例子 +**注意**:下列示例假设你在根目录服务这个应用。如果想部署到一个子目录,你需要使用 [Vue CLI 的 `publicPath` 选项](https://cli.vuejs.org/zh/config/#publicpath) 和相关的 [router `base` property](https://router.vuejs.org/zh/api/#base)。你还需要把下列示例中的根目录调整成为子目录 (例如用 `RewriteBase /name-of-your-subfolder/` 替换掉 `RewriteBase /`)。 + #### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / @@ -50,9 +55,9 @@ const fs = require('fs') const httpPort = 80 http.createServer((req, res) => { - fs.readFile('index.htm', 'utf-8', (err, content) => { + fs.readFile('index.html', 'utf-8', (err, content) => { if (err) { - console.log('We cannot open "index.htm" file.') + console.log('We cannot open "index.html" file.') } res.writeHead(200, { @@ -124,7 +129,7 @@ rewrite { ## 警告 -给个警告,因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 `index.html` 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。 +给个警告,因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 `index.html` 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后再给出一个 404 页面。 ``` js const router = new VueRouter({ diff --git a/docs/zh/guide/essentials/named-routes.md b/docs/zh/guide/essentials/named-routes.md index 175eeefab..ca607386b 100644 --- a/docs/zh/guide/essentials/named-routes.md +++ b/docs/zh/guide/essentials/named-routes.md @@ -1,8 +1,10 @@ # 命名路由 + + 有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 `routes` 配置中给某个路由设置名称。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -16,14 +18,14 @@ const router = new VueRouter({ 要链接到一个命名路由,可以给 `router-link` 的 `to` 属性传一个对象: -``` html +```html User ``` 这跟代码调用 `router.push()` 是一回事: -``` js -router.push({ name: 'user', params: { userId: 123 }}) +```js +router.push({ name: 'user', params: { userId: 123 } }) ``` 这两种方式都会把路由导航到 `/user/123` 路径。 diff --git a/docs/zh/guide/essentials/navigation.md b/docs/zh/guide/essentials/navigation.md index 488e919f5..62e7e1e77 100644 --- a/docs/zh/guide/essentials/navigation.md +++ b/docs/zh/guide/essentials/navigation.md @@ -14,8 +14,8 @@ sidebarDepth: 0 当你点击 `` 时,这个方法会在内部调用,所以说,点击 `` 等同于调用 `router.push(...)`。 -| 声明式 | 编程式 | -|-------------|--------------| +| 声明式 | 编程式 | +| ------------------------- | ------------------ | | `` | `router.push(...)` | 该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如: @@ -54,8 +54,8 @@ router.push({ path: '/user', params: { userId }}) // -> /user 跟 `router.push` 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。 -| 声明式 | 编程式 | -|-------------|--------------| +| 声明式 | 编程式 | +| --------------------------------- | --------------------- | | `` | `router.replace(...)` | ## `router.go(n)` diff --git a/docs/zh/guide/essentials/nested-routes.md b/docs/zh/guide/essentials/nested-routes.md index e9e0f2b84..257abf267 100644 --- a/docs/zh/guide/essentials/nested-routes.md +++ b/docs/zh/guide/essentials/nested-routes.md @@ -1,5 +1,7 @@ # 嵌套路由 + + 实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如: ``` @@ -17,27 +19,25 @@ 接着上节创建的 app: -``` html +```html
    ``` -``` js +```js const User = { template: '
    User {{ $route.params.id }}
    ' } const router = new VueRouter({ - routes: [ - { path: '/user/:id', component: User } - ] + routes: [{ path: '/user/:id', component: User }] }) ``` 这里的 `` 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 ``。例如,在 `User` 组件的模板添加一个 ``: -``` js +```js const User = { template: `
    @@ -50,10 +50,12 @@ const User = { 要在嵌套的出口中渲染组件,需要在 `VueRouter` 的参数中使用 `children` 配置: -``` js +```js const router = new VueRouter({ routes: [ - { path: '/user/:id', component: User, + { + path: '/user/:id', + component: User, children: [ { // 当 /user/:id/profile 匹配成功, @@ -79,15 +81,16 @@ const router = new VueRouter({ 此时,基于上面的配置,当你访问 `/user/foo` 时,`User` 的出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个 空的 子路由: -``` js +```js const router = new VueRouter({ routes: [ { - path: '/user/:id', component: User, + path: '/user/:id', + component: User, children: [ // 当 /user/:id 匹配成功, // UserHome 会被渲染在 User 的 中 - { path: '', component: UserHome }, + { path: '', component: UserHome } // ...其他子路由 ] diff --git a/docs/zh/guide/essentials/passing-props.md b/docs/zh/guide/essentials/passing-props.md index 120a64dde..6ab202ca8 100644 --- a/docs/zh/guide/essentials/passing-props.md +++ b/docs/zh/guide/essentials/passing-props.md @@ -1,25 +1,25 @@ # 路由组件传参 + + 在组件中使用 `$route` 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。 使用 `props` 将组件和路由解耦: **取代与 `$route` 的耦合** -``` js +```js const User = { template: '
    User {{ $route.params.id }}
    ' } const router = new VueRouter({ - routes: [ - { path: '/user/:id', component: User } - ] + routes: [{ path: '/user/:id', component: User }] }) ``` **通过 `props` 解耦** -``` js +```js const User = { props: ['id'], template: '
    User {{ id }}
    ' @@ -48,10 +48,14 @@ const router = new VueRouter({ 如果 `props` 是一个对象,它会被按原样设置为组件属性。当 `props` 是静态的时候有用。 -``` js +```js const router = new VueRouter({ routes: [ - { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } } + { + path: '/promotion/from-newsletter', + component: Promotion, + props: { newsletterPopup: false } + } ] }) ``` @@ -60,10 +64,14 @@ const router = new VueRouter({ 你可以创建一个函数返回 `props`。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。 -``` js +```js const router = new VueRouter({ routes: [ - { path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) } + { + path: '/search', + component: SearchUser, + props: route => ({ query: route.query.q }) + } ] }) ``` diff --git a/docs/zh/guide/essentials/redirect-and-alias.md b/docs/zh/guide/essentials/redirect-and-alias.md index 56a567946..811dae65c 100644 --- a/docs/zh/guide/essentials/redirect-and-alias.md +++ b/docs/zh/guide/essentials/redirect-and-alias.md @@ -35,7 +35,7 @@ const router = new VueRouter({ }) ``` -注意[导航守卫](../advanced/navigation-guards.md)并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 `/a` 路由添加一个 `beforeEach` 或 `beforeLeave` 守卫并不会有任何效果。 +注意[导航守卫](../advanced/navigation-guards.md)并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 `/a` 路由添加一个 `beforeEnter` 守卫并不会有任何效果。 其它高级用法,请参考[例子](https://github.com/vuejs/vue-router/blob/dev/examples/redirect/app.js)。 diff --git a/docs/zh/installation.md b/docs/zh/installation.md index dc9bff8a6..0ae1c03f3 100644 --- a/docs/zh/installation.md +++ b/docs/zh/installation.md @@ -2,10 +2,10 @@ ### 直接下载 / CDN -[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js) +[https://unpkg.com/vue-router@3/dist/vue-router.js](https://unpkg.com/vue-router@3/dist/vue-router.js) -[Unpkg.com](https://unpkg.com) 提供了基于 NPM 的 CDN 链接。上面的链接会一直指向在 NPM 发布的最新版本。你也可以像 `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js` 这样指定 版本号 或者 Tag。 +[Unpkg.com](https://unpkg.com) 提供了基于 NPM 的 CDN 链接。上面的链接会一直指向在 NPM 发布的最新版本。你也可以像 `https://unpkg.com/vue-router@3.0.0/dist/vue-router.js` 这样指定 版本号 或者 Tag。 在 Vue 后面加载 `vue-router`,它会自动安装的: @@ -32,6 +32,14 @@ Vue.use(VueRouter) 如果使用全局的 script 标签,则无须如此 (手动安装)。 +### Vue CLI + +如果你有一个正在使用 [Vue CLI](https://cli.vuejs.org/zh/) 的项目,你可以以项目插件的形式添加 Vue Router。CLI 可以生成上述代码及两个示例路由。**它也会覆盖你的 `App.vue`**,因此请确保在项目中运行以下命令之前备份这个文件: + +```sh +vue add router +``` + ### 构建开发版 如果你想使用最新的开发版,就得从 GitHub 上直接 clone,然后自己 build 一个 `vue-router`。 diff --git a/examples/active-links/app.js b/examples/active-links/app.js index 1715d58b6..021374b2c 100644 --- a/examples/active-links/app.js +++ b/examples/active-links/app.js @@ -108,6 +108,10 @@ new Vue({
  • /redirect-gallery named (redirect to /gallery)
  • /redirect-image (redirect to /gallery/image1)
  • /redirect-image named (redirect to /gallery/image1)
  • + +
  • /users?one
  • +
  • /users?two
  • +
  • /users/nested?two
  • diff --git a/examples/basic/app.js b/examples/basic/app.js index cced2e29e..148949806 100644 --- a/examples/basic/app.js +++ b/examples/basic/app.js @@ -1,6 +1,30 @@ import Vue from 'vue' import VueRouter from 'vue-router' +// track number of popstate listeners +let numPopstateListeners = 0 +const listenerCountDiv = document.createElement('div') +listenerCountDiv.id = 'popstate-count' +listenerCountDiv.textContent = numPopstateListeners + ' popstate listeners' +document.body.appendChild(listenerCountDiv) + +const originalAddEventListener = window.addEventListener +const originalRemoveEventListener = window.removeEventListener +window.addEventListener = function (name, handler) { + if (name === 'popstate') { + listenerCountDiv.textContent = + ++numPopstateListeners + ' popstate listeners' + } + return originalAddEventListener.apply(this, arguments) +} +window.removeEventListener = function (name, handler) { + if (name === 'popstate') { + listenerCountDiv.textContent = + --numPopstateListeners + ' popstate listeners' + } + return originalRemoveEventListener.apply(this, arguments) +} + // 1. Use plugin. // This installs and , // and injects $router and $route to all router-enabled child components @@ -11,6 +35,7 @@ const Home = { template: '
    home
    ' } const Foo = { template: '
    foo
    ' } const Bar = { template: '
    bar
    ' } const Unicode = { template: '
    unicode
    ' } +const Query = { template: '
    query: "{{ $route.params.q }}"
    ' } // 3. Create the router const router = new VueRouter({ @@ -20,14 +45,25 @@ const router = new VueRouter({ { path: '/', component: Home }, { path: '/foo', component: Foo }, { path: '/bar', component: Bar }, - { path: '/é', component: Unicode } + { path: encodeURI('/é'), component: Unicode }, + { path: '/query/:q', component: Query } ] }) +router.beforeEach((to, from, next) => { + if (to.query.delay) { + setTimeout(() => { + next() + }, Number(to.query.delay)) + } else { + next() + } +}) + // 4. Create and mount root instance. // Make sure to inject the router. // Route components will be rendered inside . -new Vue({ +const vueInstance = new Vue({ router, data: () => ({ n: 0 }), template: ` @@ -40,14 +76,18 @@ new Vue({ /bar -
  • -
  • /é?t=%ñ
  • -
  • /é#%25ñ
  • - +
  • +
  • /é?t=%ñ
  • +
  • /é#%25ñ
  • +
  • {{ props.route.path }} (with v-slot).
  • +
  • /foo (replace)
  • +
  • /query/A%
  • +
  • / (delay of 500ms)
  • +
  • /foo (delay of 500ms)
  • {{ n }}
    @@ -68,3 +108,8 @@ new Vue({ } } }).$mount('#app') + +document.getElementById('unmount').addEventListener('click', () => { + vueInstance.$destroy() + vueInstance.$el.innerHTML = '' +}) diff --git a/examples/basic/index.html b/examples/basic/index.html index 78a0c040f..695d668f5 100644 --- a/examples/basic/index.html +++ b/examples/basic/index.html @@ -1,6 +1,8 @@ ← Examples index + +
    diff --git a/examples/composables/app.js b/examples/composables/app.js new file mode 100644 index 000000000..1762aeea0 --- /dev/null +++ b/examples/composables/app.js @@ -0,0 +1,146 @@ +import Vue, { defineComponent, watch, ref } from 'vue' +import VueRouter from 'vue-router' +import { + useRoute, + useRouter, + onBeforeRouteLeave, + onBeforeRouteUpdate, + useLink +} from 'vue-router/composables' + +Vue.use(VueRouter) + +const Foo = defineComponent({ + setup () { + const route = useRoute() + onBeforeRouteUpdate((to, from, next) => { + console.log('Foo updating') + next() + }) + onBeforeRouteLeave((to, from, next) => { + console.log('Foo leaving') + next() + }) + + return { route } + }, + template: ` +
    +

    Foo

    + {{ route.fullPath }} +
    + ` +}) + +const Home = defineComponent({ + setup () { + const route = useRoute() + const router = useRouter() + + // should be / + const startRoute = route.fullPath + + onBeforeRouteUpdate((to, from, next) => { + console.log('Home updating') + next() + }) + + onBeforeRouteLeave((to, from, next) => { + console.log('Home leaving') + next() + }) + + const watchCount = ref(0) + + watch( + () => route.query.n, + () => { + watchCount.value++ + } + ) + + function navigate () { + router.push({ query: { n: 1 + (Number(route.query.n) || 0) }}) + } + return { route, navigate, watchCount, startRoute } + }, + template: ` +
    +

    Home

    +

    {{ startRoute }}

    +

    {{ watchCount }}

    +

    {{ route.fullPath }}

    + +
    + +
    + `, + components: { Foo } +}) + +const About = defineComponent({ + setup () { + const route = useRoute() + return { route } + }, + template: ` +
    +

    About

    +

    {{ route.fullPath }}

    +
    + ` +}) + +const Nested = defineComponent({ + template: `` +}) + +const NestedEmpty = defineComponent({ + template: `
    NestedEmpty
    ` +}) + +const NestedA = defineComponent({ + template: `
    NestedA
    ` +}) + +const router = new VueRouter({ + mode: 'history', + base: __dirname, + routes: [ + { path: '/', component: Home }, + { + path: '/nested', + component: Nested, + children: [ + { path: '', component: NestedEmpty }, + { path: 'a', component: NestedA } + ] + }, + { path: '/about', component: About } + ] +}) + +new Vue({ + router, + template: ` +
    +

    Basic

    +
      +
    • /
    • +
    • /about
    • +
    • /nested
    • +
    • /nested/a
    • +
    + + +
    {{ href }}: {{ isActive }}, {{ isExactActive }}
    +
    + `, + setup () { + const { href, isActive, isExactActive, navigate, route } = useLink({ + to: '/nested' + }) + + return { href, isActive, navigate, route, isExactActive } + } +}).$mount('#app') diff --git a/examples/composables/index.html b/examples/composables/index.html new file mode 100644 index 000000000..b6f6342a7 --- /dev/null +++ b/examples/composables/index.html @@ -0,0 +1,7 @@ + + +← Examples index +
    +
    + + diff --git a/examples/hash-mode/app.js b/examples/hash-mode/app.js index c8081cf2a..16475ffc8 100644 --- a/examples/hash-mode/app.js +++ b/examples/hash-mode/app.js @@ -1,6 +1,30 @@ import Vue from 'vue' import VueRouter from 'vue-router' +// track number of popstate listeners +let numPopstateListeners = 0 +const listenerCountDiv = document.createElement('div') +listenerCountDiv.id = 'popstate-count' +listenerCountDiv.textContent = numPopstateListeners + ' popstate listeners' +document.body.appendChild(listenerCountDiv) + +const originalAddEventListener = window.addEventListener +const originalRemoveEventListener = window.removeEventListener +window.addEventListener = function (name, handler) { + if (name === 'popstate') { + listenerCountDiv.textContent = + ++numPopstateListeners + ' popstate listeners' + } + return originalAddEventListener.apply(this, arguments) +} +window.removeEventListener = function (name, handler) { + if (name === 'popstate') { + listenerCountDiv.textContent = + --numPopstateListeners + ' popstate listeners' + } + return originalRemoveEventListener.apply(this, arguments) +} + // 1. Use plugin. // This installs and , // and injects $router and $route to all router-enabled child components @@ -11,6 +35,7 @@ const Home = { template: '
    home
    ' } const Foo = { template: '
    foo
    ' } const Bar = { template: '
    bar
    ' } const Unicode = { template: '
    unicode: {{ $route.params.unicode }}
    ' } +const Query = { template: '
    query: "{{ $route.params.q }}"
    ' } // 3. Create the router const router = new VueRouter({ @@ -20,15 +45,16 @@ const router = new VueRouter({ { path: '/', component: Home }, // all paths are defined without the hash. { path: '/foo', component: Foo }, { path: '/bar', component: Bar }, - { path: '/é', component: Unicode }, - { path: '/é/:unicode', component: Unicode } + { path: encodeURI('/é'), component: Unicode }, + { path: encodeURI('/é/:unicode'), component: Unicode }, + { path: '/query/:q', component: Query, name: 'param' } ] }) // 4. Create and mount root instance. // Make sure to inject the router. // Route components will be rendered inside . -new Vue({ +const vueInstance = new Vue({ router, template: `
    @@ -38,14 +64,24 @@ new Vue({
  • /foo
  • /bar
  • /bar -
  • -
  • /é/ñ
  • -
  • /é/ñ?t=%ñ
  • -
  • /é/ñ#é
  • +
  • +
  • /é/ñ
  • +
  • /é/ñ?t=%ñ
  • +
  • /é/ñ#é
  • +
  • /query/A%
  • +
  • /query/A% (object)
  • +
  • /query/A%2FE
  • +
  • /query/A%2FE (object)
  • {{ $route.query.t }}
    {{ $route.hash }}
    - ` + `, + methods: {} }).$mount('#app') + +document.getElementById('unmount').addEventListener('click', () => { + vueInstance.$destroy() + vueInstance.$el.innerHTML = '' +}) diff --git a/examples/hash-mode/index.html b/examples/hash-mode/index.html index 68e93063a..5789d784f 100644 --- a/examples/hash-mode/index.html +++ b/examples/hash-mode/index.html @@ -1,6 +1,8 @@ ← Examples index + +
    diff --git a/examples/index.html b/examples/index.html index c0ee5bc16..2b41a845a 100644 --- a/examples/index.html +++ b/examples/index.html @@ -18,6 +18,7 @@

    Vue Router Examples

  • Route Props
  • Route Alias
  • Route Params
  • +
  • Router errors
  • Transitions
  • Data Fetching
  • Navigation Guards
  • @@ -27,6 +28,9 @@

    Vue Router Examples

  • Discrete Components
  • Nested Routers
  • Keepalive View
  • +
  • Multiple Apps
  • +
  • Restart App
  • +
  • Composables
  • diff --git a/examples/multi-app/app.js b/examples/multi-app/app.js new file mode 100644 index 000000000..c9d28a0c7 --- /dev/null +++ b/examples/multi-app/app.js @@ -0,0 +1,75 @@ +import Vue from 'vue' +import VueRouter from 'vue-router' + +// track number of popstate listeners +let numPopstateListeners = 0 +const listenerCountDiv = document.getElementById('popcount') +listenerCountDiv.textContent = 0 + +const originalAddEventListener = window.addEventListener +const originalRemoveEventListener = window.removeEventListener +window.addEventListener = function (name, handler) { + if (name === 'popstate') { + listenerCountDiv.textContent = + ++numPopstateListeners + } + return originalAddEventListener.apply(this, arguments) +} +window.removeEventListener = function (name, handler) { + if (name === 'popstate') { + listenerCountDiv.textContent = + --numPopstateListeners + } + return originalRemoveEventListener.apply(this, arguments) +} + +// 1. Use plugin. +// This installs and , +// and injects $router and $route to all router-enabled child components +Vue.use(VueRouter) + +const looper = [1, 2, 3] + +looper.forEach((n) => { + let vueInstance + const mountEl = document.getElementById('mount' + n) + const unmountEl = document.getElementById('unmount' + n) + + mountEl.addEventListener('click', () => { + // 2. Define route components + const Home = { template: '
    home
    ' } + const Foo = { template: '
    foo
    ' } + + // 3. Create the router + const router = new VueRouter({ + mode: 'history', + base: __dirname, + routes: [ + { path: '/', component: Home }, + { path: '/foo', component: Foo } + ] + }) + + // 4. Create and mount root instance. + // Make sure to inject the router. + // Route components will be rendered inside . + vueInstance = new Vue({ + router, + template: ` +
    +

    Basic

    +
      +
    • /
    • +
    • /foo
    • +
    + +
    + ` + }).$mount('#app-' + n) + }) + + unmountEl.addEventListener('click', () => { + vueInstance.$destroy() + vueInstance.$el.innerHTML = '' + }) +}) diff --git a/examples/multi-app/index.html b/examples/multi-app/index.html new file mode 100644 index 000000000..96f094114 --- /dev/null +++ b/examples/multi-app/index.html @@ -0,0 +1,24 @@ + + +← Examples index + + + + + +
    + + + + + +
    + +popstate count: + +
    +
    +
    + + + \ No newline at end of file diff --git a/examples/navigation-guards/app.js b/examples/navigation-guards/app.js index 86b2c9496..48de8aa0d 100644 --- a/examples/navigation-guards/app.js +++ b/examples/navigation-guards/app.js @@ -91,15 +91,25 @@ const Quux = { } const NestedParent = { - template: `
    Nested Parent
    - /parent/child/1 - /parent/child/2 -
    -

    - {{ log }} -

    - -
    `, + template: ` +
    + Nested Parent +
    + /parent/child/1 + /parent/child/2 +
    +

    + {{ log }} +

    + + + + + + + + +
    `, data: () => ({ logs: [] }), beforeRouteEnter (to, from, next) { next(vm => { diff --git a/examples/navigation-guards/index.html b/examples/navigation-guards/index.html index 0e7e950ee..580cb9768 100644 --- a/examples/navigation-guards/index.html +++ b/examples/navigation-guards/index.html @@ -1,5 +1,13 @@ + ← Examples index
    diff --git a/examples/restart-app/app.js b/examples/restart-app/app.js new file mode 100644 index 000000000..424affe10 --- /dev/null +++ b/examples/restart-app/app.js @@ -0,0 +1,66 @@ +import Vue from 'vue' +import VueRouter from 'vue-router' + +const Home = { template: '
    home
    ' } +const Foo = { template: '
    foo
    ' } + +const routes = [ + { path: '/', component: Home }, + { path: '/foo', component: Foo } +] + +const router = new VueRouter({ + mode: 'history', + base: __dirname, + routes +}) + +// track number of beforeResolve +const increment = name => { + const counter = document.getElementById(name) + counter.innerHTML++ +} + +document.getElementById('beforeEach').innerHTML = 0 +router.beforeEach((to, from, next) => { + increment('beforeEach') + next() +}) + +document.getElementById('beforeResolve').innerHTML = 0 +router.beforeResolve((to, from, next) => { + increment('beforeResolve') + next() +}) + +document.getElementById('afterEach').innerHTML = 0 +router.afterEach((to, from) => { + increment('afterEach') +}) + +Vue.use(VueRouter) + +let vueInstance +const mountEl = document.getElementById('mount') +const unmountEl = document.getElementById('unmount') + +mountEl.addEventListener('click', () => { + vueInstance = new Vue({ + router, + template: ` +
    +

    Hello "Restart-app"

    +
      +
    • Go to Home
    • +
    • Go to Foo
    • +
    + +
    + ` + }).$mount('#app') +}) + +unmountEl.addEventListener('click', () => { + vueInstance.$destroy() + vueInstance.$el.innerHTML = '' +}) diff --git a/examples/restart-app/index.html b/examples/restart-app/index.html new file mode 100644 index 000000000..c8885ad66 --- /dev/null +++ b/examples/restart-app/index.html @@ -0,0 +1,21 @@ + + +← Examples index + +
    + + + + +
    + +Count beforeEach:
    +Count beforeResolve:
    +Count afterEach:
    + +
    + +
    + + + \ No newline at end of file diff --git a/examples/router-errors/app.js b/examples/router-errors/app.js new file mode 100644 index 000000000..b6979380d --- /dev/null +++ b/examples/router-errors/app.js @@ -0,0 +1,55 @@ +import Vue from 'vue' +import VueRouter from 'vue-router' + +const component = { + template: ` +
    + {{ $route.fullPath }} +
    + ` +} + +Vue.use(VueRouter) + +const router = new VueRouter({ + routes: [ + { path: '/', component }, { path: '/foo', component } + ] +}) + +window.router = router + +router.beforeEach((to, from, next) => { + console.log('from', from.fullPath) + console.log('going to', to.fullPath) + if (to.query.wait) { + setTimeout(() => next(), 100) + } else if (to.query.redirect) { + next(to.query.redirect) + } else if (to.query.abort) { + next(false) + } else { + next() + } +}) + +new Vue({ + el: '#app', + router +}) + +// 4 NAVIGATION ERROR CASES : + +// navigation duplicated +// router.push('/foo') +// router.push('/foo') + +// navigation cancelled +// router.push('/foo?wait=y') +// router.push('/') + +// navigation redirected +// router.push('/foo?redirect=/') + +// navigation aborted +// router.push('/foo?abort=y') diff --git a/examples/router-errors/index.html b/examples/router-errors/index.html new file mode 100644 index 000000000..12011560b --- /dev/null +++ b/examples/router-errors/index.html @@ -0,0 +1,8 @@ + +
    + / + /foo + +
    + + diff --git a/examples/server.js b/examples/server.js index 0f37cc57d..be636798e 100644 --- a/examples/server.js +++ b/examples/server.js @@ -27,7 +27,8 @@ fs.readdirSync(__dirname).forEach(file => { app.use(express.static(__dirname)) +const host = process.env.HOST || 'localhost' const port = process.env.PORT || 8080 -module.exports = app.listen(port, () => { - console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`) +module.exports = app.listen(port, host, () => { + console.log(`Server listening on http://${host}:${port}, Ctrl+C to stop`) }) diff --git a/examples/transitions/index.html b/examples/transitions/index.html index e18704f31..1abaa4f81 100644 --- a/examples/transitions/index.html +++ b/examples/transitions/index.html @@ -2,14 +2,14 @@