User Settings
-Hello App!
diff --git a/docs/kr/guide/advanced/lazy-loading.md b/docs/kr/guide/advanced/lazy-loading.md index dba772649..fb82b381c 100644 --- a/docs/kr/guide/advanced/lazy-loading.md +++ b/docs/kr/guide/advanced/lazy-loading.md @@ -6,39 +6,40 @@ Vue의 [비동기 컴포넌트](http://vuejs.org/guide/components.html#Async-Com 첫째, 비동기 컴포넌트는 Promise를 반환하는 팩토리 함수로 정의할 수 있습니다 (컴포넌트가 resolve 되어야함). -``` js -const Foo = () => Promise.resolve({ /* 컴포넌트 정의 */ }) +```js +const Foo = () => + Promise.resolve({ + /* 컴포넌트 정의 */ + }) ``` 둘째, Webpack 2에서 [dynamic import](https://github.com/tc39/proposal-dynamic-import)를 사용하여 코드 분할 포인트를 지정할 수 있습니다. -``` js +```js import('./Foo.vue') // returns a Promise - ``` +``` - > 참고: Babel을 사용하고 있는 경우 올바른 구문 분석을 위해 [syntax-dynamic-import](http://babeljs.io/docs/plugins/syntax-dynamic-import/) 플러그인을 추가해야합니다. +> 참고: Babel을 사용하고 있는 경우 올바른 구문 분석을 위해 [syntax-dynamic-import](http://babeljs.io/docs/plugins/syntax-dynamic-import/) 플러그인을 추가해야합니다. - 이 두 가지를 결합하여 Webpack에 의해 자동으로 코드 분할될 비동기 컴포넌트를 정의하는 방법입니다. +이 두 가지를 결합하여 Webpack에 의해 자동으로 코드 분할될 비동기 컴포넌트를 정의하는 방법입니다. - ``` js - const Foo = () => import('./Foo.vue') +```js +const Foo = () => import('./Foo.vue') ``` 라우트 설정에서 아무것도 바꿀 필요가 없습니다. `Foo`만 사용하면 됩니다. -``` js +```js const router = new VueRouter({ - routes: [ - { path: '/foo', component: Foo } - ] + routes: [{ path: '/foo', component: Foo }] }) ``` ### 같은 묶음으로 컴포넌트 그룹화하기 -때로는 동일한 라우트 아래에 중첩된 모든 컴포넌트를 동일한 비동기 묶음으로 그룹화 할 수 있습니다. 이를 위해 특수 주석 문법을 사용하는 이름(Webpack 2.4 이상 지원)을 제공하여 [이름을 가진 묶음](https://webpack.js.org/guides/code-splitting-async/#chunk-names)을 사용해야합니다. +때로는 동일한 라우트 아래에 중첩된 모든 컴포넌트를 동일한 비동기 묶음으로 그룹화 할 수 있습니다. 이를 위해 특수 주석 문법을 사용하는 이름(Webpack 2.4 이상 지원)을 제공하여 [이름을 가진 묶음](https://webpack.js.org/api/module-methods/#magic-comments)을 사용해야합니다. -``` js +```js const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') diff --git a/docs/kr/guide/essentials/getting-started.md b/docs/kr/guide/essentials/getting-started.md index 4e6ca1d61..91919aa1b 100644 --- a/docs/kr/guide/essentials/getting-started.md +++ b/docs/kr/guide/essentials/getting-started.md @@ -9,8 +9,8 @@ Vue.js와 vue-router로 단일 페이지 애플리케이션을 만드는 것은 ### HTML ``` html - - + +Hello App!
diff --git a/docs/kr/guide/essentials/history-mode.md b/docs/kr/guide/essentials/history-mode.md index 8645c45e1..5b71b53d3 100644 --- a/docs/kr/guide/essentials/history-mode.md +++ b/docs/kr/guide/essentials/history-mode.md @@ -4,7 +4,7 @@ 해시를 제거하기 위해 라우터의 **history 모드** 를 사용할 수 있습니다. `history.pushState` API를 활용하여 페이지를 다시 로드하지 않고도 URL 탐색을 할 수 있습니다. -``` js +```js const router = new VueRouter({ mode: 'history', routes: [...] @@ -22,6 +22,9 @@ const router = new VueRouter({ #### Apache ```apache +Первое приложение!
diff --git a/docs/ru/guide/advanced/data-fetching.md b/docs/ru/guide/advanced/data-fetching.md index c0582cd04..cec068029 100644 --- a/docs/ru/guide/advanced/data-fetching.md +++ b/docs/ru/guide/advanced/data-fetching.md @@ -81,14 +81,14 @@ export default { error: null } }, - beforeRouteEnter (to, from, next) { + beforeRouteEnter(to, from, next) { getPost(to.params.id, (err, post) => { next(vm => vm.setData(err, post)) }) }, // если путь изменяется, а компонент уже отображён, // то логика будет немного иной - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { this.post = null getPost(to.params.id, (err, post) => { this.setData(err, post) diff --git a/docs/ru/guide/advanced/lazy-loading.md b/docs/ru/guide/advanced/lazy-loading.md index b256c762c..ebaeffc1b 100644 --- a/docs/ru/guide/advanced/lazy-loading.md +++ b/docs/ru/guide/advanced/lazy-loading.md @@ -7,7 +7,10 @@ Во-первых, асинхронный компонент можно определить как функцию-фабрику, которая возвращает Promise (который должен разрешиться самим компонентом): ```js -const Foo = () => Promise.resolve({ /* определение компонента */ }) +const Foo = () => + Promise.resolve({ + /* определение компонента */ + }) ``` Во-вторых, с Webpack 2 мы можем использовать синтаксис [динамических импортов](https://github.com/tc39/proposal-dynamic-import) для указания точек разделения кода: @@ -30,15 +33,13 @@ const Foo = () => import('./Foo.vue') ```js const router = new VueRouter({ - routes: [ - { path: '/foo', component: Foo } - ] + routes: [{ path: '/foo', component: Foo }] }) ``` ## Группировка компонентов в одном фрагменте -Иногда может понадобиться объединить в одном фрагменте все компоненты, расположенные по определённому маршруту. Для этого можно указывать [имена фрагментов Webpack](https://webpack.js.org/guides/code-splitting-async/#chunk-names), используя специальный синтаксис комментариев (в версиях Webpack > 2.4): +Иногда может понадобиться объединить в одном фрагменте все компоненты, расположенные по определённому маршруту. Для этого можно указывать [имена фрагментов Webpack](https://webpack.js.org/api/module-methods/#magic-comments), используя специальный синтаксис комментариев (в версиях Webpack > 2.4): ```js const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') diff --git a/docs/ru/guide/advanced/meta.md b/docs/ru/guide/advanced/meta.md index 18eb2430a..298789a59 100644 --- a/docs/ru/guide/advanced/meta.md +++ b/docs/ru/guide/advanced/meta.md @@ -1,6 +1,6 @@ # Метаданные маршрутов -Вы можете добавить поле `meta` при определении маршрута: +Иногда может быть удобным добавить дополнительную информацию к маршрутам, например имена анимаций переходов, кто может получить доступ к маршруту и т.д. Этого можно достичь с помощью свойства `meta`, которое принимает объект свойств и к которому можно получить доступ на странице маршрута или в навигационных хуках. Свойства `meta` можно объявить так: ```js const router = new VueRouter({ diff --git a/docs/ru/guide/advanced/navigation-failures.md b/docs/ru/guide/advanced/navigation-failures.md new file mode 100644 index 000000000..b7bcf9a7f --- /dev/null +++ b/docs/ru/guide/advanced/navigation-failures.md @@ -0,0 +1,65 @@ +# Сбои при навигации + +> Добавлено в версии 3.4.0 + +При использовании `router-link` Vue Router вызывает `router.push` для запуска навигации. В большинстве случаев ожидаемое поведение ссылок заключается в переходе на новую страницу, но есть несколько ситуаций при которых пользователь остаётся на той же странице: + +- Пользователь уже находится на странице, на которую пытается перейти +- [Навигационный хук](./navigation-guards.md) прерывает навигацию вызовом `next(false)` +- [Навигационный хук](./navigation-guards.md) выбрасывает ошибку или вызывает `next(new Error())` + +При использовании компонента `router-link` **ни один из этих случаев не будет логироваться как ошибка**. Однако при использовании `router.push` или `router.replace` можно столкнуться с сообщением _"Uncaught (in promise) Error"_ после которого в консоли последует более конкретное сообщение. Давайте разберемся как отличать _сбои навигации_. + +::: tip История вопроса +В версии 3.2.0, _навигационные сбои_ доступны через два необязательных коллбэка `router.push`: `onComplete` и `onAbort`. Начиная с версии 3.1.0, `router.push` и `router.replace` возвращают _Promise_ если не указаны коллбэки `onComplete`/`onAbort`. Этот _Promise_ разрешается вместо вызова `onComplete` и отклоняется вместо вызова `onAbort`. + ::: + +## Обнаружение сбоев навигации + +_Сбой навигации_ будет экземпляром `Error` с парой дополнительных свойств. Проверить произошла ли ошибка в маршрутизаторе можно с помощью функции `isNavigationFailure`: + +```js +import VueRouter from 'vue-router' +const { isNavigationFailure, NavigationFailureType } = VueRouter + +// попытка перехода к странице администрирования +router + .push('/admin') + .catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + // отображение уведомления пользователю + showToast('Необходимо авторизоваться для доступа к панели администрирования') + } + }) +``` + +::: tip СОВЕТ +Если опустить второй параметр в `isNavigationFailure(failure)`, то будет проверяться только, является ли ошибка _сбоем навигации_. +::: + +## Тип `NavigationFailureType` + +Тип `NavigationFailureType` поможет разработчикам определять тип _навигационного сбоя_. Существует 4 различных типа: + +- `redirected`: внутри навигационного хука был вызван `next(newLocation)` для перенаправления в другое место. +- `aborted`: внутри навигационного хука был вызван `next(false)` для отмены навигации. +- `cancelled`: произошла полностью новая навигация до того, как текущая могла закончиться. Например, во время ожидания внутри навигационного хука был вызван `router.push`. +- `duplicated`: навигация была предотвращена, потому что уже находимся в месте назначения. + +## Свойства _ошибок навигации_ + +Все сбои навигации предоставляют доступ к свойствам `to` и `from`, отображающие для навигации в которой произошёл сбой местоположение места назначения и текущее местоположение соответственно: + +```js +// попытка получения доступа к странице администрирования +router + .push('/admin') + .catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + console.log(failure.to.path) // '/admin' + console.log(failure.from.path) // '/' + } + }) +``` + +Во всех случаях значения `to` и `from` будут объектами нормализованных маршрутов. diff --git a/docs/ru/guide/advanced/navigation-guards.md b/docs/ru/guide/advanced/navigation-guards.md index e5555d2e3..a1a380237 100644 --- a/docs/ru/guide/advanced/navigation-guards.md +++ b/docs/ru/guide/advanced/navigation-guards.md @@ -39,7 +39,7 @@ router.beforeEach((to, from, next) => { ```js // ПЛОХО router.beforeEach((to, from, next) => { - if (!isAuthenticated) next('/login') + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) // если пользователь не авторизован, то `next` будет вызываться дважды next() }) @@ -48,7 +48,7 @@ router.beforeEach((to, from, next) => { ```js // ХОРОШО router.beforeEach((to, from, next) => { - if (!isAuthenticated) next('/login') + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) else next() }) ``` @@ -98,12 +98,12 @@ const router = new VueRouter({ ```js const Foo = { template: `...`, - beforeRouteEnter (to, from, next) { + beforeRouteEnter(to, from, next) { // вызывается до подтверждения пути, соответствующего этому компоненту. // НЕ ИМЕЕТ доступа к контексту экземпляра компонента `this`, // так как к моменту вызова экземпляр ещё не создан! }, - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { // вызывается когда маршрут, что рендерит этот компонент изменился, // но этот компонент будет повторно использован в новом маршруте. // Например, для маршрута с динамическими параметрами `/foo/:id`, когда мы @@ -111,7 +111,7 @@ const Foo = { // будет использован повторно, и этот хук будет вызван когда это случится. // Также имеется доступ в `this` к экземпляру компонента. }, - beforeRouteLeave (to, from, next) { + beforeRouteLeave(to, from, next) { // вызывается перед переходом от пути, соответствующего текущему компоненту; // имеет доступ к контексту экземпляра компонента `this`. } @@ -123,17 +123,17 @@ const Foo = { Тем не менее, доступ к экземпляру можно получить, передав коллбэк в `next`. Эта функция будет вызвана после подтверждения навигации, а экземпляр компонента будет передан в неё в качестве параметра: ```js -beforeRouteEnter (to, from, next) { +beforeRouteEnter(to, from, next) { next(vm => { // экземпляр компонента доступен как `vm` }) } ``` -Обратите внимание, что `beforeRouteEnter` — единственный хук, который поддерживает передачу коллбэка в `next`. Для `beforeRouteUpdate` и `beforeRouteLeave`, `this` уже доступен, поэтому передача коллбэка не требуется и поэтому *не поддерживается*: +Обратите внимание, что `beforeRouteEnter` — единственный хук, который поддерживает передачу коллбэка в `next`. Для `beforeRouteUpdate` и `beforeRouteLeave`, `this` уже доступен, поэтому передача коллбэка не требуется и поэтому _не поддерживается_: ```js -beforeRouteUpdate (to, from, next) { +beforeRouteUpdate(to, from, next) { // просто используйте `this` this.name = to.params.name next() @@ -143,7 +143,7 @@ beforeRouteUpdate (to, from, next) { **Навигационный хук ухода со страницы** обычно используется для предотвращения случайного ухода пользователя со страницы с несохранёнными изменениями. Навигацию можно отменить вызовом `next(false)`. ```js -beforeRouteLeave (to, from, next) { +beforeRouteLeave(to, from, next) { const answer = window.confirm('Вы хотите уйти? У вас есть несохранённые изменения!') if (answer) { next() @@ -153,6 +153,18 @@ beforeRouteLeave (to, from, next) { } ``` +При использовании примесей, которые будут добавлять навигационные хуки для компонента, убедитесь, что объявляете примесь **после установки плагина vue-router**: + +```js +Vue.use(Router) + +Vue.mixin({ + beforeRouteUpdate(to, from, next) { + // ... + } +}) +``` + ## Полная цепочка обработки навигации 1. Срабатывание навигации. diff --git a/docs/ru/guide/advanced/scroll-behavior.md b/docs/ru/guide/advanced/scroll-behavior.md index 87ccac72f..7b1d8957c 100644 --- a/docs/ru/guide/advanced/scroll-behavior.md +++ b/docs/ru/guide/advanced/scroll-behavior.md @@ -9,7 +9,7 @@ ```js const router = new VueRouter({ routes: [...], - scrollBehavior (to, from, savedPosition) { + scrollBehavior(to, from, savedPosition) { // возвращаем требуемую позицию прокрутки } }) @@ -27,7 +27,7 @@ const router = new VueRouter({ Например: ```js -scrollBehavior (to, from, savedPosition) { +scrollBehavior(to, from, savedPosition) { return { x: 0, y: 0 } } ``` @@ -37,7 +37,7 @@ scrollBehavior (to, from, savedPosition) { Возврат `savedPosition` позволяет эмулировать нативное поведение браузера при использовании кнопок назад/вперёд: ```js -scrollBehavior (to, from, savedPosition) { +scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition } else { @@ -49,7 +49,7 @@ scrollBehavior (to, from, savedPosition) { Эмулировать поведение "прокрутки к якорю" на странице можно так: ```js -scrollBehavior (to, from, savedPosition) { +scrollBehavior(to, from, savedPosition) { if (to.hash) { return { selector: to.hash @@ -68,7 +68,7 @@ scrollBehavior (to, from, savedPosition) { Можно также вернуть Promise, который разрешится объектом с желаемой позицией прокрутки: ```js -scrollBehavior (to, from, savedPosition) { +scrollBehavior(to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ x: 0, y: 0 }) @@ -78,3 +78,18 @@ scrollBehavior (to, from, savedPosition) { ``` Это можно связать с событиями компонента transition на уровне страницы, чтобы реализовать такое поведение прокрутки, которое сочетается с анимациями перехода между страницами, но из-за множества возможных вариантов и комплексности примеров, мы просто предоставляем этот простой пример, чтобы показать где можно разместить собственную реализацию. + +## Плавная прокрутка + +Можно включить нативную плавную прокрутку для [браузеров, которые поддерживают её](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior), просто добавив опцию `behavior` к объекту, возвращаемому из `scrollBehavior`: + +```js +scrollBehavior(to, from, savedPosition) { + if (to.hash) { + return { + selector: to.hash, + behavior: 'smooth', + } + } +} +``` diff --git a/docs/ru/guide/essentials/dynamic-matching.md b/docs/ru/guide/essentials/dynamic-matching.md index 3e86a5925..ad7c5bcba 100644 --- a/docs/ru/guide/essentials/dynamic-matching.md +++ b/docs/ru/guide/essentials/dynamic-matching.md @@ -29,9 +29,9 @@ const User = { Может быть несколько динамических сегментов в одном маршруте. Для каждого сегмента появится соответствующее свойство в `$route.params`. Например: -| Шаблон | Совпадающий путь | $route.params | -|---------|------|--------| -| /user/:username | /user/evan | `{ username: 'evan' }` | +| Шаблон | Совпадающий путь | $route.params | +| ----------------------------- | ------------------- | -------------------------------------- | +| /user/:username | /user/evan | `{ username: 'evan' }` | | /user/:username/post/:post_id | /user/evan/post/123 | `{ username: 'evan', post_id: '123' }` | Кроме `$route.params`, объект `$route` предоставляют и другую полезную информацию, например `$route.query` (если URL содержит строку запроса), `$route.hash`, и т.д. Подробнее в [справочнике API](../../api/#объект-route). @@ -58,7 +58,7 @@ const User = { ```js const User = { template: '...', - beforeRouteUpdate (to, from, next) { + beforeRouteUpdate(to, from, next) { // обрабатываем изменение параметров маршрута... // не забываем вызвать next() } diff --git a/docs/ru/guide/essentials/history-mode.md b/docs/ru/guide/essentials/history-mode.md index 0f42bf028..289f08b78 100644 --- a/docs/ru/guide/essentials/history-mode.md +++ b/docs/ru/guide/essentials/history-mode.md @@ -24,6 +24,9 @@ const router = new VueRouter({ #### Apache ```apache +Hello App!
diff --git a/docs/zh/guide/advanced/lazy-loading.md b/docs/zh/guide/advanced/lazy-loading.md index f87191ad7..8d55a1172 100644 --- a/docs/zh/guide/advanced/lazy-loading.md +++ b/docs/zh/guide/advanced/lazy-loading.md @@ -1,18 +1,23 @@ # 路由懒加载 + + 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。 结合 Vue 的[异步组件](https://cn.vuejs.org/v2/guide/components-dynamic-async.html#异步组件)和 Webpack 的[代码分割功能](https://doc.webpack-china.org/guides/code-splitting-async/#require-ensure-/),轻松实现路由组件的懒加载。 首先,可以将异步组件定义为返回一个 Promise 的工厂函数 (该函数返回的 Promise 应该 resolve 组件本身): -``` js -const Foo = () => Promise.resolve({ /* 组件定义对象 */ }) +```js +const Foo = () => + Promise.resolve({ + /* 组件定义对象 */ + }) ``` 第二,在 Webpack 2 中,我们可以使用[动态 import](https://github.com/tc39/proposal-dynamic-import)语法来定义代码分块点 (split point): -``` js +```js import('./Foo.vue') // 返回 Promise ``` @@ -22,17 +27,15 @@ import('./Foo.vue') // 返回 Promise 结合这两者,这就是如何定义一个能够被 Webpack 自动代码分割的异步组件。 -``` js +```js const Foo = () => import('./Foo.vue') ``` 在路由配置中什么都不需要改变,只需要像往常一样使用 `Foo`: -``` js +```js const router = new VueRouter({ - routes: [ - { path: '/foo', component: Foo } - ] + routes: [{ path: '/foo', component: Foo }] }) ``` @@ -40,7 +43,7 @@ const router = new VueRouter({ 有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 [命名 chunk](https://webpack.js.org/guides/code-splitting-require/#chunkname),一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。 -``` js +```js const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') diff --git a/docs/zh/guide/advanced/navigation-failures.md b/docs/zh/guide/advanced/navigation-failures.md new file mode 100644 index 000000000..9f7288b1a --- /dev/null +++ b/docs/zh/guide/advanced/navigation-failures.md @@ -0,0 +1,66 @@ +# 导航故障 + +> 3.4.0中新增 + +::: tip 译者注 +*导航故障*,或者叫*导航失败*,表示一次失败的导航,原文叫 navigation failures,本文统一采用*导航故障*。 +::: + +当使用 `router-link` 组件时,Vue Router 会自动调用 `router.push` 来触发一次导航。 虽然大多数链接的预期行为是将用户导航到一个新页面,但也有少数情况下用户将留在同一页面上: + +- 用户已经位于他们正在尝试导航到的页面 +- 一个[导航守卫](./navigation-guards.md)通过调用 `next(false)` 中断了这次导航 +- 一个[导航守卫](./navigation-guards.md)抛出了一个错误,或者调用了 `next(new Error())` + +当使用 `router-link` 组件时,**这些失败都不会打印出错误**。然而,如果你使用 `router.push` 或者 `router.replace` 的话,可能会在控制台看到一条 _"Uncaught (in promise) Error"_ 这样的错误,后面跟着一条更具体的消息。让我们来了解一下如何区分*导航故障*。 + +::: tip 背景故事 +在 v3.2.0 中,可以通过使用 `router.push` 的两个可选的回调函数:`onComplete` 和 `onAbort` 来暴露*导航故障*。从版本 3.1.0 开始,`router.push` 和 `router.replace` 在没有提供 `onComplete`/`onAbort` 回调的情况下会返回一个 *Promise*。这个 *Promise* 的 resolve 和 reject 将分别用来代替 `onComplete` 和 `onAbort` 的调用。 +::: + + +## 检测导航故障 + +*导航故障*是一个 `Error` 实例,附带了一些额外的属性。要检查一个错误是否来自于路由器,可以使用 `isNavigationFailure` 函数: + +```js +import VueRouter from 'vue-router' +const { isNavigationFailure, NavigationFailureType } = VueRouter + +// 正在尝试访问 admin 页面 +router.push('/admin').catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + // 向用户显示一个小通知 + showToast('Login in order to access the admin panel') + } +}) +``` + +::: tip +如果你忽略第二个参数:`isNavigationFailure(failure)`,那么就只会检查这个错误是不是一个*导航故障*。 +::: + +## `NavigationFailureType` + +`NavigationFailureType` 可以帮助开发者来区分不同类型的*导航故障*。有四种不同的类型: + +- `redirected`:在导航守卫中调用了 `next(newLocation)` 重定向到了其他地方。 +- `aborted`:在导航守卫中调用了 `next(false)` 中断了本次导航。 +- `cancelled`:在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了 `router.push`。 +- `duplicated`:导航被阻止,因为我们已经在目标位置了。 + +## *导航故障*的属性 + +所有的导航故障都会有 `to` 和 `from` 属性,分别用来表达这次失败的导航的目标位置和当前位置。 + +```js +// 正在尝试访问 admin 页面 +router.push('/admin').catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.redirected)) { + failure.to.path // '/admin' + failure.from.path // '/' + } +}) +``` + +在所有情况下,`to` 和 `from` 都是规范化的路由位置。 diff --git a/docs/zh/guide/advanced/navigation-guards.md b/docs/zh/guide/advanced/navigation-guards.md index 28ff3908b..f87993d86 100644 --- a/docs/zh/guide/advanced/navigation-guards.md +++ b/docs/zh/guide/advanced/navigation-guards.md @@ -8,11 +8,13 @@ 记住**参数或查询的改变并不会触发进入/离开的导航守卫**。你可以通过[观察 `$route` 对象](../essentials/dynamic-matching.md#响应路由参数的变化)来应对这些变化,或使用 `beforeRouteUpdate` 的组件内守卫。 + + ## 全局前置守卫 你可以使用 `router.beforeEach` 注册一个全局前置守卫: -``` js +```js const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { @@ -38,7 +40,24 @@ router.beforeEach((to, from, next) => { - **`next(error)`**: (2.4.0+) 如果传入 `next` 的参数是一个 `Error` 实例,则导航会被终止且该错误会被传递给 [`router.onError()`](../../api/#router-onerror) 注册过的回调。 -**确保要调用 `next` 方法,否则钩子就不会被 resolved。** +**确保 `next` 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错**。这里有一个在用户未能验证身份时重定向到 `/login` 的示例: + +```js +// BAD +router.beforeEach((to, from, next) => { + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) + // 如果用户未能验证身份,则 `next` 会被调用两次 + next() +}) +``` + +```js +// GOOD +router.beforeEach((to, from, next) => { + if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) + else next() +}) +``` ## 全局解析守卫 @@ -50,7 +69,7 @@ router.beforeEach((to, from, next) => { 你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 `next` 函数也不会改变导航本身: -``` js +```js router.afterEach((to, from) => { // ... }) @@ -60,7 +79,7 @@ router.afterEach((to, from) => { 你可以在路由配置上直接定义 `beforeEnter` 守卫: -``` js +```js const router = new VueRouter({ routes: [ { @@ -84,32 +103,32 @@ const router = new VueRouter({ - `beforeRouteUpdate` (2.2 新增) - `beforeRouteLeave` -``` js +```js const Foo = { template: `...`, - beforeRouteEnter (to, from, next) { + beforeRouteEnter(to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例 `this` // 因为当守卫执行前,组件实例还没被创建 }, - beforeRouteUpdate (to, from, next) { - // 在当前路由改变,但是该组件被复用时调用 + beforeRouteUpdate(to, from, next) { + // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` }, - beforeRouteLeave (to, from, next) { + beforeRouteLeave(to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` } } ``` -`beforeRouteEnter` 守卫 **不能** 访问 `this`,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。 +`beforeRouteEnter` 守卫 **不能** 访问 `this`,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。 不过,你可以通过传一个回调给 `next`来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。 -``` js +```js beforeRouteEnter (to, from, next) { next(vm => { // 通过 `vm` 访问组件实例 @@ -143,7 +162,7 @@ beforeRouteLeave (to, from, next) { ## 完整的导航解析流程 1. 导航被触发。 -2. 在失活的组件里调用离开守卫。 +2. 在失活的组件里调用 `beforeRouteLeave` 守卫。 3. 调用全局的 `beforeEach` 守卫。 4. 在重用的组件里调用 `beforeRouteUpdate` 守卫 (2.2+)。 5. 在路由配置里调用 `beforeEnter`。 @@ -153,4 +172,4 @@ beforeRouteLeave (to, from, next) { 9. 导航被确认。 10. 调用全局的 `afterEach` 钩子。 11. 触发 DOM 更新。 -12. 用创建好的实例调用 `beforeRouteEnter` 守卫中传给 `next` 的回调函数。 +12. 调用 `beforeRouteEnter` 守卫中传给 `next` 的回调函数,创建好的组件实例会作为回调函数的参数传入。 diff --git a/docs/zh/guide/advanced/scroll-behavior.md b/docs/zh/guide/advanced/scroll-behavior.md index c30670362..a2eac879c 100644 --- a/docs/zh/guide/advanced/scroll-behavior.md +++ b/docs/zh/guide/advanced/scroll-behavior.md @@ -1,12 +1,14 @@ # 滚动行为 + + 使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 `vue-router` 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。 **注意: 这个功能只在支持 `history.pushState` 的浏览器中可用。** 当创建一个 Router 实例,你可以提供一个 `scrollBehavior` 方法: -``` js +```js const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { @@ -26,7 +28,7 @@ const router = new VueRouter({ 举例: -``` js +```js scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } } @@ -36,7 +38,7 @@ scrollBehavior (to, from, savedPosition) { 返回 `savedPosition`,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样: -``` js +```js scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition @@ -48,7 +50,7 @@ scrollBehavior (to, from, savedPosition) { 如果你要模拟“滚动到锚点”的行为: -``` js +```js scrollBehavior (to, from, savedPosition) { if (to.hash) { return { @@ -66,7 +68,7 @@ scrollBehavior (to, from, savedPosition) { 你也可以返回一个 Promise 来得出预期的位置描述: -``` js +```js scrollBehavior (to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { @@ -77,3 +79,18 @@ scrollBehavior (to, from, savedPosition) { ``` 将其挂载到从页面级别的过渡组件的事件上,令其滚动行为和页面过渡一起良好运行是可能的。但是考虑到用例的多样性和复杂性,我们仅提供这个原始的接口,以支持不同用户场景的具体实现。 + +## 平滑滚动 + +只需将 `behavior` 选项添加到 `scrollBehavior` 内部返回的对象中,就可以为[支持它的浏览器](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior)启用原生平滑滚动: + +```js +scrollBehavior (to, from, savedPosition) { + if (to.hash) { + return { + selector: to.hash, + behavior: 'smooth', + } + } +} +``` diff --git a/docs/zh/guide/advanced/transitions.md b/docs/zh/guide/advanced/transitions.md index f71869df5..2039bb6a9 100644 --- a/docs/zh/guide/advanced/transitions.md +++ b/docs/zh/guide/advanced/transitions.md @@ -1,20 +1,22 @@ # 过渡动效 + + `{{ n }}@@ -68,3 +108,8 @@ new Vue({ } } }).$mount('#app') + +document.getElementById('unmount').addEventListener('click', () => { + vueInstance.$destroy() + vueInstance.$el.innerHTML = '' +}) diff --git a/examples/basic/index.html b/examples/basic/index.html index 78a0c040f..695d668f5 100644 --- a/examples/basic/index.html +++ b/examples/basic/index.html @@ -1,6 +1,8 @@ ← Examples index + +
diff --git a/examples/composables/app.js b/examples/composables/app.js new file mode 100644 index 000000000..1762aeea0 --- /dev/null +++ b/examples/composables/app.js @@ -0,0 +1,146 @@ +import Vue, { defineComponent, watch, ref } from 'vue' +import VueRouter from 'vue-router' +import { + useRoute, + useRouter, + onBeforeRouteLeave, + onBeforeRouteUpdate, + useLink +} from 'vue-router/composables' + +Vue.use(VueRouter) + +const Foo = defineComponent({ + setup () { + const route = useRoute() + onBeforeRouteUpdate((to, from, next) => { + console.log('Foo updating') + next() + }) + onBeforeRouteLeave((to, from, next) => { + console.log('Foo leaving') + next() + }) + + return { route } + }, + template: ` +
Foo
+ {{ route.fullPath }} +Home
+{{ startRoute }}
+{{ watchCount }}
+{{ route.fullPath }}
+ ++
About
+{{ route.fullPath }}
+Basic
+-
+
/
+ /about
+ /nested
+ /nested/a
+
{{ href }}: {{ isActive }}, {{ isExactActive }}+
+ + + diff --git a/examples/hash-mode/app.js b/examples/hash-mode/app.js index c8081cf2a..16475ffc8 100644 --- a/examples/hash-mode/app.js +++ b/examples/hash-mode/app.js @@ -1,6 +1,30 @@ import Vue from 'vue' import VueRouter from 'vue-router' +// track number of popstate listeners +let numPopstateListeners = 0 +const listenerCountDiv = document.createElement('div') +listenerCountDiv.id = 'popstate-count' +listenerCountDiv.textContent = numPopstateListeners + ' popstate listeners' +document.body.appendChild(listenerCountDiv) + +const originalAddEventListener = window.addEventListener +const originalRemoveEventListener = window.removeEventListener +window.addEventListener = function (name, handler) { + if (name === 'popstate') { + listenerCountDiv.textContent = + ++numPopstateListeners + ' popstate listeners' + } + return originalAddEventListener.apply(this, arguments) +} +window.removeEventListener = function (name, handler) { + if (name === 'popstate') { + listenerCountDiv.textContent = + --numPopstateListeners + ' popstate listeners' + } + return originalRemoveEventListener.apply(this, arguments) +} + // 1. Use plugin. // This installs
{{ $route.query.t }}
{{ $route.hash }}
diff --git a/examples/index.html b/examples/index.html index c0ee5bc16..2b41a845a 100644 --- a/examples/index.html +++ b/examples/index.html @@ -18,6 +18,7 @@