Skip to content

Instantly share code, notes, and snippets.

@zmts
Last active March 17, 2021 14:43
Show Gist options
  • Select an option

  • Save zmts/b91f2311048749b9cdef78553f18d608 to your computer and use it in GitHub Desktop.

Select an option

Save zmts/b91f2311048749b9cdef78553f18d608 to your computer and use it in GitHub Desktop.
Про состояние компонентов Vue.js и где его держать.

FLUX vs noFLUX in Vue.js

Жил был разработчик. Писал он себе кодяру на Vue.js и горя не знал, пока черти не дернули его более внимательно прочитать доку по VUEX. И увидел он там волшебное слово FLUX.

Про состояние компонентов Vue.js и где его держать.

Обычно при разработке приложений придерживаюсь принципа:

  • Все данные которые относятся непосредственно компоненту храним непосредственно в нем
  • Global/Shared данные в стор
  • Как вариант разделать stateless и stateful компоненты

На пример у нас есть простенькая страничка со списком айтемов и нам необходимо его отрисовать. Я создаю компонент pages\news\NewsPage.vue и pages\news\NewsItem.vue.

В первом обращаюсь к АПИ, сохраняю список айтомов, меняю статусы(loading/error). Все в одном компоненте(файле). Второй принимает через проп айтем и отрисовывает его. Все! Далее для дебага юзаем как обычно VueDevTools и не знаем горя.

Но у FLUX'а на эту задачу немного другой ответ. И так рассмотрим тот же пример с двумя компонентами. Для реализации данного ф-ционала нам понадобится взаимодействовать с пятью абстракциями:

  • State module store
  • Action
  • API call
  • Mutation
  • Getter

В action через api call фетчим данные >> записываем данные в стор через mutation. При этом зачастую для каждой сущности(доменной группы сущностей) в сторе создается отдельный модуль и в каждом модуле лежит 4-5 js файлов (actions.js, mutations.js ...).

Для фетча данных выходит такой путь DISPATCH >> ACTION >> API_CALL >> MUTATION >> GETTER. Для отображение какого нибудь loading статуса, необходимо пройти$store >> someModuleState >> loading ну да или воспользоваться вспомогательной ф-цией mapState. Вместо this.loading. А как быть с формочками тоже прикажите хранить эти все чекбоксы/инпуты в сторе ?

При таком раскладе каждый разработчик трактует по своему как разделять данные. Это в стор/это в локальное состояние компонента. У каждого свой FLUX.

А все это делается что бы придерживаться принципа одностороннего изменения данных. Компонент не должен знать как менять данные итд. Что в итоге деет свои плюшки при поддержке и дебаге приложения. А если сюда еще добавить еще и функциональщину и иммутабельность... все ховайся... А по факту в большинстве случаев(я не обобщаю.) все как дебажили консоллогом так и дебажат. Периодически заглядывая в VueDevTools.

Внимание вопрос!

В контексте React приложений своя атмосфера и оно там к месту.

  • К месту ли такие танцы в контексте Vue ?
  • Стоит ли овчинка выделки ?
  • Как понимать что хранить в сторе а что в локальном состоянии компонента ? Статусы (error/loading), филды формочек, чекбоксы... это все куда ?

Если вам есть что сказать буду рад обсудить в комментах.

p.s.

@zmts
Copy link
Author

zmts commented Apr 30, 2018

https://ru.vuejs.org/v2/guide/state-management.html

Обратите внимание, что все действия, изменяющие состояние хранилища, сами помещены в него. Такой подход к глобальному управлению состоянием приложения облегчает понимание возможных изменений и источников их появления. Кроме того, если что-то пойдёт не так — у нас будет лог, по которому можно отследить последовательность действий, приводящую к возникновению бага.

Перечитав доку пришел к такому выводу:

Одним из основных задач FLUX есть централизация управления состояния приложения(single source of truth). Например есть у нас сущность news. В сторе создаем соотвествующий модуль, в нем описываем все взаимодействия сущности с приложением в том числе и запросы к АПИ. В итоге имеем ЕДИНУЮ точку управления сущностью.

Для примера если у нас страничка Новости размазана по 10ти компонентам вместо того чтобы описывать кучу методов/апиколлов в каждом компоненте(ну или почти каждом) при необходимости мы обращаемся к ЕДИНОЙ точке изменения состояния к модулю news в сторе.

Еще раз но иными словами.
Есть у нас страничка Новости (сущность news). Она состоит из 10 компонентов. В части этих компонентов описываются апиколлы/методы/геттеры. В общем к каждом компоненте свой набор методов. FLUX предлагает создать для сущности news свой модуль в сторе и там описывать все апиколлы/методы для данной сущности. Повторюсь таким образом мы не распыляем по компонентам бизнес логику а централизованно держим в одном месте.

@xanf
Copy link

