Spec-Zone.ru › Vue Router 4

Миграция с Vue 2

Большая часть API Vue Router осталась неизменной во время переписывания с версии v3 (для Vue 2) на v4 (для Vue 3), но всё же есть несколько критических изменений, с которыми вы можете столкнуться при миграции приложения. Это руководство поможет вам понять, почему произошли эти изменения и как адаптировать ваше приложение для работы с Vue Router 4.

Критические изменения

Изменения упорядочены по частоте использования. Поэтому рекомендуется следовать этому списку в порядке.

new Router стало createRouter

Vue Router больше не является классом, а представляет собой набор функций. Вместо того чтобы писать new Router(), теперь вы должны вызвать createRouter:

// previously was
// import Router from 'vue-router'
import { createRouter } from 'vue-router'

const router = createRouter({
  // ...
})

Новый history параметр для замены mode

Параметр mode: 'history' был заменён более гибким параметром под названием history. В зависимости от используемого вами режима, вам нужно будет заменить его соответствующей функцией:

  • "history": createWebHistory()
  • "hash": createWebHashHistory()
  • "abstract": createMemoryHistory()

Вот полный фрагмент:

import { createRouter, createWebHistory } from 'vue-router'
// there is also createWebHashHistory and createMemoryHistory

createRouter({
  history: createWebHistory(),
  routes: [],
})

При SSR необходимо вручную передать соответствующую историю:

// router.js
let history = isServer ? createMemoryHistory() : createWebHistory()
let router = createRouter({ routes, history })
// somewhere in your server-entry.js
router.push(req.url) // request url
router.isReady().then(() => {
  // resolve the request
})

Причина: возможность исключения неиспользуемых историй с помощью tree shaking, а также реализация пользовательских историй для расширенных случаев использования, таких как нативные решения.

Перемещение параметра base

Параметр base теперь передаётся в качестве первого аргумента в createWebHistory (и другие истории):

import { createRouter, createWebHistory } from 'vue-router'
createRouter({
  history: createWebHistory('/base-directory/'),
  routes: [],
})

Удаление параметра fallback

Параметр fallback больше не поддерживается при создании маршрутизатора:

