Skip to Content
VueРоутер, Pinia, жизненный цикл

Роутер, Pinia и жизненный цикл

Уровень: middle.

Вопрос

Чем Pinia отличается от Vuex и какие хуки меняются при переходе на Vue 3?

Короткий ответ

Pinia — штатное хранилище для Vue 3. Нет мутаций: состояние меняют в действиях или напрямую, и это уже отслеживается. Сторы — функции defineStore, их можно вызывать внутри setup. Devtools и типы выводятся из состояния без отдельного модуля мутаций, как в Vuex.

Vuex 4 запускается на Vue 3, но новый код пишут на Pinia. Во Vuex изменение состояния шло через мутации, чтобы лента devtools была явной. В Pinia ту же ленту строят без этого слоя.

Vue Router 4 создаётся через createRouter. История — createWebHistory(), а не mode: 'history'. Catch-all записывают как /:pathMatch(.*)*, параметр * больше не работает. В гардах можно возвращать false или маршрут, а не только вызывать next.

Хуки экземпляра:

Vue 2Vue 3
beforeDestroybeforeUnmount
destroyedunmounted
beforeCreate / createdкод в setup до монтирования

onMounted в Composition API соответствует mounted. Парный onUnmounted снимает слушатели, таймеры и подписки.

Разбор

Данные маршрута читают через useRoute(). Для перехода — useRouter(). Следить за параметром нужно явно: компонент может переиспользоваться при смене :id, и onMounted второй раз не вызовется. Тогда берут watch(() => route.params.id, load).

В Pinia стор не деструктурируют напрямую, если нужна реактивность. storeToRefs(store) оставляет реактивными поля состояния и computed, а действия забирают с самого стора.

const store = useCartStore() const { items } = storeToRefs(store) store.add(product)

Порядок гардов при переходе с маршрута A на B: beforeRouteLeave у старого компонента, глобальный beforeEach, beforeRouteUpdate если компонент переиспользуется, beforeEnter в конфиге, beforeRouteEnter у нового (там ещё нет this), затем beforeResolve и после подтверждения afterEach. Проверку входа чаще вешают на beforeEach и to.meta.requiresAuth.

Навигационный гард, который ходит в сеть, лучше делать с понятным отказом: редирект на логин или отмена перехода. Тихий next(false) без причины сложно отлаживать.

$subscribe следит за любым изменением стора, например чтобы записать корзину в localStorage. $onAction видит имя, аргументы, успешный конец и ошибку экшена. Плагин Pinia расширяет все сторы сразу, типичный пример — pinia-plugin-persistedstate.

На что смотрят на senior

Стор не обязан знать про компоненты. Если действие стора принимает экземпляр роутера «чтобы само редиректнуло», границу размыли. Редирект остаётся в гарде или в обработчике экрана, стор возвращает результат.

Кэш страницы через KeepAlive сохраняет экземпляр: onUnmounted не вызовется, пока компонент не уберут из кэша. Для «ушёл с экрана, но компонент жив» есть onDeactivated / onActivated. Это частый источник живых таймеров на скрытой вкладке.

Типичные ошибки

  • Писать мутации Pinia по привычке из Vuex. Отдельного типа мутаций нет.
  • Деструктурировать стор и терять обновления шаблона.
  • Полагаться на onMounted при смене param на том же компоненте маршрута.
  • Забывать переименовать beforeDestroy при переносе компонента на Vue 3.

Задача

Маршрут /orders/:id использует один и тот же компонент. При переходе с /orders/1 на /orders/2 onMounted не вызывается, на экране остаётся первый заказ. Чем догрузить второй?

Показать ответ

Компонент переиспользуется, монтирование один раз. Нужен watch(() => route.params.id, load).

beforeRouteUpdate решает ту же точку в Options API. Гард beforeEnter на такой переход между двумя id одного маршрута не рассчитывайте как на единственное место: экземпляр уже жив.

Last updated on