Pull to refresh

Comments 19

Спасибо за статью!
А мы вот отказались от централизованного хранилища состояний насовсем - в пользу сервисов с Composition API (или use-функций, кому как удобнее). Пожалуй, единственный минус - отсутствие удобной навигации в devtools. Также нет возможности вернуться по состоянию в прошлое, но это и так мало кто всерьез использует.
Зато из плюсов - никакой магии, полная поддержка типов (это ведь просто классы / функции), нет ограничений на композицию классов, сервисы можно без проблем тестировать, внедрить DI и так далее.

Спасибо Вам за комментарий!

Отказ от централизованного хранилища состояний - тоже решение, и даже более чистое, но как мне кажется, достаточно радикальное и также может повлечь за собой последствия в виде props drilling и усложнения логики в компоненте

В данной статье главной целью было предложить решение для уже устоявшейся концепции

Да, чем вам обычная композбл функция не угодила?
Pinia - глобализация локального стейта. А вы берете глобальный стейт и обратно локализуете.

Если мы решаем для этих же целей использовать композабл, то вновь возникает проблема хранения состояния для всех компонентов модуля целиком. Нам необходимо, чтобы доступ к состоянию был на всех уровнях иерархии компонентов.

По сути у нас три варианта:

  1. Инициализируем composable на самом верху и прокидываем состояние пропсами вниз. По сути это отказ от централизованного хранилища. Это решение, и даже более чистое, но как мне кажется, достаточно радикальное и также может повлечь за собой последствия в виде props drilling(особенно больно выглядит при большом количестве уровней в иерархии компонентов) и усложнения логики в компоненте. Думаю не каждый на готов пойти на этот шаг и отказаться от Flux архитектуры

  2. Каждый раз заново инициализировать состояние при использовании composable. Тоже кажется не совсем решает нашу проблему и добавляет кучу усложнении логике компонентов

  3. Вынести состояние за composable. Условно создать реактивную переменную-состояние и обращаться к нему внутри composable. Здесь мы сталкиваемся с аналогичными проблемами Pinia, которые я пытался исправить в моем посте

И как мы видим, у каждого из этих вариантов есть свои недостатки

Пункт 1 и provide/inject?

Кроме того, вы уверены, что надо прокидывать всё состояние, а не конкретные реактивные переменные?

Сколько у вас уровней, что встает проблема props drilling-a?

Да, но у provide/inject есть свои недостатки, хорошо это удалось описать Илье Климову(с 10:30 минуты, https://www.youtube.com/watch?v=p3vfmNIjmW4). В доке Vue одно время даже был варнинг по его использованию, с чем я согласен и на что мы уже натыкались сами

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

Также у нас в модуле оплаты из статьи около 10 уровней вложенности

Честно говоря мы пробовали идти по этому пути и именно из-за недостатков выше были вынуждены от него отказаться

Pinia scoped стор

Я пару минут ломал себе мозг, пытаясь понять, что это за "ситоп" такой загадочный. Не стоит так языки смешивать, да и слово "хранилище" можно было использовать..

А вы уверены что модуль module/1 удалится при удалении компонента?
А его точно ни кто кроме Вас не получит?
Вместо mapState можно использовать UseNamespacedGetters

Поделюсь своими мыслями, более лаконичная реализация.

Использовать функциональный компонент scoped-store, который создает scoped стору, и провайдит прямо инстанс сторы.

useStore этот инстанс пытается инжектить и возвращает глобальную стору в случае если ни чего нету.
Либо, бросает исключение - зависит от контекста.
Есть жесткий варн на provide/inject.

Прикол в том, как организованы модули.

Глобальные модули инициализируются статическим объектом state.
Тогда мы уверены что состояние модуля будет единым для всех инстансов сторы.

scoped модули - state инициализируется из функции, это гарантирует создание пустого индивидуального стейта на каждый инстанс.

Хороший пример - у нас есть два кейса.
Компонент используется как корневой в роутере, тогда стейт прилетает в глобальную стору.
И на этой же странице нам нужно показать модалку, в которой нам нужно отобразить этот компонент в списке
Каждый заворачиваем в скопед стору и привет.
Конечно, настолько элементарный пример реализуется без таких приседаний.
Но уж если дело дошло до скопед стор, то такое решение проще и юзабельнее подключеня новых модулей в глобальную стору.

Встряхнули и полетели. Потомки ни чего не знают о верхних модулях.
Не забываем что подход provide/inject не только плох, но и, в чем-то, необходим.

Да, модуль стора удалится, для этого добавлен хук onUnmounted, в котором и происходит очистка

Ваш вариант через компоненты тоже интересный, спасибо за коммент! Единственный момент - думаю не всем может подойти создание функциональных компонентов scoped сторов. Есть риски усложнить код + наружу выносится то, что у меня располагается под капотом(и кажется неплохо под этим капотом себя чувствует)). Также сама идея делать из компонента стор будет выглядит немного странновато, если будет сосуществовать одновременно с классическим стором на pinia или vuex(которые используются в большинстве проектов на Vue)

Также стоит отметить, что я не предлагаю заменить все pinia сторы на pinia scoped сторы. Скорее предлагаю инструмент, который можно использовать там, где классический подход pinia не справляется)

Модуль стора удалится, но все еще доступен глобально.

При возникновении исключения в onUnmounted он останется в сторе

Как раз, в этом случае, scoped store будет виден только внутри компонента и не доступен больше ниоткуда

И гарантированно удалится без хуков

Пока компонент не удален - будет
И Вы удаляете не модуль, а стейт?

А как же сайд эффекты, обсерверы?

Да, пока компонент жив в pinia будет существовать scoped стор и будет лежать рядом с обычными сторами. В имени scoped стора будет содержаться id скоупа, его сложно будет перепутать с другими модулями

По поводу удаления обсерверов - умирает главный родительский компонент и вместе с ним умирают все его дети и следовательно все обсерверы и рефы в них(так как scoped стор используется только в них). Остается только сам стейт в pinia, его я удаляю уже в onUnmounted

А сам стор?
Реактивщина внутри стора.
Есть регистер/унрегистер, почему не он?

И, если я не путаю, правильно использовать событие onBeforeUnmount
Может получиться что не вся реактивщина помрет

И в унмоунтед что-то может посчитать что оно погибло и/или что-то уже не доступно

По onBeforeUnmount подумаем, спасибо!

Также сама идея делать из компонента стор будет выглядит немного странновато

Странновато, так же, лежит и глобальная версия сторы в корневом компоненте Vue

Sign up to leave a comment.

Articles