-new VueRouter({
+createRouter({
-  fallback: false,
// other options...
})

Причина: все браузеры, поддерживаемые Vue, поддерживают API истории HTML5, что позволяет избежать обходных путей с модификацией location.hash и напрямую использовать history.pushState().

Удаление маршрутов «все» (звёздочка или catch all)

Маршруты «все» (*, /* необходимо теперь определять с помощью параметра со специальным регулярным выражением:

const routes = [
  // pathMatch is the name of the param, e.g., going to /not/found yields
  // { params: { pathMatch: ['not', 'found'] }}
  // this is thanks to the last *, meaning repeated params and it is necessary if you
  // plan on directly navigating to the not-found route using its name
  { path: '/:pathMatch(.*)*', name: 'not-found', component: NotFound },
  // if you omit the last `*`, the `/` character in params will be encoded when resolving or pushing
  { path: '/:pathMatch(.*)', name: 'bad-not-found', component: NotFound },
]
// bad example if using named routes:
router.resolve({
  name: 'bad-not-found',
  params: { pathMatch: 'not/found' },
}).href // '/not%2Ffound'
// good example:
router.resolve({
  name: 'not-found',
  params: { pathMatch: ['not', 'found'] },
}).href // '/not/found'
СОВЕТ

Вам не нужно добавлять * для повторяющихся параметров, если вы не планируете напрямую переходить на маршрут не найден используя его имя. Если вы вызываете router.push('/not/found/url'), он предоставит правильный параметр pathMatch.

Причина: Vue Router больше не использует path-to-regexp; вместо этого он реализует собственную систему разбора, позволяющую сортировать маршруты и использовать динамическое маршрутизирование. Поскольку мы обычно добавляем один единственный маршрут «все» в проект, нет большой выгоды в поддержке специального синтаксиса для *.

Замена onReady на isReady

Существующая функция router.onReady() была заменена на router.isReady(), которая не принимает аргументов и возвращает промис:

// replace
router.onReady(onSuccess, onError)
// with
router.isReady().then(onSuccess).catch(onError)
// or use await:
try {
  await router.isReady()
  // onSuccess
} catch (err) {
  // onError
}

Изменения в scrollBehavior

Объект, возвращаемый в scrollBehavior, теперь похож на ScrollToOptions: x переименовано в left, а y переименовано в top. Смотрите RFC.

Причина: сходство объекта с ScrollToOptions для большей схожести с нативными JS API и потенциальной возможности добавления новых параметров в будущем.

<router-view>, <keep-alive>, и <transition>

transition и keep-alive теперь необходимо использовать **внутри** RouterView через API v-slot:

<router-view v-slot="{ Component }">
  <transition>
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </transition>
</router-view>

Причина: Это необходимое изменение. Смотрите соответствующий RFC.

Удаление параметра append в <router-link>

Параметр append был удалён из <router-link>. Вместо этого вы можете вручную конкатенировать значение с существующим path:

replace
<router-link to="child-route" append>to relative child</router-link>
with
<router-link :to="append($route.path, 'child-route')">
  to relative child
</router-link>

Вы должны определить глобальную функцию append в вашем экземпляре App:

app.config.globalProperties.append = (path, pathToAppend) =>
  path + (path.endsWith('/') ? '' : '/') + pathToAppend

Причина: append не часто использовался и легко дублируется в пользовательском коде.

Удаление параметров event и tag в <router-link>

Оба параметра event, и tag были удалены из <router-link>. Вы можете использовать v-slot API для полной настройки <router-link>:

replace
<router-link to="/about" tag="span" event="dblclick">About Us</router-link>
with
<router-link to="/about" custom v-slot="{ navigate }">
  <span @click="navigate" @keypress.enter="navigate" role="link">About Us</span>
</router-link>

Причина: Эти параметры часто использовались вместе для использования чего-то отличного от тега <a>, но были введены до API v-slot и недостаточно используются, чтобы оправдать увеличение размера сборки для всех.

Удаление параметра exact в <router-link>

Параметр exact был удалён, так как проблема, которую он решал, больше не актуальна. Однако необходимо учитывать два момента:

  • Маршруты теперь активны на основе записей маршрутов, а не сгенерированных объектов расположения маршрутов и их свойств path, query, и hash
  • Сопоставляется только раздел path, query, и hash больше не учитываются.

Если вам необходимо настроить это поведение, например, учитывать раздел hash, используйте v-slot API для расширения <router-link>.

Причина: см. RFC о изменениях в сопоставлении активных ссылок для получения дополнительных сведений.

Навигационные гварды в миксинах игнорируются

В настоящее время навигационные гварды в миксинах не поддерживаются. Следите за поддержкой на vue-router#454.

Удаление router.match и изменения в router.resolve

router.match, и router.resolve объединены в router.resolve с немного изменённой сигнатурой. См. API для получения дополнительных сведений.

Причина: Объединение нескольких методов, которые использовались для одной цели.

Удаление router.getMatchedComponents()

Метод router.getMatchedComponents теперь удалён, так как соответствующие компоненты можно получить из router.currentRoute.value.matched:

router.currentRoute.value.matched.flatMap(record =>
  Object.values(record.components)
)

Причина: Этот метод использовался только при SSR и представляет собой одну строку кода, которую пользователь может легко реализовать самостоятельно.

**Все** переходы теперь всегда асинхронны

Все переходы, включая первый, теперь асинхронные. Это означает, что при использовании transition, возможно, потребуется подождать, пока маршрутизатор не станет готовым, прежде чем монтировать приложение:

app.use(router)
// Note: on Server Side, you need to manually push the initial location
router.isReady().then(() => app.mount('#app'))

В противном случае произойдёт начальный переход, как если бы вы передали параметр appear к transition, поскольку маршрутизатор отображает своё начальное расположение (ничего), а затем отображает первое расположение.

Обратите внимание, что если у вас есть навигационные гварды при начальной навигации, вам может не потребоваться блокировать рендер приложения, пока они не будут решены, если вы не выполняете рендеринг на стороне сервера. В этом случае ожидание готовности маршрутизатора для монтирования приложения даст тот же результат, что и в Vue 2.

Удаление router.app

router.app раньше представлял собой последний корневой компонент (экземпляр Vue), в который был внедрён маршрутизатор. Теперь Vue Router можно безопасно использовать в нескольких приложениях Vue одновременно. Вы по-прежнему можете добавить его при использовании маршрутизатора:

app.use(router)
router.app = app

Вы также можете расширить определение TypeScript интерфейса Router для добавления свойства app.

Причина: Приложения Vue 3 не существуют в Vue 2, и теперь мы правильно поддерживаем несколько приложений, использующих один и тот же экземпляр маршрутизатора. Поэтому наличие свойства app было бы вводящим в заблуждение, поскольку оно относилось бы к приложению, а не к корневому экземпляру.

Передача содержимого компонентам маршрута через <slot>

Ранее вы могли напрямую передавать шаблон для рендеринга компонентами маршрута через <slot>, вкладывая его в компонент <router-view>:

<router-view>
  <p>In Vue Router 3, I render inside the route component</p>
</router-view>

Из-за введения API v-slot для <router-view>, вы должны передавать его компоненту <component> с использованием API v-slot:

<router-view v-slot="{ Component }">
  <component :is="Component">
    <p>In Vue Router 3, I render inside the route component</p>
  </component>
</router-view>

Удаление parent из расположений маршрутов

Свойство parent было удалено из нормализованных расположений маршрутов (this.$route и объект, возвращаемый router.resolve). Вы по-прежнему можете получить к нему доступ через массив matched:

const parent = this.$route.matched[this.$route.matched.length - 2]

Причина: Наличие parent и children создаёт ненужные циклические ссылки, в то время как свойства можно было получить через matched.

Удаление pathToRegexpOptions

Свойства pathToRegexpOptions и caseSensitive записей маршрутов были заменены опциями sensitive и strict для createRouter(). Теперь их также можно напрямую передавать при создании роутера с помощью createRouter(). Любые другие опции, специфичные для path-to-regexp были удалены, так как path-to-regexp больше не используется для парсинга путей.

Удаление безымянных параметров

Из-за удаления path-to-regexp безымянные параметры больше не поддерживаются:

  • /foo(/foo)?/suffix становится /foo/:_(foo)?/suffix
  • /foo(foo)? становится /foo:_(foo)?
  • /foo/(.*) становится /foo/:_(.*)
СОВЕТ

Обратите внимание, что вы можете использовать любое имя вместо _ для параметра. Главное — предоставить имя.

Использование history.state

Vue Router сохраняет информацию в history.state. Если у вас есть код, который вручную вызывает history.pushState(), вам, вероятно, следует избегать этого или переписать его с помощью обычного router.push() и history.replaceState():

// replace
history.pushState(myState, '', url)
// with
await router.push(url)
history.replaceState({ ...history.state, ...myState }, '')

Аналогично, если вы вызывали history.replaceState() без сохранения текущего состояния, вам необходимо передать текущее history.state:

// replace
history.replaceState({}, '', url)
// with
history.replaceState(history.state, '', url)

Причина: Мы используем состояние истории для сохранения информации о навигации, такой как позиция прокрутки, предыдущее местоположение и т.д.

Опция routes обязательна в options

Свойство routes теперь является обязательным в options.

createRouter({ routes: [] })

Причина: Роутер разработан для создания с маршрутами, хотя вы можете добавить их позже. Вам в большинстве сценариев нужен хотя бы один маршрут, и это записывается один раз на приложение в целом.

Отсутствующие именованные маршруты

Попытка перехода или разрешения несуществующего именованного маршрута вызывает ошибку:

// Oops, we made a typo in name
router.push({ name: 'homee' }) // throws
router.resolve({ name: 'homee' }) // throws

Причина: Ранее роутер переходил на /, но ничего не отображал (вместо домашней страницы). Вызов ошибки более целесообразен, так как мы не можем создать допустимый URL для перехода.

Отсутствующие обязательные params в именованных маршрутах

Попытка перехода или разрешения именованного маршрута без его обязательных параметров вызовет ошибку:

// given the following route:
const routes = [{ path: '/users/:id', name: 'user', component: UserDetails }]

// Missing the `id` param will fail
router.push({ name: 'user' })
router.resolve({ name: 'user' })

Причина: То же самое, что и выше.

Именованные дочерние маршруты с пустым path больше не добавляют слеш

Рассмотрим вложенный именованный маршрут с пустым path:

const routes = [
  {
    path: '/dashboard',
    name: 'dashboard-parent',
    component: DashboardParent,
    children: [
      { path: '', name: 'dashboard', component: DashboardDefault },
      {
        path: 'settings',
        name: 'dashboard-settings',
        component: DashboardSettings,
      },
    ],
  },
]

Переход или разрешение именованного маршрута dashboard теперь приведет к URL без конечного слэша:

router.resolve({ name: 'dashboard' }).href // '/dashboard'

Это имеет важное последствие для записей дочерних redirect маршрутов, например:

const routes = [
  {
    path: '/parent',
    component: Parent,
    children: [
      // this would now redirect to `/home` instead of `/parent/home`
      { path: '', redirect: 'home' },
      { path: 'home', component: Home },
    ],
  },
]

Обратите внимание, что это будет работать, если path был /parent/, так как относительное расположение home к /parent/ действительно /parent/home, но относительное расположение home к /parent равно /home.

Причина: Это сделано для согласованности поведения с конечным слешем: по умолчанию все маршруты допускают конечный слеш. Его можно отключить с помощью опции strict и вручную добавлять (или не добавлять) слеш к маршрутам.

Кодировка свойств маршрута

Декодированные значения в params, query, и hash теперь согласованы независимо от того, где инициирована навигация (старые браузеры всё ещё будут генерировать не закодированные path и fullPath). Начальная навигация должна давать те же результаты, что и навигация в приложении.

Рассмотрим нормализованное расположение маршрута:

  • Значения в path, fullPath больше не декодируются. Они будут отображаться так, как предоставил браузер (большинство браузеров предоставляют их закодированными). Например, прямой ввод в адресной строке https://example.com/hello world даст закодированную версию: https://example.com/hello%20world, и как path, так и fullPath будут /hello%20world.
  • hash теперь декодируется, что позволяет скопировать его: router.push({ hash: $route.hash }) и использовать непосредственно в опции el scrollBehavior.
  • При использовании push, resolve, и replace и предоставлении расположения string или свойства path в объекте, оно должно быть закодировано (как в предыдущей версии). С другой стороны, params, query и hash должны быть предоставлены в не закодированном виде.
  • Символ слеша (/) теперь правильно декодируется внутри params, при этом на URL всё ещё генерируется закодированная версия: %2F.

Причина: Это позволяет легко копировать существующие свойства расположения при вызове router.push() и router.resolve(), и делает результирующее расположение маршрута согласованным в разных браузерах. router.push() теперь идемпотентен, что означает, что вызов router.push(route.fullPath), router.push({ hash: route.hash }), router.push({ query: route.query }), и router.push({ params: route.params }) не создаст дополнительной кодировки.

Изменения в TypeScript

Для повышения согласованности и выразительности типов некоторые типы были переименованы:

vue-router@3 vue-router@4
RouteConfig RouteRecordRaw
Location RouteLocation
Route RouteLocationNormalized

Новые возможности

Некоторые новые возможности Vue Router 4, которые стоит изучить:

  • Динамический маршрут
  • API композиций
Предложить изменения на этой странице
Динамический маршрут

© 2013–present Evan You
Licensed under the MIT License.
https://next.router.vuejs.org/guide/migration/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API