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 88598c736..520f3ff13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,82 @@ +## [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 diff --git a/README.md b/README.md index f34ff93e5..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,74 +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) - - +

Gold Sponsors

-

- - Passionate People - - - - VueJobs + + + + VueJobs +

Silver Sponsors

-

- - Vue Mastery - - - - Vuetify - - - - CodeStream + + + + VueMastery + - - - Bird Eats bug + + + + 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 - + +

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 08df6f308..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 / 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 5ad2c12a0..017faa45f 100644 --- a/docs/guide/README.md +++ b/docs/guide/README.md @@ -1,6 +1,8 @@ # 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). @@ -13,8 +15,8 @@ Creating a Single-page Application with Vue + Vue Router feels natural: with Vue ## HTML ```html - - + +

Hello App!

diff --git a/docs/guide/advanced/transitions.md b/docs/guide/advanced/transitions.md index ce2d5c813..0b8a937df 100644 --- a/docs/guide/advanced/transitions.md +++ b/docs/guide/advanced/transitions.md @@ -10,7 +10,7 @@ Since the `` is essentially a dynamic component, we can apply trans ``` -[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 diff --git a/docs/guide/essentials/history-mode.md b/docs/guide/essentials/history-mode.md index 4e570374b..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 / 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/api/README.md b/docs/ja/api/README.md index d56b8c890..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 - + +

Hello App!

diff --git a/docs/ja/guide/essentials/history-mode.md b/docs/ja/guide/essentials/history-mode.md index 8352471e6..5bf831441 100644 --- a/docs/ja/guide/essentials/history-mode.md +++ b/docs/ja/guide/essentials/history-mode.md @@ -24,6 +24,9 @@ history モードを使用する時は、URL は "普通" に見えます e.g. ` #### Apache ```apache + + Options -MultiViews + RewriteEngine On RewriteBase / 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/guide/README.md b/docs/ru/guide/README.md index beea6bb0e..cf31e84d5 100644 --- a/docs/ru/guide/README.md +++ b/docs/ru/guide/README.md @@ -11,8 +11,8 @@ ## HTML ```html - - + +

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

diff --git a/docs/ru/guide/essentials/dynamic-matching.md b/docs/ru/guide/essentials/dynamic-matching.md index 1cef1351c..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). 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/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/guide/README.md b/docs/zh/guide/README.md index 4c5348820..f03de3092 100644 --- a/docs/zh/guide/README.md +++ b/docs/zh/guide/README.md @@ -13,8 +13,8 @@ ## HTML ```html - - + +

Hello App!

diff --git a/docs/zh/guide/advanced/transitions.md b/docs/zh/guide/advanced/transitions.md index 877553486..2039bb6a9 100644 --- a/docs/zh/guide/advanced/transitions.md +++ b/docs/zh/guide/advanced/transitions.md @@ -10,7 +10,7 @@ ``` -[Transition 的所有功能](https://cn.vuejs.org/guide/transitions.html) 在这里同样适用。 +[Transition 的所有功能](https://v2.cn.vuejs.org/v2/guide/transitions.html) 在这里同样适用。 ## 单个路由的过渡 diff --git a/docs/zh/guide/essentials/history-mode.md b/docs/zh/guide/essentials/history-mode.md index 74351d9bd..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 / 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/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 213b2e93a..816a32865 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-router", - "version": "3.5.2", + "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" @@ -64,7 +86,7 @@ "@rollup/plugin-node-resolve": "^11.0.0", "@vuepress/plugin-pwa": "^1.5.3", "@vuepress/theme-vue": "^1.5.3", - "axios": "^0.21.1", + "axios": "^0.28.0", "babel-core": "^6.24.1", "babel-eslint": "^10.0.2", "babel-loader": "^7.1.3", @@ -73,7 +95,7 @@ "babel-preset-flow-vue": "^1.0.0", "browserstack-local": "^1.4.8", "buble": "^0.19.8", - "chromedriver": "^90.0.0", + "chromedriver": "^96.0.0", "conventional-changelog-cli": "^2.0.11", "cross-spawn": "^7.0.3", "css-loader": "^2.1.1", @@ -99,11 +121,11 @@ "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