xanf commented May 15, 2018

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

  1. Существует несколько источников изменения данных - пример - таблица с результатами, которая грузится рестом а протом обновляется по вебсокету. В таком случае нам необходим single source of truth - и вьюкс вполне неплохое решение за счет тесной интеграции с экосистемой всего фреймворка
  2. Жизненный цикл данных (актуальности данных) не совпадает с жизненным циклом страницы. Т.е. грубо говоря данные загруженные на одной странице, актуальны и нужны на другой. Такое встречается реже, чем думается, чаще всего при переходе между страницами нам хочется обновить данные. Сюда же относится сценарий, когда нам необходимо сериализовывать данные чтобы при перезагрузке страницы восстановить состояние (допустим из локал стораджа)
  3. Операции изменения данных СЛОЖНЫЕ. Т.е. с данными может происходить много вещей кучей разных способов и хочется аккуратно разделять запросы на изменение и сами изменения. При этом у самой флакс архитектуры есть один не очень приятный побочный эффект - вы видите ЧТО происходит с данными, но не видите КТО иницировал этот запрос

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

@zmts
Copy link
Author

zmts commented May 15, 2018

@xanf Большое спасибо за развернутый ответ!

@sp1ker
Copy link

sp1ker commented May 16, 2018

Vuex слишком нагромождён для мелких проектов и несвязанных страниц. Но сам FLUX это не только Vuex. Можно написать свой с блекджеком и однопоточными данными.
Где Vuex может быть не очень:

  1. Страницы, которые работают сами по себе и никак между собой не связаны. Например, при разработке компонента CRUD для всех таблиц БД я не использовал Vuex. Без него всё получилось чистым и с куда меньшим количеством кода, т.к. каждая страница и каждая сущность не связана ни с чем, кроме самой себя. Для обычного REST без кеширования в самый раз.
  2. Модальные окна. Управлять состоянием модальных окон через Vuex такое себе удовольствие. Окна могут создаваться динамически, они могут открываться друг в друге 5 раз. Где-то всё это нужно хранить. Локальное состояние тут в самый раз.
  3. Выводы всяких ошибок. Опять же, нам не нужно хранить это всё во Vuex. ошибку может генерировать Api-метод, который просто инициализирует динамически компонент и прокидывает ему код ошибки с текстом. Vuex тут наоборот запутает добавлением нового модуля или замусориванием главного хранилища.

Где Vuex отлично подходит:

  1. У вас очень много сущностей, например, User, Post, Comment, Rate и многое другое. Они все очень сильно связаны и изменение одной сущности влияет на всё остальное. В этом случае компоненты перестают задумываться о том, откуда данные пришли и как они обновились. За это отвечает хранилище. А компоненты просто получают эти данные и реактивно всё обновляют.
  2. SPA/PWA. Почти всё, что и в первом случае. Много зависимостей + страницы живут очень долго.

Сам по себе FLUX как архитектурный стиль очень хорош. Можно писать свои аналоги Vuex со своими киллер-фичами, которые требуются именно вам и вашему проекту. Но в вебе без него сейчас сложно что-то делать. По своему существу FLUX похож на обычные события, но только всё проходит централизованно, более точечно и декларативно.

У себя использую гибридный подход. Что-то очень хорошо подходит под локальные состояния, что-то под Vuex. В любом случае, если что-то у меня используется в одном месте, а потом понадобилось вынести во Vuex, это сделать очень просто.

@zmts
Copy link
Author

zmts commented May 16, 2018

@sp1ker Круто! Спасибо!

@akleandrov
Copy link

Пришел к решению что в компонентах оптимально хранить только те данные, которые необходимы для работы с отображением, во всех остальных случаях мапим на модуль в сторе.

@igogrek
Copy link

igogrek commented Sep 10, 2018

Практически полностью согласен с @xanf, поэтому не буду вдаваться в подробности, а просто приведу наш опыт и несколько примеров.

Мы сделали пару проектов используя"чистый" store с попыткой выносить туда все данные, как только они появляются. То есть не использовать локальную data вовсе, дабы потом сэкономить время на перенос из data в store. В конечном итоге, вся эта красивость и чистота кода большой пользы нам не принесла, а размер стора вырос весьма существенно.
Особенно плохо это работало для случаев когда данные как раз зависят от жизненного цикла компонента. С локальной датой хуки прекрасно очищают ее в момент уничтожения компонента, а со стором это вовсе не так удобно.

В итоге для себя мы выбрали смешанный подход:

  1. data используется всегда по умолчанию до тех пор пока необходимо использовать одни и те же данные в 2+ компонентах/страницах.
    Принципиально не используем в таких случаях передачу данных через props ниже по дереву компонентов.
  2. Только data всегда используется в простых переиспользуемых компонентах, вроде чекбоксов, селектов, таблиц и т.д.,
    • за исключением случаев, когда компоненты состоят из большого количества подкомпонентов (когда они подпадают под пункт 1), тогда у корневого компонента появляется свой собственный стор
  3. Всегда используем геттеры для обращения к полям стора
  4. Используем ACTION'ы только если это асинхронные операции или комбинации большого количества мутаций, а так же в случае если необходимо одну операцию выполнять из 2+ компонентов

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment