С большими возможностями приходит и большая ответственность
! Человек паук
Сегодня мы разберем продвинутые техники идеального укрощения роутинга в реактивном фреймворке на примере VUE. Если вы совершаете переход от обычных страниц к реактивности - можете наткнуться на несколько камней, которые мало где описаны как можно решать правильно.
В чем же особенность роутинга в реактивных приложениях. А в том, что страница не перегружается полностью а происходит эмуляция обновления страницы, а именно изменяющейся части. Мы же реактивные, информацию в шапке и других общих частях (которая обычно располагается в папке layouts) и это здорово, потому что браузеру не нужно заново рендерить эту самую неизменную область схожую у нескольких страниц заново. А URL эмулируется либо через хеш # либо через History API
И всё тут складно и всё красиво ровно до тех пор, пока мы не замечаем проблему при переходе с одной страницы роутинга на ту же самую страницу. И почему-то контент не ведет себя аналогичным образом, компонент page на который смотрит роутинг не выполняет методы жизненного цикла create и mount и отсюда имеем не то поведение которое от него ожидаем
Ранее проблемы не возникало потому что не было возможности сохранить страницу и обновить только изменяемую часть.
Можно решить задачу тупо в лоб. Снести всю реактивность и поставить методы как привыкли, назовем метод refresh(). Нужно обновить - вешаем метод который всё что нужно обновит на каждое событие которое может привести к изменению. Вот досада - нативные методы вперед-назад теперь не откликаются. Тут без реактивности не обойтись. У нас же есть наблюдатели watch, в которые можно запихнуть роутинг и следить за его изменением. Теперь все методы обновления можно делегировать ему.
замечательно. Роутинг сам следит за обновлением, вешать ничего нигде не надо. Вперед-назад также работает
Ну а теперь представьте что вы делаете приложение немножко круче чем стандартный блог. И что, на каждой странице, вам нужно готовить функцию для обновления? Так прекрасно жили ранее когда не было таких проблем. Может ну его, этот роутинг, отказаться и все дела.
Для тех кто не сдался - в запасе есть еще пара фокусов. Мы же всё ещё в javascript мире. Нам всё ещё доступны window.reload()
Если честно, очень грязный трюк. Крайне не рекомендую. Лучше возьмите на вооружение что-нибудь полегче, например такое
const to = this.$router.currentRoute
this.$router.replace("/")
this.$nextTick(() => this.$router.replace(to))
(нарыл в лучших рекомендациях на stackoverflow)
а теперь давайте познакомимся с одной интересной возможностью VUE - атрибут :key. Вы часто встречали его в циклах и списках, но он также может использоваться и для обычных элементов. Зачем он нужен? Он используется чтобы совмещать DOM дерево с виртуальным DOM шаблоном. Или как-то так. В общем элементы с одинаковым :key внутри компонента приводят к ошибке и перестают адекватно реагировать на изменение реактивности. Дополнительно при изменении этого самого :key - будет перерендерен сам элемент со всеми вытекающими. В общем то что нам нужно.
Теперь осталось связать наш любимый роутинг с этим самым :key на отслеживание. Как это сделать? Проще простого.
<router-view :key="$route.path"></router-view>
или даже не
<router-view :key="$route.fullPath"></router-view>
нас же интересует изменение и гет параметров, поисков и всего такого
всё готово. В продакшн. (Это решение тоже было подсмотрено на просторах stackoverflow, странно что в официальной документации такая базовая вещь не расписывается)
Хепи енд?
Смотрим что теперь получается. Теперь мы попали в другую крайность и стали заложниками маршрутизатора. При малейшем его изменении всегда и везде - будет обновление страницы (контента). Если у нас приложение сложнее чем портал с подстраницами 2го уровня и больше и общей шапкой, которую не нужно обновлять регулярно, чтобы экран не дергался, да и лишние запросы и нагрузка - вы же понимаете, мы обсуждали это всё чуть ранее. В общем не красиво это.
Это точно не хепи енд. И Даже nuxt тут не поможет
Теперь чтобы привнести мир в этом всё дело - мы можем просто добавить список исключений в параметр key для главного контента таким образом, чтобы он не менялся при переходе между подстраницами раздела. Сделать это можно легко с помощью computed выражения например с исключением на странице admin
<router-view :key="refreshablePath"></router-view>
computed: {
refreshablePath() {
// роут внутри admin исключение
if (this.$route.path.substr(0, 6) === "/admin") {
return this.$route.path.substr(0, 6)
}
return this.$route.fullPath
},
},
дополнительно на самой странице нужно повесить :key="$route.fullPath" на компоненте, который должен перенегеиться в зависимости от изменения роута внутри основной страницы.
да звучит хардкорно, и выглядит избыточно, чтобы для каждой такой страницы прописывать похожую конструкцию. Наверняка есть пусть элегантно уницифировать это на какие-нибудь нативные методы вроде meta тегов внутри роутов и какой-нибудь облегченной проверки через pathMatch
Об этом возможно вы услышите в следующей главе, или откопаете сами в качестве домашнего задания.