Vue state management: Pinia stores или composables с глобальными рефами?

На Reddit прошла интересная дискуссия с 25К+ просмотрами по вопросу предпочтений разработчиков при необходимости управлять глобальным состоянием во Vue 3. Ниже её итоги.

Reddit подводит итоги года по частоте посещения /r/vuejs из разных стран мира
Reddit подводит итоги года по частоте посещения /r/vuejs из разных стран мира

Вопрос автором был поставлен так: Зачем использовать Pinia вместо глобальных ref's?

В своих проектах я использую composable функции с глобальным состоянием, как описано в документации Vue.

Каждая функция представляет собой объект бизнес-логики - например, useShoppingCart, useAppConfig и т. д. - и инкапсулирует реактивное состояние и бизнес-логику.

Этот подход часто критикуют и рекомендуют использовать Pinia.

Я знаю о поддержке SSR и возможностях Devtools в Pinia, но это не мой случай - они мне не нужны.

Так почему же, за исключением нужды в SSR и Devtools, я должен использовать Pinia?

Плюсы composable сторов на глобальных рефах очевидны:

  1. Простота

  2. Нативность по отношению к фреймворку.

  3. Отсутствие зависимостей означает отсутствие будущей ситуации "RIP Vuex" с переписыванием 50% кодовой базы проекта.

  4. API Composition выглядит очень зрелым и стабильным и вряд ли сильно изменится в ближайшем будущем (по сравнению с переходом Vue 2 -> Vue 3).

  5. Позволяет использовать всю мощь Reactivity API вместо жесткой Reactive обертки для переменных у Pinia. Разница в производительности может быть огромной.

Минусы?

Было получено на данный момент 36 комментариев, которые можно скомпилировать в следующие выводы:

  1. Большинство согласилось, что если не нужна поддержка SSR и интеграция с Devtools, то работа с Reactivity API напрямую и инкапсуляция реактивного состояния и бизнес логики в composable функции вполне возможна. Для многих это лучше использования Pinia.

  2. Работа с Reactivity API позволяет делать многое, что не позволяет Pinia - например, делать сторы на TypeScript классах.

  3. Был предложен лайфхак - во время разработки импортировать реактивные данные из composable сторов в Pinia, и тогда возможно использование Devtools. При билде для продакшна Pinia уже нет.

  4. Из-за того, что Pinia оборачивает всё в Reactive (даже в режиме setup stores) происходит сильная потеря производительности при работе с Ref или ShallowRef, которые не используют Proxy. Evan You говорил об этом, но плюсы от использования Proxy по сравнению с самописной реализацией реактивности во Vue 2, перекрывают минусы по его словам.

  5. Единственный аргумент в пользу Pinia - унификация работы со стором в команде.

Очень интересный пример использования TypeScript классов в качестве стора через composable был предложен пользователем ferferga. Он дает возможность использовать приватные поля, сеттеры и геттеры (без .value), получить first class type support, что было бы невозможно в случае с Pinia. Данный пример приведен здесь не в качестве рекомендации, но как демонстрация того, что возможно с Composition API


Интересная и полезная информация о Vue.js и фронтенде в целом на нашем сайте: Vue‑FAQ.org.

Также заходите на наш Телеграм‑канал: https://t.me/vuefaq

@gmtd
13.12.2023 11:13 UTC
Первоисточник

Комментарии

@kipy
13.12.2023 15:51 UTC
0

Вы могли бы не стесняться и указать, что являетесь автором поста на реддите.

@gmtd
13.12.2023 15:59 UTC
0

Не отрицаю, но в тексте статьи это выглядело бы самолюбиво и повлияло на беспристрастное восприятие материала

@Ar_f
13.12.2023 17:31 UTC
0

Плюс pinia над композаблом с глобальным рефом еще и в том, что у пинии явная семантика глобального хранилища, а у композабла это скрыто в реализации. Т.е. никогда не знаешь, этот композабл юзает глобальный стейт или нет. Я как-то решил эту проблему разделением композаблов по папкам: в этой папке композаблы с глобальным стейтом, а в этой с локальным.

А т.к. pinia - это широко известное решение проблемы глобального стейта, то и выглядит оно привлекательней. Меньше шансов нагородить ерунды.

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

@gmtd
13.12.2023 17:51 UTC
0

А зачем вам знать, что внутри композабла? Это сервис, у которого есть интерфейс (то, что его функция возвращает), есть грубо говоря документация, описывающая его.

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

13.12.2023 18:07 UTC
0

Мне обычно как-то спокойнее, когда я сразу вижу жизненный цикл объекта. Пиниа стор живет от старта и до закрытия приложения, а "экземпляр" композабла создается каждый раз, как мы его вызываем (условно скажем - в каждом юзающем его компоненте, коих может быть очень много). Может это мои предпочтения, но для меня кажется логичным и удобным такое разделение на синглтон стор и многоинстансный композабл.

13.12.2023 18:23 UTC
0

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

@Ar_f
13.12.2023 17:46 UTC
0

А аргумент в медленности reactive по сравнению с ref выглядит немного искусственным. Поправьте, если я ошибаюсь. Я не отрицаю, что разница огромная, судя по бэнчмарку. Но в бэнчмарке было сделан 1миллион операций записи. И в худшем случае получили 800 миллисекунд. Это медленно, но в рамках терпимого. Но это же 1 миллион записей. В каком фронтенде может потребоваться миллион раз перезаписать поле в прокси? (Напомню, это глобальный стейт, который по своей сути не должен слишком сильно разрастаться). В реальном приложении я бы ожидал увидеть десятки, может сотни записей в стор за раз. Но поправьте, если я в этом ошибаюсь.

@gmtd
13.12.2023 18:04 UTC
0

Тесты проводились на элементарных данных.

Теперь представим что мы грузим массив из 1000 продуктов в админку, каждый продукт - объект со многими уровнями вложений данных. Далее, зависимостей от этого массива тоже может быть с десяток в приложении, и каждая из них имеет свои зависимости. То есть, при изменении этого массива, грубо говоря, объем кода, который должен исполниться, растет экспоненциально, а с ним и падает в таком же порядке и производительность

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

На Ref, ShallowRef и других фичах Reactivity API наверняка можно сделать более оптимизированное приложение так, что вышеуказанная разница примет весьма ощутимые размеры.

Ну и не забываем, что Vue, Svelte и другие соревнуются между собой на проценты, кто кого быстрее. Так что даже они важны.

@Metotron0
13.12.2023 23:10 UTC
0

Я пока что не дочитал документацию до composable — некогда, нужно проект делать :) Когда всё же удаётся почитать документацию, то в новые части проекта добавляются разные штуки, которых раньше не знал. Просто, когда я устраивался на текущую работу, 3-й Vue не так уж сильно давно появился, а когда я стал работать, то стало не до учебников, но новые проекты изредка нужно начинать, а делать их на второй версии не хочется, надо же и третью понемногу осваивать, а то когда же я её освою?

@Twin
20.06.2024 19:02 UTC
0

Слава богу, нейронки заменят программистов с таким подходом :)

21.06.2024 15:13 UTC
0

Типа, крутым программистам менеджеры выделяют кучу времени на чтение учебников?

@Neoldian
15.12.2023 17:42 UTC
0

В продуктовом решении используем пинью и в большом легаси на vue2 - vuex. Но для себя в пет проекте чисто ради эксперимента попробовал юзать композаблы с глобальным стейтом и соглашусь с автором, это бесшовно (refs сразу без приведения) и удобно, можно растащить такие глобальные сторы по функц. модулям, и инициализировать когда чанк первого использующего композабл компонента запустился у клиента(это антипатерн, но как по мне удобно в модульной архитектуре хранить стор внутри модуля). Сразу вспомнилось чем в своё время зацепил, ныне почивший, knockout, там по сути так и реализовывался глобальный стейт ;) Спасибо, тема интересная!

@HAGer2000
11.01.2024 05:48 UTC
0

инициализировать когда чанк первого использующего композабл компонента запустился у клиента(это антипатерн

А почему это антипаттерн? Можете поделиться какими-то источниками, которые это обосновывают?

@kresh
23.01.2024 11:09 UTC
0

А в связке с Nuxt интересно этот подход кто-то применяет? В доках написано "Never define const state = ref() outside of <script setup> or setup() function.
Such state will be shared across all users visiting your website and can lead to memory leaks!" https://nuxt.com/docs/getting-started/state-management

@gmtd
23.01.2024 11:11 UTC
0

Нет, нельзя

C SSR вообще хватает ограничений по сравнению с "стандартным" Vue