diff --git a/.eslintignore b/.eslintignore index 1521c8b76..2b88bf081 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ dist +*.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 910195326..520f3ff13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,88 @@ +## [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 + +- **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 diff --git a/README.md b/README.md index 9ccf5f9bc..40cba38ce 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # 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). +> 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

@@ -9,54 +11,85 @@ Vue Router is part of the Vue Ecosystem and is an MIT-licensed open source proje - [Become a Sponsor on GitHub](https://github.com/sponsors/posva) - [One-time donation via PayPal](https://paypal.me/posva) - - - -

Silver Sponsors

+ +

Gold Sponsors

- - Vue Mastery + + + + VueJobs + +

- - Vuetify +

Silver Sponsors

+

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

Bronze Sponsors

-

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

+ + --- -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). +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 # 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 diff --git a/build/configs.js b/build/configs.js index 33117a641..4fbfa6d24 100644 --- a/build/configs.js +++ b/build/configs.js @@ -5,8 +5,7 @@ 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 index 206e892e1..e9c704b39 100644 --- a/docs/.vuepress/components/sponsors.json +++ b/docs/.vuepress/components/sponsors.json @@ -1,14 +1,44 @@ { + "platinum": [], + "gold": [], "silver": [ { "href": "https://www.vuemastery.com/", "alt": "VueMastery", - "imgSrc": "https://www.vuemastery.com/images/vuemastery.svg" + "imgSrcLight": "https://posva-sponsors.pages.dev/logos/vuemastery-light.svg", + "imgSrcDark": "https://posva-sponsors.pages.dev/logos/vuemastery-dark.png" }, { - "imgSrc": "https://cdn.vuetifyjs.com/docs/images/logos/vuetify-logo-light-text.svg", - "href": "https://www.vuetifyjs.com/", - "alt": "Vuetify" + "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 b43144e02..79c12f352 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -33,6 +33,10 @@ module.exports = ctx => ({ }, 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` } @@ -66,7 +70,8 @@ module.exports = ctx => ({ themeConfig: { algolia: ctx.isProd ? { - apiKey: 'f854bb46d3de7eeb921a3b9173bd0d4c', + appId: 'LI3RW4C4QI', + apiKey: '08e7ef7cd3969442874f0dee9dec34be', indexName: 'vue-router' } : null, @@ -92,6 +97,10 @@ module.exports = ctx => ({ 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' @@ -142,6 +151,10 @@ module.exports = ctx => ({ 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' @@ -192,6 +205,10 @@ module.exports = ctx => ({ 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' @@ -223,7 +240,8 @@ module.exports = ctx => ({ '/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' ] } ] @@ -241,6 +259,10 @@ module.exports = ctx => ({ 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' @@ -291,6 +313,10 @@ module.exports = ctx => ({ 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' @@ -340,6 +366,10 @@ module.exports = ctx => ({ 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' 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/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/theme/Layout.vue b/docs/.vuepress/theme/Layout.vue index 3a5fc8151..a27d64a62 100644 --- a/docs/.vuepress/theme/Layout.vue +++ b/docs/.vuepress/theme/Layout.vue @@ -1,36 +1,105 @@ @@ -38,7 +107,7 @@ export default { + + 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/api/README.md b/docs/api/README.md index 52a3b7fd6..5c9dacc60 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -23,6 +23,7 @@ sidebar: auto ```html
  • ` and ``. Wh Signature: - ``` + ```ts type PositionDescriptor = { x: number, y: number } | { selector: string } | - ?{} + void type scrollBehaviorHandler = ( to: Route, @@ -340,21 +342,21 @@ 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 +### 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 Router from 'vue-router' + import VueRouter from 'vue-router' - const router = new Router({ + const router = new VueRouter({ // ... }) router.beforeEach((to, from) => { - if (from === START_LOCATION) { + if (from === VueRouter.START_LOCATION) { // initial navigation } }) @@ -441,7 +443,7 @@ Reverse URL resolving. Given location in form same as used in ``. ### router.addRoutes -_DEPRECATED_: use [`route.addRoute()`](#router-addroute) instead. +_DEPRECATED_: use [`router.addRoute()`](#router-addroute) instead. Signature: @@ -479,7 +481,7 @@ addRoute(parentName: string, route: RouteConfig): () => void > 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 propery e.g. `regex` as it doesn't exist on Vue Router 4. +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: @@ -569,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/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/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 224ee493e..8337db43a 100644 --- a/docs/fr/guide/essentials/history-mode.md +++ b/docs/fr/guide/essentials/history-mode.md @@ -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 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/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 9ea9e231d..017faa45f 100644 --- a/docs/guide/README.md +++ b/docs/guide/README.md @@ -1,20 +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 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/lazy-loading.md b/docs/guide/advanced/lazy-loading.md index 3c9f5cd76..db059d8a0 100644 --- a/docs/guide/advanced/lazy-loading.md +++ b/docs/guide/advanced/lazy-loading.md @@ -1,6 +1,6 @@ # 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. diff --git a/docs/guide/advanced/navigation-failures.md b/docs/guide/advanced/navigation-failures.md index 234b01fa3..ff6e2cb36 100644 --- a/docs/guide/advanced/navigation-failures.md +++ b/docs/guide/advanced/navigation-failures.md @@ -46,7 +46,7 @@ If you omit the second parameter: `isNavigationFailure(failure)`, it will only c ## _Navigation Failures_'s properties -All navigation failures expose `to` and `from` properties to reflect the current location as well as the target location for the navigation that failed: +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 diff --git a/docs/guide/advanced/navigation-guards.md b/docs/guide/advanced/navigation-guards.md index e68137457..8c132c267 100644 --- a/docs/guide/advanced/navigation-guards.md +++ b/docs/guide/advanced/navigation-guards.md @@ -6,7 +6,7 @@ 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`: @@ -100,12 +100,12 @@ 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) { + 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 @@ -113,7 +113,7 @@ const Foo = { // 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. @@ -133,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) { @@ -162,7 +162,7 @@ If you are using mixins that add in-component navigation guards, make sure to ad Vue.use(Router) Vue.mixin({ - beforeRouteUpdate(to, from ,next) { + beforeRouteUpdate(to, from, next) { // ... } }) diff --git a/docs/guide/advanced/scroll-behavior.md b/docs/guide/advanced/scroll-behavior.md index 874c98aea..037329426 100644 --- a/docs/guide/advanced/scroll-behavior.md +++ b/docs/guide/advanced/scroll-behavior.md @@ -1,6 +1,6 @@ # 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. @@ -8,7 +8,7 @@ When using client-side routing, we may want to scroll to top when navigating to When creating the router instance, you can provide the `scrollBehavior` function: -``` js +```js const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { @@ -28,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 } } @@ -38,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 @@ -50,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 { @@ -69,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(() => { diff --git a/docs/guide/advanced/transitions.md b/docs/guide/advanced/transitions.md index a963da736..0b8a937df 100644 --- a/docs/guide/advanced/transitions.md +++ b/docs/guide/advanced/transitions.md @@ -1,22 +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: ` @@ -38,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 15bcdfca4..c8ced5875 100644 --- a/docs/guide/essentials/dynamic-matching.md +++ b/docs/guide/essentials/dynamic-matching.md @@ -1,6 +1,6 @@ # 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: @@ -60,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 2b2c50686..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 / @@ -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 972a72cb0..2e77ed7cf 100644 --- a/docs/guide/essentials/named-routes.md +++ b/docs/guide/essentials/named-routes.md @@ -1,10 +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: [ { @@ -18,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 f0dbb14df..c4692b5a1 100644 --- a/docs/guide/essentials/nested-routes.md +++ b/docs/guide/essentials/nested-routes.md @@ -1,6 +1,6 @@ # 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: @@ -19,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: `
    @@ -52,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 @@ -81,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 ac4ea7528..b065f0ad1 100644 --- a/docs/guide/essentials/passing-props.md +++ b/docs/guide/essentials/passing-props.md @@ -1,6 +1,6 @@ # 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. diff --git a/docs/installation.md b/docs/installation.md index 5de3b4aa7..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: @@ -51,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 18eb9fbb0..fdab568b5 100644 --- a/docs/ja/README.md +++ b/docs/ja/README.md @@ -6,7 +6,7 @@ actionLink: /ja/installation.html footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Morote --- - + Vue Router は [Vue.js](http://vuejs.org) 公式ルータです。これは Vue.js のコアと深く深く統合されており、Vue.js でシングルページアプリケーションを構築します。機能は次の通りです: diff --git a/docs/ja/api/README.md b/docs/ja/api/README.md index 9aca6a995..996a3ada3 100644 --- a/docs/ja/api/README.md +++ b/docs/ja/api/README.md @@ -20,7 +20,7 @@ sidebar: auto `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_ などのようなカスタムコンポーネントでも同様に開発者にとっても大変便利です。 -**`v-slot` API を使うとき、それは単一の子を `router-link` に通す必要がります。** そうしない場合は、 `router-linke` は `span` 要素で子をラップします。 +**`v-slot` API を使うとき、それは単一の子を `router-link` に通す必要があります。** そうしない場合は、 `router-link` は `span` 要素で子をラップします。 ```html Watch a free video course about Vue Router on Vue School
    + Vue.js と vue-router を使ったシングルページアプリケーションの構築は驚くほど簡単です。Vue.js のコンポーネントを使ってアプリケーションを既に構成しています。vue-router を混ぜ込むには、コンポーネントとルートをマッピングさせて vue-router にどこで描画するかを知らせるだけです。以下が基本的な例です。 ## HTML -``` html - - +```html + +

    Hello App!

    @@ -33,7 +33,7 @@ Vue.js と vue-router を使ったシングルページアプリケーション ## JavaScript -``` js +```js // 0. モジュールシステムを使っている場合 (例: vue-cli 経由で)、Vue と VueRouter をインポートし、`Vue.use(VueRouter)` を呼び出します。 // 1. ルートコンポーネントを定義します @@ -74,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 e337b4fb1..d767bdab1 100644 --- a/docs/ja/guide/advanced/data-fetching.md +++ b/docs/ja/guide/advanced/data-fetching.md @@ -1,6 +1,6 @@ # データ取得 - + ルートが有効化された時にサーバーからデータを取得する必要がしばしばあります。例えば、ユーザープロフィールを描画する前に、サーバーからユーザーデータを取得する必要があります。これを実現するためには 2 種類の方法があります。 @@ -16,7 +16,7 @@ `$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 ラッパーに置き換えてください @@ -73,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) @@ -98,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 65196f1e8..e4d6936ef 100644 --- a/docs/ja/guide/advanced/lazy-loading.md +++ b/docs/ja/guide/advanced/lazy-loading.md @@ -1,6 +1,6 @@ # 遅延ローディングルート - + バンドラーを使ってアプリケーションを構築している時、バンドルされる JavaScript が非常に大きいものになり得ます。結果的にページのロード時間に影響を与えてしまいます。もし各ルートコンポーネントごとに別々のチャンクにして、訪れたルートの時だけロードできればより効率的でしょう。 diff --git a/docs/ja/guide/advanced/meta.md b/docs/ja/guide/advanced/meta.md index 9c402fdf0..4ea38484f 100644 --- a/docs/ja/guide/advanced/meta.md +++ b/docs/ja/guide/advanced/meta.md @@ -1,10 +1,10 @@ # ルートメタフィールド - + ルートの定義をする際に `meta` フィールドを含めることができます。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -33,7 +33,7 @@ const router = new VueRouter({ メタフィールドをグローバルナビゲーションガードで確認するユースケースの例: -``` js +```js router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { // このルートはログインされているかどうか認証が必要です。 @@ -50,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 fc5d71a50..b7e588f61 100644 --- a/docs/ja/guide/advanced/navigation-guards.md +++ b/docs/ja/guide/advanced/navigation-guards.md @@ -1,6 +1,6 @@ # ナビゲーションガード - + この名前が示すように、 `vue-router` によって提供されるナビゲーションガードは、リダイレクトもしくはキャンセルによって遷移をガードするために主に使用されます。ルートナビゲーション処理 (グローバル、ルート単位、コンポーネント内) をフックする多くの方法があります。 @@ -10,7 +10,7 @@ `router.beforeEach` を使ってグローバル before ガードを登録できます。 -``` js +```js const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { @@ -22,21 +22,21 @@ router.beforeEach((to, from, next) => { 全てのガード関数は 3 つの引数を受け取ります。 - - **`to: Route`**: 次にナビゲーションされる対象の [ルートオブジェクト](../../api/#ルートオブジェクト)。 - - - **`from: Route`**: ナビゲーションされる前の現在のルートです。 - - - **`next: Function`**: フックを **解決** するためにこの関数を呼ぶ必要があります。この振る舞いは `next` に渡される引数に依存します: +- **`to: Route`**: 次にナビゲーションされる対象の [ルートオブジェクト](../../api/#ルートオブジェクト)。 + +- **`from: Route`**: ナビゲーションされる前の現在のルートです。 - - **`next()`**: パイプラインの次のフックに移動します。もしフックが残っていない場合は、このナビゲーションは **確立** されます。 +- **`next: Function`**: フックを **解決** するためにこの関数を呼ぶ必要があります。この振る舞いは `next` に渡される引数に依存します: - - **`next(false)`**: 現在のナビゲーションを中止します。もしブラウザのURLが変化した場合は(ユーザーが手動で変更した場合でも、戻るボタンの場合でも)、 `from` ルートのURLにリセットされます。 +- **`next()`**: パイプラインの次のフックに移動します。もしフックが残っていない場合は、このナビゲーションは **確立** されます。 - - **`next('/')` または `next({ path: '/' })`**: 異なる場所へリダイレクトします。現在のナビゲーションは中止され、あたらしいナビゲーションが始まります。任意のロケーションオブジェクトを `next` に渡すことができます。この `next` には、`replace: true`、 `name: 'home'` のようなオプション、そして [`router-link`、`to` プロパティ](../../api/#router-link)または [`router.push`](../../api/#ルーターインスタンスプロパティ)で使用される任意のオプションを指定することができます。 +- **`next(false)`**: 現在のナビゲーションを中止します。もしブラウザの URL が変化した場合は(ユーザーが手動で変更した場合でも、戻るボタンの場合でも)、 `from` ルートの URL にリセットされます。 - - **`next(error)`**: (2.4.0+) `next` に渡された引数が `Error` インスタンスである場合、ナビゲーションは中止され、エラーは `router.onError()` を介して登録されたコールバックに渡されます。 +- **`next('/')` または `next({ path: '/' })`**: 異なる場所へリダイレクトします。現在のナビゲーションは中止され、あたらしいナビゲーションが始まります。任意のロケーションオブジェクトを `next` に渡すことができます。この `next` には、`replace: true`、 `name: 'home'` のようなオプション、そして [`router-link`、`to` プロパティ](../../api/#router-link)または [`router.push`](../../api/#ルーターインスタンスプロパティ)で使用される任意のオプションを指定することができます。 - **与えられたナビゲーションガードを通過する任意のパスにおいて、常に 1 回だけ `next` 関数が呼び出されるようにしてください。それは 1 回以上出現することがありますが、論理パスが重ならないときだけで、そうしないないとフックは決して解決されない、またはエラーが発生します。** 以下は、ユーザーが認証されていない場合、`/login` にリダレクトするための例です: +- **`next(error)`**: (2.4.0+) `next` に渡された引数が `Error` インスタンスである場合、ナビゲーションは中止され、エラーは `router.onError()` を介して登録されたコールバックに渡されます。 + +**与えられたナビゲーションガードを通過する任意のパスにおいて、常に 1 回だけ `next` 関数が呼び出されるようにしてください。それは 1 回以上出現することがありますが、論理パスが重ならないときだけで、そうしないないとフックは決して解決されない、またはエラーが発生します。** 以下は、ユーザーが認証されていない場合、`/login` にリダレクトするための例です: ```js // BAD @@ -65,7 +65,7 @@ router.beforeEach((to, from, next) => { グローバル after フックを登録することもできます。しかしながら、ガードとは異なり、これらのフックは `next` 関数を受け取らず、ナビゲーションに影響しません。 -``` js +```js router.afterEach((to, from) => { // ... }) @@ -75,7 +75,7 @@ router.afterEach((to, from) => { 直接ルート設定オブジェクトの `beforeEnter` ガードを定義することができます。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -99,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` でのコンポーネントインスタンスへのアクセスができます。 @@ -126,7 +126,7 @@ const Foo = { しかしながら、 `next` にコールバックを渡すことでインスタンスにアクセスすることができます。このコールバックはナビゲーションが確立した時に呼ばれ、コンポーネントインスタンスはそのコールバックの引数として渡されます。 -``` js +```js beforeRouteEnter (to, from, next) { next(vm => { // `vm` を通じてコンポーネントインスタンスにアクセス @@ -134,7 +134,7 @@ beforeRouteEnter (to, from, next) { } ``` -コールバックを `next` に渡すことをサポートするのは、`beforeRouteEnter` ガードだけであるということに注意してください。`beforeRouteUpdate` と `beforeRouteLeave` の場合、 `this` は既に利用可能です。したがって、コールバックを渡す必要はないので、*サポートされません*: +コールバックを `next` に渡すことをサポートするのは、`beforeRouteEnter` ガードだけであるということに注意してください。`beforeRouteUpdate` と `beforeRouteLeave` の場合、 `this` は既に利用可能です。したがって、コールバックを渡す必要はないので、_サポートされません_: ```js beforeRouteUpdate (to, from, next) { @@ -158,6 +158,7 @@ beforeRouteLeave (to, from, next) { ``` ## 完全なナビゲーション解決フロー + 1. ナビゲーションがトリガされる 2. 非アクティブ化されたコンポーネントで `beforeRouteLeave` ガードを呼ぶ 3. グローバル `beforeEach` ガードを呼ぶ diff --git a/docs/ja/guide/advanced/scroll-behavior.md b/docs/ja/guide/advanced/scroll-behavior.md index 5525f29cc..2e0d6f1ba 100644 --- a/docs/ja/guide/advanced/scroll-behavior.md +++ b/docs/ja/guide/advanced/scroll-behavior.md @@ -1,6 +1,6 @@ # スクロールの振る舞い - + クライアントサイドのルーティングを使っている時に、新しいルートに対してスクロールをトップへ移動させたいかもしれません、もしくは実際のページリロードがしているように history 要素のスクロールポジションを保持したいこともあるかもしれません。 `vue-router` ではこれらをさらによく実現できます。ルートナビゲーションにおけるスクロールの挙動を完全にカスタマイズすることができます。 @@ -8,7 +8,7 @@ ルーターインスタンスを作る時に、 `scrollBehavior` 関数を提供できます。 -``` js +```js const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { @@ -17,7 +17,7 @@ const router = new VueRouter({ }) ``` -`scrollBehavior` 関数は `to` と `from` のルートオブジェクトを受け取ります。第 3 引数の `savedPosition` は `popstate` ナビゲーション (ブラウザの戻る/進むボタンがトリガーされた) 時のみ利用可能です。 +`scrollBehavior` 関数は `to` と `from` のルートオブジェクトを受け取ります。第 3 引数の `savedPosition` は `popstate` ナビゲーション (ブラウザの戻る/進むボタンがトリガーされた) 時のみ利用可能です。 この関数はスクロールポジションオブジェクトを返すことができます。そのオブジェクトは以下のような形式です。 @@ -29,7 +29,7 @@ const router = new VueRouter({ 例: -``` js +```js scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } } @@ -39,7 +39,7 @@ scrollBehavior (to, from, savedPosition) { `savedPosition` を返すことは結果的に戻る/進むボタンを押してナビゲーションした時にネイティブのような挙動になります。 -``` js +```js scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition @@ -51,7 +51,7 @@ scrollBehavior (to, from, savedPosition) { もし"アンカーへスクロール"の振る舞いをシミュレートしたい場合は以下のようにしてください。 -``` js +```js scrollBehavior (to, from, savedPosition) { if (to.hash) { return { @@ -70,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 0bf82f52c..350429959 100644 --- a/docs/ja/guide/advanced/transitions.md +++ b/docs/ja/guide/advanced/transitions.md @@ -1,10 +1,10 @@ # トランジション - + 基本的に `` は動的コンポーネントなので、 `` コンポーネントを使うのと同じ方法でトランジションを適用することができます。 -``` html +```html @@ -16,7 +16,7 @@ 上記の使い方では全てのトランジションが全てのルートに対して適用されます。もし各ルートコンポーネントにそれぞれ違うトランジションを持たせたい場合は、代わりにルーターコンポーネント内で異なる名前で `` を使うことができます。 -``` js +```js const Foo = { template: ` @@ -38,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 ab4d05902..7c72d8ff3 100644 --- a/docs/ja/guide/essentials/dynamic-matching.md +++ b/docs/ja/guide/essentials/dynamic-matching.md @@ -1,10 +1,10 @@ # 動的ルートマッチング - + パターンを使って同じコンポーネントにルートをマップする必要がしばしばあるでしょう。例えば、 `User` コンポーネントは全てのユーザーに対して描画されるべきであるが、それぞれ異なるユーザー ID を持つ場合などです。`vue-router` ではパスの中の動的なセグメントを使用して実現できます。 -``` js +```js const User = { template: '
    User
    ' } @@ -21,7 +21,7 @@ const router = new VueRouter({ 動的セグメントはコロン `:` を使って表されます。ルートがマッチした時、この動的セグメントの値は全てのコンポーネント内で `this.$route.params` として利用可能になります。したがって、現在の `User` のテンプレートを次のように更新することで現在のユーザー ID を表示することができます。 -``` js +```js const User = { template: '
    User {{ $route.params.id }}
    ' } @@ -31,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) でご確認ください。 @@ -44,11 +44,11 @@ const User = { 同じコンポーネントでパラメーター変更を検知するためには、 `$route` オブジェクトを watch するだけです。 -``` js +```js const User = { template: '...', watch: { - $route (to, from) { + $route(to, from) { // ルートの変更の検知... } } @@ -57,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() を呼び出すのを忘れないでください } @@ -83,7 +83,7 @@ const User = { ``` _アスタリスク_ ルートを使用するときは、_アスタリスク_ ルートが最後になるようにルートを正しく順序付けてください。 -`{ path: '*' }` ルートは、通常クライアントサイドの404ページで使われます。_History モード_ を使用する場合は、[正しいサーバの設定](./history-mode.md)も同様にしっかりしてください。 +`{ path: '*' }` ルートは、通常クライアントサイドの 404 ページで使われます。_History モード_ を使用する場合は、[正しいサーバの設定](./history-mode.md)も同様にしっかりしてください。 _アスタリスク_ を使用するときは、 `pathMatch` と名付けられたパラメータは、自動的に `$route.params` に追加されます。_アスタリスク_ によってマッチされた url の残りを含みます: @@ -102,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 31dcad5e3..5bf831441 100644 --- a/docs/ja/guide/essentials/history-mode.md +++ b/docs/ja/guide/essentials/history-mode.md @@ -1,12 +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: [...] @@ -24,6 +24,9 @@ history モードを使用する時は、URL は "普通" に見えます e.g. ` #### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / @@ -51,21 +54,23 @@ const http = require('http') const fs = require('fs') const httpPort = 80 -http.createServer((req, res) => { - fs.readFile('index.html', 'utf-8', (err, content) => { - if (err) { - console.log('We cannot open "index.html" 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) @@ -77,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 @@ -128,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 727e055da..4169fd3b6 100644 --- a/docs/ja/guide/essentials/named-routes.md +++ b/docs/ja/guide/essentials/named-routes.md @@ -1,10 +1,10 @@ # 名前付きルート - + しばしば、名前を使ってルートを特定できるとより便利です。特にルートにリンクするときやナビゲーションを実行するときなどです。Router インスタンスを作成するときに `routes` オプションの中でルートに名前を付けることができます。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -18,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 61bc2e956..3a1a11c74 100644 --- a/docs/ja/guide/essentials/named-views.md +++ b/docs/ja/guide/essentials/named-views.md @@ -1,10 +1,10 @@ # 名前付きビュー - + しばしば、ネストをさせずに同時に複数の view を表示する必要があるでしょう。例えば、`sidebar` view と `main` view を使ったレイアウトを作成する時です。そんな時に名前付きビューは便利です。あなたの view に 1 つのアウトレットを持つのではなく、複数のそれぞれが名前付きの view を持つことができます。名前を持たない `router-view` はその名前として `default` が付与されます。 -``` html +```html @@ -12,7 +12,7 @@ 1 つの view は 1 つのコンポーネントを使って描画されます。したがって、同じルートに対する複数の view には複数のコンポーネントが必須になります。この `components` (s が付いている) オプションに注意してください。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -27,7 +27,7 @@ const router = new VueRouter({ }) ``` -この例の動作しているデモは +この例の動作しているデモは [こちら](https://jsfiddle.net/posva/6du90epg/) です。 ## ネストされた名前付きビュー @@ -58,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 a3ee7c884..abe202225 100644 --- a/docs/ja/guide/essentials/navigation.md +++ b/docs/ja/guide/essentials/navigation.md @@ -1,6 +1,6 @@ # プログラムによるナビゲーション - + 宣言的なナビゲーションとしてアンカータグを作成する `` がありますが、ルーターのインスタンスメソッドを使ったプログラムによる方法でもそれは可能です。 @@ -10,16 +10,15 @@ 異なる URL へ遷移するときに `router.push` が使えます。このメソッドは history スタックに新しいエントリを追加します。それによってユーザーがブラウザの戻るボタンをクリックした時に前の URL に戻れるようになります。 - このメソッドは `` をクリックした時に内部的に呼ばれています。つまり `` をクリックすることは `router.push(...)` を呼ぶことと等価です。 -| 宣言的 | プログラム的 | -|-------------|--------------| +| 宣言的 | プログラム的 | +| ------------------------- | ------------------ | | `` | `router.push(...)` | 引数は文字列のパス、もしくは、location を記述するオブジェクトが使えます。例: -``` js +```js // 文字列パス router.push('home') @@ -53,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 5a5a9a5d5..f62afa99b 100644 --- a/docs/ja/guide/essentials/nested-routes.md +++ b/docs/ja/guide/essentials/nested-routes.md @@ -1,6 +1,6 @@ # ネストされたルート - + 実際のアプリケーションの UI では通常複数のレベルの階層的にネストしたコンポーネントで構成されます。ネストされたコンポーネントの特定の構造に対して URL のセグメントを対応させることはよくあります。例: @@ -19,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: `
    @@ -52,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 がマッチした時に @@ -74,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 ada50ab4c..301bf083d 100644 --- a/docs/ja/guide/essentials/passing-props.md +++ b/docs/ja/guide/essentials/passing-props.md @@ -1,27 +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 }}
    ' @@ -50,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 } + } ] }) ``` @@ -62,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 216e75b14..3631c2d08 100644 --- a/docs/ja/guide/essentials/redirect-and-alias.md +++ b/docs/ja/guide/essentials/redirect-and-alias.md @@ -1,38 +1,37 @@ # リダイレクトとエイリアス - + ### リダイレクト `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 を返します + } + } ] }) ``` @@ -49,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 a61b585bd..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` を含めると自動的にインストールされます。 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/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 e5651ca2c..5b71b53d3 100644 --- a/docs/kr/guide/essentials/history-mode.md +++ b/docs/kr/guide/essentials/history-mode.md @@ -22,6 +22,9 @@ const router = new VueRouter({ #### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / 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 d0fcb741e..c21e5cb0c 100644 --- a/docs/ru/README.md +++ b/docs/ru/README.md @@ -1,7 +1,7 @@ --- home: true heroImage: /logo.png -actionText: Get Started → +actionText: Начать знакомство → actionLink: /ru/installation.html footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Morote --- diff --git a/docs/ru/api/README.md b/docs/ru/api/README.md index 151f155a2..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` @@ -165,7 +188,7 @@ sidebar: auto ### aria-current-value -- тип: `'page' | 'step' | 'location' | 'date' | 'time'` +- тип: `'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`. @@ -268,11 +291,11 @@ sidebar: auto Сигнатура: - ``` + ```ts type PositionDescriptor = { x: number, y: number } | { selector: string } | - ?{} + void type scrollBehaviorHandler = ( to: Route, @@ -319,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 @@ -398,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 Сигнатура: @@ -488,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/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 index e47ea7c8b..b7bcf9a7f 100644 --- a/docs/ru/guide/advanced/navigation-failures.md +++ b/docs/ru/guide/advanced/navigation-failures.md @@ -48,7 +48,7 @@ router ## Свойства _ошибок навигации_ -Все сбои навигации предоставляют доступ к свойствам `to` и `from`, чтобы отобразить текущее местоположение, а также местоположение места назначения для навигации, в которой произошёл сбой: +Все сбои навигации предоставляют доступ к свойствам `to` и `from`, отображающие для навигации в которой произошёл сбой местоположение места назначения и текущее местоположение соответственно: ```js // попытка получения доступа к странице администрирования diff --git a/docs/ru/guide/advanced/navigation-guards.md b/docs/ru/guide/advanced/navigation-guards.md index 4e1254a4e..a1a380237 100644 --- a/docs/ru/guide/advanced/navigation-guards.md +++ b/docs/ru/guide/advanced/navigation-guards.md @@ -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 25d6c1d2e..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 / 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 5e7ce264a..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, и установка произойдёт автоматически: diff --git a/docs/zh/README.md b/docs/zh/README.md index 4abd9d3c0..394bb9a88 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -6,7 +6,7 @@ actionLink: /zh/installation.html footer: MIT Licensed | Copyright © 2014-present Evan You, Eduardo San Martin Morote --- - + Vue Router 是 [Vue.js](http://cn.vuejs.org) 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有: diff --git a/docs/zh/api/README.md b/docs/zh/api/README.md index 561796df5..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
  • { - if (from === START_LOCATION) { + if (from === VueRouter.START_LOCATION) { // 初始导航 } }) @@ -422,7 +424,7 @@ const resolved: { ### router.addRoutes -*已废弃*:使用 [`route.addRoute()`](#router-addroute) 代替。 +*已废弃*:使用 [`router.addRoute()`](#router-addroute) 代替。 函数签名: diff --git a/docs/zh/guide/README.md b/docs/zh/guide/README.md index 095c439bc..f03de3092 100644 --- a/docs/zh/guide/README.md +++ b/docs/zh/guide/README.md @@ -6,15 +6,15 @@ 同时,所有的例子都将使用完整版的 Vue 以解析模板。更多细节请[移步这里](https://cn.vuejs.org/v2/guide/installation.html#运行时-编译器-vs-只包含运行时)。 ::: - + 用 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 51391c85b..8d55a1172 100644 --- a/docs/zh/guide/advanced/lazy-loading.md +++ b/docs/zh/guide/advanced/lazy-loading.md @@ -1,6 +1,6 @@ # 路由懒加载 - + 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。 @@ -8,13 +8,16 @@ 首先,可以将异步组件定义为返回一个 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 ``` @@ -24,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 }] }) ``` @@ -42,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 index 457876d9d..9f7288b1a 100644 --- a/docs/zh/guide/advanced/navigation-failures.md +++ b/docs/zh/guide/advanced/navigation-failures.md @@ -51,7 +51,7 @@ router.push('/admin').catch(failure => { ## *导航故障*的属性 -所有的导航故障都会有 `to` 和 `from` 属性,用来表达这次失败的导航的当前位置和目标位置。 +所有的导航故障都会有 `to` 和 `from` 属性,分别用来表达这次失败的导航的目标位置和当前位置。 ```js // 正在尝试访问 admin 页面 diff --git a/docs/zh/guide/advanced/navigation-guards.md b/docs/zh/guide/advanced/navigation-guards.md index 98cee01ed..f87993d86 100644 --- a/docs/zh/guide/advanced/navigation-guards.md +++ b/docs/zh/guide/advanced/navigation-guards.md @@ -8,13 +8,13 @@ 记住**参数或查询的改变并不会触发进入/离开的导航守卫**。你可以通过[观察 `$route` 对象](../essentials/dynamic-matching.md#响应路由参数的变化)来应对这些变化,或使用 `beforeRouteUpdate` 的组件内守卫。 - + ## 全局前置守卫 你可以使用 `router.beforeEach` 注册一个全局前置守卫: -``` js +```js const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { @@ -69,7 +69,7 @@ router.beforeEach((to, from, next) => { 你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 `next` 函数也不会改变导航本身: -``` js +```js router.afterEach((to, from) => { // ... }) @@ -79,7 +79,7 @@ router.afterEach((to, from) => { 你可以在路由配置上直接定义 `beforeEnter` 守卫: -``` js +```js const router = new VueRouter({ routes: [ { @@ -103,21 +103,21 @@ 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` } @@ -128,7 +128,7 @@ const Foo = { 不过,你可以通过传一个回调给 `next`来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。 -``` js +```js beforeRouteEnter (to, from, next) { next(vm => { // 通过 `vm` 访问组件实例 diff --git a/docs/zh/guide/advanced/scroll-behavior.md b/docs/zh/guide/advanced/scroll-behavior.md index 17eb4681e..a2eac879c 100644 --- a/docs/zh/guide/advanced/scroll-behavior.md +++ b/docs/zh/guide/advanced/scroll-behavior.md @@ -1,6 +1,6 @@ # 滚动行为 - + 使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 `vue-router` 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。 @@ -8,7 +8,7 @@ 当创建一个 Router 实例,你可以提供一个 `scrollBehavior` 方法: -``` js +```js const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { @@ -28,7 +28,7 @@ const router = new VueRouter({ 举例: -``` js +```js scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } } @@ -38,7 +38,7 @@ scrollBehavior (to, from, savedPosition) { 返回 `savedPosition`,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样: -``` js +```js scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition @@ -50,7 +50,7 @@ scrollBehavior (to, from, savedPosition) { 如果你要模拟“滚动到锚点”的行为: -``` js +```js scrollBehavior (to, from, savedPosition) { if (to.hash) { return { @@ -68,7 +68,7 @@ scrollBehavior (to, from, savedPosition) { 你也可以返回一个 Promise 来得出预期的位置描述: -``` js +```js scrollBehavior (to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { diff --git a/docs/zh/guide/advanced/transitions.md b/docs/zh/guide/advanced/transitions.md index d1197b007..2039bb6a9 100644 --- a/docs/zh/guide/advanced/transitions.md +++ b/docs/zh/guide/advanced/transitions.md @@ -1,22 +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: ` @@ -38,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 9eec0b5bb..831f3db0e 100644 --- a/docs/zh/guide/essentials/dynamic-matching.md +++ b/docs/zh/guide/essentials/dynamic-matching.md @@ -1,6 +1,6 @@ # 动态路由匹配 - + 我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 `User` 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 `vue-router` 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果: @@ -32,7 +32,7 @@ const User = { 你可以在一个路由中设置多段“路径参数”,对应的值都会设置到 `$route.params` 中。例如: -| 模式 | 匹配路径 | $route.params | +| 模式 | 匹配路径 | \$route.params | | ----------------------------- | ------------------- | -------------------------------------- | | /user/:username | /user/evan | `{ username: 'evan' }` | | /user/:username/post/:post_id | /user/evan/post/123 | `{ username: 'evan', post_id: '123' }` | @@ -61,7 +61,7 @@ const User = { ```js const User = { template: '...', - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { // react to route changes... // don't forget to call next() } @@ -102,4 +102,4 @@ this.$route.params.pathMatch // '/non-existing' ## 匹配优先级 -有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。 +有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:路由定义得越早,优先级就越高。 diff --git a/docs/zh/guide/essentials/history-mode.md b/docs/zh/guide/essentials/history-mode.md index e3b27ba5a..0f6a5e6ec 100644 --- a/docs/zh/guide/essentials/history-mode.md +++ b/docs/zh/guide/essentials/history-mode.md @@ -24,6 +24,9 @@ const router = new VueRouter({ #### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / @@ -126,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 da4481cf6..ca607386b 100644 --- a/docs/zh/guide/essentials/named-routes.md +++ b/docs/zh/guide/essentials/named-routes.md @@ -1,10 +1,10 @@ # 命名路由 - + 有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 `routes` 配置中给某个路由设置名称。 -``` js +```js const router = new VueRouter({ routes: [ { @@ -18,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 8d0d15a40..257abf267 100644 --- a/docs/zh/guide/essentials/nested-routes.md +++ b/docs/zh/guide/essentials/nested-routes.md @@ -1,6 +1,6 @@ # 嵌套路由 - + 实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如: @@ -19,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: `
    @@ -52,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 匹配成功, @@ -81,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 2d30d20f5..6ab202ca8 100644 --- a/docs/zh/guide/essentials/passing-props.md +++ b/docs/zh/guide/essentials/passing-props.md @@ -1,6 +1,6 @@ # 路由组件传参 - + 在组件中使用 `$route` 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。 @@ -8,20 +8,18 @@ **取代与 `$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 }}
    ' @@ -50,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 } + } ] }) ``` @@ -62,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/installation.md b/docs/zh/installation.md index d496daa03..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`,它会自动安装的: 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/index.html b/examples/index.html index 5f2cd4f32..2b41a845a 100644 --- a/examples/index.html +++ b/examples/index.html @@ -30,6 +30,7 @@

    Vue Router Examples

  • Keepalive View
  • Multiple Apps
  • Restart App
  • +
  • Composables
  • diff --git a/examples/webpack.config.js b/examples/webpack.config.js index 85b68fcf4..a74f22fb4 100644 --- a/examples/webpack.config.js +++ b/examples/webpack.config.js @@ -49,7 +49,8 @@ module.exports = { resolve: { alias: { vue: 'vue/dist/vue.esm.js', - 'vue-router': path.join(__dirname, '..', 'src') + 'vue-router': path.join(__dirname, '..', 'src'), + 'vue-router/composables': path.join(__dirname, '..', 'src/composables') } }, diff --git a/package.json b/package.json index 9af19922b..816a32865 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-router", - "version": "3.5.1", + "version": "3.6.5", "description": "Official router for Vue.js 2", "author": "Evan You", "license": "MIT", @@ -17,10 +17,32 @@ "files": [ "src", "dist/*.js", + "dist/*.mjs", "types/*.d.ts", + "composables.mjs", + "composables.js", + "composables.d.ts", "vetur/tags.json", "vetur/attributes.json" ], + "exports": { + ".": { + "import": { + "node": "./dist/vue-router.mjs", + "default": "./dist/vue-router.esm.js" + }, + "require": "./dist/vue-router.common.js", + "types": "./types/index.d.ts" + }, + "./composables": { + "import": "./composables.mjs", + "require": "./composables.js", + "types": "./composables.d.ts" + }, + "./dist/*": "./dist/*", + "./types/*": "./types/*", + "./package.json": "./package.json" + }, "vetur": { "tags": "vetur/tags.json", "attributes": "vetur/attributes.json" @@ -46,7 +68,8 @@ "docs": "vuepress dev docs", "docs:build": "vuepress build docs", "changelog": "conventional-changelog -p angular -r 2 -i CHANGELOG.md -s", - "release": "bash scripts/release.sh" + "release": "bash scripts/release.sh", + "commit": "git-cz" }, "gitHooks": { "pre-commit": "lint-staged", @@ -59,9 +82,11 @@ ] }, "devDependencies": { + "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-node-resolve": "^11.0.0", "@vuepress/plugin-pwa": "^1.5.3", "@vuepress/theme-vue": "^1.5.3", - "axios": "^0.19.0", + "axios": "^0.28.0", "babel-core": "^6.24.1", "babel-eslint": "^10.0.2", "babel-loader": "^7.1.3", @@ -70,7 +95,7 @@ "babel-preset-flow-vue": "^1.0.0", "browserstack-local": "^1.4.8", "buble": "^0.19.8", - "chromedriver": "^86.0.0", + "chromedriver": "^96.0.0", "conventional-changelog-cli": "^2.0.11", "cross-spawn": "^7.0.3", "css-loader": "^2.1.1", @@ -91,18 +116,16 @@ "path-to-regexp": "^1.8.0", "rollup": "^2.34.1", "rollup-plugin-buble": "^0.19.8", - "@rollup/plugin-commonjs": "^17.0.0", "rollup-plugin-flow-no-whitespace": "^1.0.0", - "@rollup/plugin-node-resolve": "^11.0.0", "rollup-plugin-replace": "^2.0.0", "rollup-watch": "^4.0.0", "selenium-server": "^3.141.59", "terser": "^4.2.0", - "typescript": "^3.5.2", - "vue": "^2.6.12", + "typescript": "^4.7.0", + "vue": "^2.7.0", "vue-loader": "^15.9.3", - "vue-server-renderer": "^2.6.12", - "vue-template-compiler": "^2.6.12", + "vue-server-renderer": "^2.7.0", + "vue-template-compiler": "^2.7.0", "vuepress": "^1.5.3", "vuepress-theme-vue": "^1.1.1", "webpack": "^4.35.2", diff --git a/scripts/release.sh b/scripts/release.sh index be596e04f..6b22ad516 100644 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -11,7 +11,7 @@ then # commit VERSION=$VERSION npm run build - git add dist + git add dist composables.* git commit -m "build: bundle $VERSION" npm version $VERSION --message "chore(release): %s" @@ -25,5 +25,5 @@ then # publish git push origin refs/tags/v$VERSION git push - npm publish + npm publish --tag legacy fi diff --git a/src/components/link.js b/src/components/link.js index 9c2a25784..821b69f02 100644 --- a/src/components/link.js +++ b/src/components/link.js @@ -189,7 +189,7 @@ export default { } } -function guardEvent (e) { +export function guardEvent (e: any) { // don't redirect with control keys if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return // don't redirect when preventDefault called diff --git a/src/composables/globals.js b/src/composables/globals.js new file mode 100644 index 000000000..8331f28ec --- /dev/null +++ b/src/composables/globals.js @@ -0,0 +1,34 @@ +import { + getCurrentInstance, + shallowReactive, + effectScope +} from 'vue' +import { throwNoCurrentInstance } from './utils' + +export function useRouter () { + if (process.env.NODE_ENV !== 'production') { + throwNoCurrentInstance('useRouter') + } + + return getCurrentInstance().proxy.$root.$router +} + +export function useRoute () { + if (process.env.NODE_ENV !== 'production') { + throwNoCurrentInstance('useRoute') + } + + const root = getCurrentInstance().proxy.$root + if (!root._$route) { + const route = effectScope(true).run(() => + shallowReactive(Object.assign({}, root.$router.currentRoute)) + ) + root._$route = route + + root.$router.afterEach(to => { + Object.assign(route, to) + }) + } + + return root._$route +} diff --git a/src/composables/guards.js b/src/composables/guards.js new file mode 100644 index 000000000..6312e9401 --- /dev/null +++ b/src/composables/guards.js @@ -0,0 +1,68 @@ +import { getCurrentInstance, onUnmounted } from 'vue' +import { throwNoCurrentInstance } from './utils' +import { useRouter } from './globals' + +export function onBeforeRouteUpdate (guard) { + if (process.env.NODE_ENV !== 'production') { + throwNoCurrentInstance('onBeforeRouteUpdate') + } + + return useFilteredGuard(guard, isUpdateNavigation) +} +function isUpdateNavigation (to, from, depth) { + const toMatched = to.matched + const fromMatched = from.matched + return ( + toMatched.length >= depth && + toMatched + .slice(0, depth + 1) + .every((record, i) => record === fromMatched[i]) + ) +} + +function isLeaveNavigation (to, from, depth) { + const toMatched = to.matched + const fromMatched = from.matched + return toMatched.length < depth || toMatched[depth] !== fromMatched[depth] +} + +export function onBeforeRouteLeave (guard) { + if (process.env.NODE_ENV !== 'production') { + throwNoCurrentInstance('onBeforeRouteLeave') + } + + return useFilteredGuard(guard, isLeaveNavigation) +} + +const noop = () => {} +function useFilteredGuard (guard, fn) { + const instance = getCurrentInstance() + const router = useRouter() + + let target = instance.proxy + // find the nearest RouterView to know the depth + while ( + target && + target.$vnode && + target.$vnode.data && + target.$vnode.data.routerViewDepth == null + ) { + target = target.$parent + } + + const depth = + target && target.$vnode && target.$vnode.data + ? target.$vnode.data.routerViewDepth + : null + + if (depth != null) { + const removeGuard = router.beforeEach((to, from, next) => { + return fn(to, from, depth) ? guard(to, from, next) : next() + }) + + onUnmounted(removeGuard) + return removeGuard + } + + return noop +} diff --git a/src/composables/index.js b/src/composables/index.js new file mode 100644 index 000000000..d1cefa8cf --- /dev/null +++ b/src/composables/index.js @@ -0,0 +1,3 @@ +export * from './guards' +export * from './globals' +export * from './useLink' diff --git a/src/composables/useLink.js b/src/composables/useLink.js new file mode 100644 index 000000000..237fd3355 --- /dev/null +++ b/src/composables/useLink.js @@ -0,0 +1,113 @@ +import { computed, unref } from 'vue' +import { guardEvent } from '../components/link' +import { throwNoCurrentInstance } from './utils' +import { useRouter, useRoute } from './globals' + +function includesParams (outer, inner) { + for (const key in inner) { + const innerValue = inner[key] + const outerValue = outer[key] + if (typeof innerValue === 'string') { + if (innerValue !== outerValue) return false + } else { + if ( + !Array.isArray(outerValue) || + outerValue.length !== innerValue.length || + innerValue.some((value, i) => value !== outerValue[i]) + ) { + return false + } + } + } + + return true +} + +// helpers from vue router 4 + +function isSameRouteLocationParamsValue (a, b) { + return Array.isArray(a) + ? isEquivalentArray(a, b) + : Array.isArray(b) + ? isEquivalentArray(b, a) + : a === b +} + +function isEquivalentArray (a, b) { + return Array.isArray(b) + ? a.length === b.length && a.every((value, i) => value === b[i]) + : a.length === 1 && a[0] === b +} + +export function isSameRouteLocationParams (a, b) { + if (Object.keys(a).length !== Object.keys(b).length) return false + + for (const key in a) { + if (!isSameRouteLocationParamsValue(a[key], b[key])) return false + } + + return true +} + +export function useLink (props) { + if (process.env.NODE_ENV !== 'production') { + throwNoCurrentInstance('useLink') + } + + const router = useRouter() + const currentRoute = useRoute() + + const resolvedRoute = computed(() => router.resolve(unref(props.to), currentRoute)) + + const activeRecordIndex = computed(() => { + const route = resolvedRoute.value.route + const { matched } = route + const { length } = matched + const routeMatched = matched[length - 1] + const currentMatched = currentRoute.matched + if (!routeMatched || !currentMatched.length) return -1 + const index = currentMatched.indexOf(routeMatched) + if (index > -1) return index + // possible parent record + const parentRecord = currentMatched[currentMatched.length - 2] + + return ( + // we are dealing with nested routes + length > 1 && + // if the parent and matched route have the same path, this link is + // referring to the empty child. Or we currently are on a different + // child of the same parent + parentRecord && parentRecord === routeMatched.parent + ) + }) + + const isActive = computed( + () => + activeRecordIndex.value > -1 && + includesParams(currentRoute.params, resolvedRoute.value.route.params) + ) + const isExactActive = computed( + () => + activeRecordIndex.value > -1 && + activeRecordIndex.value === currentRoute.matched.length - 1 && + isSameRouteLocationParams(currentRoute.params, resolvedRoute.value.route.params) + ) + + const navigate = e => { + const href = resolvedRoute.value.route + if (guardEvent(e)) { + return props.replace + ? router.replace(href) + : router.push(href) + } + return Promise.resolve() + } + + return { + href: computed(() => resolvedRoute.value.href), + route: computed(() => resolvedRoute.value.route), + isExactActive, + isActive, + navigate + } +} diff --git a/src/composables/utils.js b/src/composables/utils.js new file mode 100644 index 000000000..969d0f7c0 --- /dev/null +++ b/src/composables/utils.js @@ -0,0 +1,11 @@ +import { getCurrentInstance } from 'vue' + +// dev only warn if no current instance + +export function throwNoCurrentInstance (method) { + if (!getCurrentInstance()) { + throw new Error( + `[vue-router]: Missing current instance. ${method}() must be called inside