image

При создании веб-приложения мы в какой-то момент задаемся вопросом — как управлять его состоянием? Vue предоставляет нам способ управлять им в пределах одного компонента, подход очень простой, и при этом замечательно работает. Но что делать, если в приложении множество компонентов, которые должны иметь доступ к одним и тем же данным? Одно из решений этой задачи — Vuex, инструмент для централизованного управления состоянием. В данной статье мы рассмотрим из чего он состоит и как его использовать.

Что такое Vuex?


В официальной документации Vuex описывается следующим образом:
Vuex — это паттерн управления состоянием и библиотека для приложений на Vue.js. Он служит центральным хранилищем данных для всех компонентов приложения и обеспечивает предсказуемость изменения данных при помощи определённых правил

Лучше понять местоположение Vuex в приложении поможет следующая схема:
image
Как можно заметить, хранилище становится своеобразным связующим звеном для всех остальных частей приложения. Подробное описание этого есть в документации, (в том числе и на русском), целью же данной статьи будет быстрое погружение.

Для того, чтобы начать использовать Vuex нам нужно подключить его в наш проект — это можно сделать через npm, или просто подключить библиотеку с cdnjs. Мы не будем акцентировать на этом внимание, а перейдем сразу к созданию базового хранилища:

Vue.use(Vuex)

const store = new Vuex.Store({
    state: {},
    actions: {},
    mutations: {},
    getters: {},  
    modules: {}
})

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

State


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

state: {
    notes: []
}

Actions


В данной части объявляются методы, которые будут вызывать какие-либо изменения в хранилище. Здесь мы можем сделать запрос к серверу, и после получения ответа вызвать изменение состояния. Actions могут быть вызваны из компонентов с помощью метода dispatch. Мы еще увидим его в действии, а пока просто добавим метод для добавления заметки. Поскольку сервера у нас нет, он будет содержать только следующее:

actions: {
    addNote({commit}, note) {
        commit('ADD_NOTE', note)
    }
}

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

Mutations


В мутациях изменяется состояние. Здесь не может быть асинхронных вызовов функций, вычислений и.т.д. — только изменение состояния. В нашем примере с добавлением заметки это будет выглядеть примерно так:

mutations: {
    ADD_NOTE(state, note) {
        state.notes.push(note)
    }
}

Getters


Для того, чтобы использовать данные, положенные в хранилище, их нужно оттуда достать. Причем часто нам нужны не просто данные, а только часть из них — мы хотим применить к ним какие-то фильтры. Геттеры дают нам такую возможность. В базовом варианте мы можем просто вернуть заметки в том виде, в котором они есть:

getters: {
    notes(state) {
        return state.notes
    }
}

Modules


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

const moduleA = { state: {}, mutations: {}, actions: {}, getters: {} }
const moduleB = { state: {}, mutations: {}, actions: {}, getters: {} }

const store = new Vuex.Store({
    modules: {
        a: moduleA,
        b: moduleB
    }
})

store.state.a // -> состояние модуля moduleA
store.state.b // -> состояние модуля moduleB

Более подробно о модулях и пространствах имен можно почитать в документации.

Переходим от слов к делу


Разобравшись с тем, из чего состоит Vuex, соберем наше мини-приложение. Для начала объединим рассмотренные выше пять частей в хранилище и передадим его в качестве аргумента в объект Vue, для того, чтобы его использовать. Хранилище будет доступно через this.$store и в дочерних компонентах. Также понадобится метод addNew для добавления новой заметки. Обратите внимание на использование геттера и метода dispatch для работы с хранилищем.

const store = new Vuex.Store({
    state: {
        notes: []
    },
    actions: {
        addNote({commit}, note) {
            commit('ADD_NOTE', note)
        }
    },
    mutations: {
        ADD_NOTE(state, note) {
            state.notes.push(note)
        }
    },
    getters: {
        notes(state) {
            return state.notes
        }
    }
})

new Vue({
    el: '#app',
    store,
    computed: {
        notes() {
            return this.$store.getters.notes;
        }
    },
    methods: {
        addNew() {
            this.$store.dispatch('addNote', { text: 'новая заметка' })
        }
    }
})

Таким образом мы сделали простейшее приложение, позволяющее добавлять и хранить заметки. Остается добавить разметку для отображения их на странице. Исходный код доступен в песочнице на codepen.

Заключение


Мы рассмотрели устройство Vuex и базовый пример его использования. Как можно видеть, инструмент простой и интуитивно понятный. Но нужно понимать его сферу применения. При создании простого приложения, вроде того, что мы написали выше, использование Vuex кажется излишним, но по мере роста приложения он может стать незаменимым подспорьем в управлении состоянием.

Документация на английском
Документация на русском
Поделиться с друзьями
-->

Комментарии (99)


  1. Yeah
    22.02.2017 00:15

    А можно пояснить, как это работает?


    new Vue({
        el: '#app',
        store
    })

    Как так store без ключа?


    1. nightflash
      22.02.2017 00:19
      +4

      Это es6, равносильно store: store


      1. babylon
        22.02.2017 01:19
        -2

        Если это уже ES6, то где setPrototypeOf? В противном случае рост статического ядра неизбежен.Но некоторых это видимо нисколько не смущает.


        1. movl
          22.02.2017 01:39
          +10

          Зачем и у чего нужно менять прототип в данном примере?


  1. Fen1kz
    22.02.2017 00:22
    +5

    Интересно, в чем смысл называть dispatch commit'ом, а middleware mutation'ами. И почему тогда store не какой-нибудь vault...


    1. ookami_kb
      22.02.2017 01:02

      «Чтобы никто не догадался!»


    1. jMas
      22.02.2017 01:03

      [сарказм]Evan You любит постоянно создавать что то "новое".[/сарказм]
      А если честно, вуе становиться похожим на винегрет из различных практик в немножко самобытной обертке. Кому то нравится, но а меня смущает заворачивание методов в объекты, имеющие какой то магический клей между друг дружкой. Например, проследить вложенность методов в IDE становиться затруднительно, потому что IDE просто не может понять связи между ними. Но это имхо.


      1. MrCheater
        22.02.2017 01:22
        +3

        Redux тоже недружелюбен для IDE. Проследить цепочку Component -> ActionCreator -> Action -> Middleware -> Reducer -> Connect -> Component очень сложно. Все вызовы непрямые, все объекты предварительно биндятся/обёртываются специальными функциями. Обращение ко всему через объект Props ломает оставшиеся подсказки IDE.


        Спасает только то, что структура папок/файлов в проектах на React примерно одинаковая везде, и ясно, куда смотреть, если нужно подебажиться.
        P.S. — пишу на React 2 года. Юзаю Redux в своих проектах


        1. jMas
          22.02.2017 01:54

          Если о реакт-компонентах, то там все достаточно дружелюбно с IDE. А при диспетчеризации да, к сожалению, все достаточно не очевидно.


        1. Fen1kz
          22.02.2017 02:21

          IDE нормально подсказывает если PropTypes прописывать


          1. VolCh
            22.02.2017 11:05

            Редакс ничего не знает про компоненты. Что там ИДЕ подскажет?


        1. xom9lk
          22.02.2017 11:07
          +3

          Потому TypeScript сильно выручает


        1. YYevs
          22.02.2017 11:07

          фишка redux в том, что можно просто сделать grep по названию action creator и action, и уже таким образом проследить цепочку. естественно не настолько удобно как в ide, но с задачей справляется весьма неплохо.


    1. raveclassic
      22.02.2017 01:04
      +2

      Потому-что не redux.


    1. k12th
      22.02.2017 01:42
      +2

      А какой смысл называть mutation reducer'ом?


      1. Fen1kz
        22.02.2017 02:36

        1) Ну первым придумали reducer
        2) Reducer более наглядно чем mutation.
        Reducer от reduce — он принимает очередь из action'ов и выдает объект
        Можно было бы сказать что Mutation мутирует, но он не делает этого, ведь в коде вообще нету генов


        1. faiwer
          22.02.2017 08:32
          +3

          Возможно я не правильно понял это в статье (не работал с Vue), но:


          • Vue "мутирует" свои переменные/объекты/данные. Т.к. изменяет их. "Мутабельность". Обратите внимание, что mutations.ADD_NOTE ничего не возвращает.
          • Redux прогоняет всё через цепочку reducer-ов (.reduce) получая на выходе новый объект.

          Поправьте меня, если я не прав.


          1. raveclassic
            22.02.2017 10:35
            +2

            Все просто, мутации императивны, редьюсеры функциональны. Со всеми вытекающими.


          1. Fen1kz
            22.02.2017 17:22
            +3

            Оу, тогда я резко против этих mutations'ов, это же Глобальный Объект и в большом приложении умрешь выяснять какая из мутаций гадит в него


            1. movl
              23.02.2017 01:00

              Если честно я достаточно поверхностно знаком с redux, но не совсем понимаю в чем иммутабельные объекты, в данном случае, выигрывают. Если говорить про дебаг, то можно логгировать каждое действие, делать снапшот состояния тоже можно, с чем vue-devtools неплохо справляется. В чем поиск мутаций отличается от поиска редьюсеров?>


              1. raveclassic
                23.02.2017 02:14
                +1

                Иммутабельность гарантирует вам идентиченость всего состояния независимо от времени для определенного набора экшенов. Из этого вытекает целый ворох всяких бонусов из серии тестируемости, отладки, time-travelling, оптимизаций рендеринга и прочего.
                Сделать 2 снепшота, конечно, можно, пройдясь по ним вглубь, но это дорого. И, потом, как вы определите, различаются ли они, если кто-то сбоку что-нибудь изменил? Только опять пройдясь по ним вглубь, что, опять же, дорого.
                В случае же с неизменяемыми структурами вы просто получаете другой объект, если он действительно изменился, и делаете сравнение по ссылке, что дешево.

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

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


                1. VolCh
                  23.02.2017 06:44

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

                  "На халяву" узнаем только о факте изменения, а для выявления того, что действительно изменилось, то же глубокое сравнение. Может без необходимости заходить во все ветви, но глубокое. И что значит "искать не нужно"? Чтобы убрать или изменить какую-то "мутацию" нужно бродить по всему дереву композиции, чтобы понять что где меняется.


                  1. raveclassic
                    23.02.2017 11:51
                    +1

                    А зачем вам выявлять, что действительно изменилось? Редьюсеры пришли из ФП, там все строится на композиции, сильно сдобренной мемоизацией. Если вы получаете измененный стейт из двух подобъектов, вы заново прогоняете редьюсеры для этих подобъектов, и, каждый из низ, если пришел не интересующий его экшен, вернет тот же подобъект (тот самый дефолтный кейс в свитче). Таким образом, вы не «ищите» изменения для последующей сборки нового стейта, вы сразу собираете новый.

                    Идем дальше, редьюсеры в виде redux писались для реакта, а там virtual dom и трюк с shouldComponentUpdate. Если в компонент попадает старый объект из стейта он просто не будет перерендериваться (это своего рода мемоизация), и вот как раз тут неизменяемая структура гарантирует, что если посередине стейта что-то изменилось, то все ссылки выше вплоть до корня тоже изменились, тогда как при мутациях это еще нужно будет проверить. Таким образом, вам опять не нужен поиск, вы просто заново строите дерево компонентов.

                    Стоит еще упомянуть reselect с его мемоизирующими селекторами для redux connect, там опять таки идет упор на неизменяемость, ведь, хоть там и одноуровневый кэш, проверка на изменение значения между селекторами при их композиции проходит так же по ссылке на конкретный объект без поиска изменений.

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


                    1. raveclassic
                      23.02.2017 12:13

                      Я прошу прощения, ерунду ж написал:

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


                      1. babylon
                        23.02.2017 13:45

                        баблинг наше всё


                    1. faiwer
                      23.02.2017 12:41
                      +2

                      С другой стороны медали:


                      • мутабельный код писать много-много проще, и этот код разительно понятнее/очевиднее
                      • observer-ы позволяют не делать вообще никаких сравнений, а сразу по месту обновлять DOM и что-угодно ещё (что подписалось на нужные observable-значения)

                      Другой подход ? другие преимущества, другие недостатки.


                    1. VolCh
                      23.02.2017 13:52

                      А зачем вам выявлять, что действительно изменилось?

                      Для отладки, как минимум. Для изменения этих изменений.


                      1. raveclassic
                        23.02.2017 15:03

                        Для изменения этих изменений.
                        Не очень понял, что вы имеете в виду. Можете привести пример?


                        1. VolCh
                          23.02.2017 15:24

                          Задача на изменение логики в незнакомом коде. Да или даже исправление бага.


                          1. raveclassic
                            24.02.2017 13:45

                            Тогда я не понимаю, какое отношение к этому имеют изменения в данных?


                            1. VolCh
                              24.02.2017 18:05

                              Инициируем какое-то действие, которое предположительно где-то что-то меняет, надо узнать где и что


                              1. raveclassic
                                24.02.2017 20:24

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


                                1. VolCh
                                  25.02.2017 10:15

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


                                  1. raveclassic
                                    25.02.2017 13:09

                                    Ладно, не хотите пример, и не надо.
                                    Вам неизменяемость как-то мешает отлаживаться в браузере? Может, перестают брейкпоинты работать?


                                    1. faiwer
                                      25.02.2017 13:12
                                      +1

                                      Может, перестают брейкпоинты работать?

                                      offtop. У меня одного последние 3-4 версии Chrome дичайшим образом глючат с brakepoint-ми? Особенно если это <2 webpack? Стал отлаживать в Chromium-е из-за этого.


                                      1. MrCheater
                                        25.02.2017 14:45
                                        +1

                                        да — breakpoint-ы стали глючить, когда Chrome сделал скачек в поддержке ES6 с 60%+ до 90%+
                                        с тех пор от версии к версии их по-разному колбасит


                1. movl
                  23.02.2017 14:20
                  +2

                  Вот вы говорите про то, что дорого при отладке и тестировании делать снапшоты и сравнивать их, но на сколько дорого генерировать новые объекты на каждое действие в бою? Мы все же говорим про JS, и все это состояние вычисляется далеко не ленивым способом. По поводу изменений с боку, опять же со стороны языка толком ограничений нет, можно заморозить объекты или как-то извратится, но гарантий это все равно не даст, тем не менее при попытке изменить состояние во vuex выскочит предупреждение (или даже исключение), как с этим в redux я не знаю, где-то же конечные данные вылазят внаружу и теоритически их можно менять. Во vuex все обвернуто, чтобы можно было повесить слушателей и контролировать изменения.


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


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


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

                  Если мы говорим про vuex и redux, то почему это другая история? Во vuex все построено на прослушке изменений конкретных участков всего состояния.


                  1. raveclassic
                    23.02.2017 14:58
                    +2

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

                    но на сколько дорого генерировать новые объекты на каждое действие в бою
                    Все не так грустно, immutable.js использует hash map trees и vector trees, чтобы не копировать структуры постоянно — вполне эффективный подход.

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

                    как с этим в redux я не знаю, где-то же конечные данные вылазят внаружу и теоритически их можно менять.
                    Если нужно, вы можете защитить их с помощью либо typescript и readonly/Readonly<>, либо с помощью того же immutable.js. Просто защита данных от мутаций не входит в обязанности redux, и вот тут как раз мне не очень понятно, зачем этим занимается vuex (так как вы сказали, что полетят исключения).

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

                    Если мы говорим про vuex и redux, то почему это другая история?
                    Потому как разговор уйдет в русло сравнения реализации двух разных подходов в двух разных библиотеках.


                    1. faiwer
                      23.02.2017 15:13

                      и это практически бесплатная операция

                      Любая операция будучи выполненной в большом цикле неприятно вас удивит. А если сам цикл выполняет в цикле… Чума. А если это всё ещё заторможено каким-нибудь .freeze или immutable.js… Хотя, я полагаю, там на production-е можно этого избежать.


                      Одна из неприятных вещей в том же React+Redux, заключается в том, что если поместить в state какой-нибудь часто-меняющийся параметр, расположенный глубоко в иерархии, то на любой чих придётся регулярно менять все обёртки (благо хоть не руками). И если часть из таких обёрток не самые мелкие массивы… В общем грустно всё это.


                      Вспомнился один из распространённых в redux хаков — держать в сторе большие массивы последовательностей и массивы значений друг от друга отдельно.


                    1. faiwer
                      23.02.2017 15:17

                      Кстати говоря, я не проверял, но очень интересно. А у вас вроде с redux-react большой опыт. Что будет если я на 7-ом уровне вложенности что-нибудь поменяю? Это заставит перестроить virtualDOM для всех вышестоящих компонентов (ссылки то обновились)? На сколько я понимаю как это работает, то должно. Благо что все соседи будут на уровне pureComponents отсеяны. Но всё равно overhead такой приличный. Я букву в input заменил а тут 100 проверок.


                      1. movl
                        23.02.2017 15:57

                        Я полагаю, что существуют паттерны для решения данного вопроса, например линзы. Redux вроде как не накладывает ограничения на то, что именно должно хранится в стейте. У меня с ФП и redux в частности опыта особо нет, не могу сказать на сколько это повсеместно используется, но использование выглядит логичным для решения проблем, которые были вами озвучены. А так тоже интересно было бы узнать как это решается на практике.


                        1. faiwer
                          23.02.2017 17:20

                          Внимательно изучил статью по вашей ссылке. По сути линзы это удобные функциональные обёртки по установке и получению значения в иммутабельной структуре. Но никаких проблем с производительностью они даже близко не решают. То, что паттеры для быстрой работы с иммутабельными списками быть должны, я и не сомневаюсь. Вот только не уверен, что такое есть и хорошо работает в JS. У нас тут даже типизированные массивы относительно недавно появились. Тут хорошо бы специалиста в тред :)


                    1. faiwer
                      23.02.2017 15:22
                      +2

                      И ещё 1 вопрос. Есть у меня массив на 200 элементов. Нужно обновить элемент под номером 143. [...arr.slice(0, 143), el, ...arr.slice(144)] выглядит жутковато, но ещё хуже, оно же заставляет ЦП и ОЗУ гонять туда сюда одно и тоже во имя бога иммутабельности. Кажется такие вещи пока никем и никак не оптимизируются (в рамках JS, про Haskel и др. функ. языки молчу). Я прав? Или есть какие-то простые и эффективные решения?


                      1. jMas
                        23.02.2017 16:30

                        Не в тему, но булк операции можно делать так...


                        var list1 = Immutable.List.of(1,2,3);
                        var list2 = list1.withMutations(function (list) {
                          list.push(4).push(5).push(6);
                        });
                        assert(list1.size === 3);
                        assert(list2.size === 6);

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


                        1. faiwer
                          23.02.2017 16:36
                          +2

                          Во внутренностях Immutable.js не копался, не знаю. Но графики отсюда сильно расстроили. А учитывая, какой там синтаксис, да и вес в 56 KiB minified…


                          1. jMas
                            23.02.2017 17:07
                            +1

                            Спасибо за ссылку, там отыскалась seamless-immutable, одна из самых быстрых. Все стало еще неоднозначней… с одной стороны да, Immutable.js работает долго, с другой стороны есть альтернативы, которые работают быстро.


                            P.S.: Здесь хорошо подметили: странно почему vuex стор контролирует или пытается контролировать иммутабельность, а не отдельная специальизированная библиотека.


                            1. movl
                              23.02.2017 17:37

                              Считаю что нужно уточнить некоторый момент: vuex пытается контролировать мутабельность, чтобы злой буратино случайно не изменил что-то со стороны. И все завязано на структуре данных, потому что vue переопределяет все свойства объектов с помощью Object.defineProperty, у массивов и прочих структур методы которые влекут мутацию (например push, pop) тоже переопределяются если я не ошибаюсь. На этом завязана вся реактивность vue и возможность контроля вытекает из этого же.


                              https://github.com/vuejs/vuex/blob/dev/src/index.js#L390


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


                          1. jMas
                            23.02.2017 17:16

                            Погорячился насчет альтернатив, не разглядел отчетов на запись.


                      1. raveclassic
                        24.02.2017 13:37

                        Любая операция будучи выполненной в большом цикле неприятно вас удивит.
                        Все же, достаточно абстрактно звучит. Проверки выполняются в фазе рендеринга в virtual dom, а они ограничены requestAnimationFrame (по крайней мере в реакте), ну и проверка === все-таки легче всего остального возможного.

                        Одна из неприятных вещей в том же React+Redux, заключается в том, что если поместить в state какой-нибудь часто-меняющийся параметр, расположенный глубоко в иерархии, то на любой чих придётся регулярно менять все обёртки (благо хоть не руками).
                        Вспомнился один из распространённых в redux хаков — держать в сторе большие массивы последовательностей и массивы значений друг от друга отдельно.
                        Одна из рекомендаций по redux — это держать все в нормализованном в виде, а не в глубоких структурах, как раз для того чтобы избежать изменения родителя, если у него в списке изменился кто-то. Ну т.е. это не хак, это фича.
                        Что будет если я на 7-ом уровне вложенности что-нибудь поменяю? Это заставит перестроить virtualDOM для всех вышестоящих компонентов (ссылки то обновились)? На сколько я понимаю как это работает, то должно. Благо что все соседи будут на уровне pureComponents отсеяны. Но всё равно overhead такой приличный. Я букву в input заменил а тут 100 проверок.
                        Тут надо подумать, а действительно ли вам нужно значение из локального инпута в глобальном стейте? Redux не обязывает вас держать в глобальном стейте абсолютно все. Если же вам все-таки нужно это значение, ну, например, выводить синхронно куда-то в другое место, то вы можете то место обернуть в контейнер с селектором именно этого значения, а в вышестоящих не использовать этот селектор. То же самое касается и родителей этого инпута, если им самим это значение из стейта не нужно, то и доставать его тоже не нужно. Соответственно, перерендериваться ничего не будет.

                        И ещё 1 вопрос. Есть у меня массив на 200 элементов. Нужно обновить элемент под номером 143. [...arr.slice(0, 143), el, ...arr.slice(144)] выглядит жутковато, но ещё хуже, оно же заставляет ЦП и ОЗУ гонять туда сюда одно и тоже во имя бога иммутабельности. Кажется такие вещи пока никем и никак не оптимизируются (в рамках JS, про Haskel и др. функ. языки молчу). Я прав? Или есть какие-то простые и эффективные решения?
                        Именно поэтому рекомендуется данные держать в нормализованном виде для простоты их изменения, а в самих списках — айдишники этих элементов. Тогда при изменении «объекта в списке» вы меняете сам объект в хэшмэпе по его id, а сам список не трогаете.

                        оно же заставляет ЦП и ОЗУ гонять туда сюда одно и тоже во имя бога иммутабельности
                        Справедливости ради, если у вас в массиве объекты, то копируются только ссылки на них, а не вся структура.


                        1. faiwer
                          24.02.2017 14:02
                          +4

                          Все же, достаточно абстрактно звучит

                          Давайте представим, что программист ударился головой об угол и решил, что mouseover координаты хорошо бы держать в store. В случае vue, knockout это будет достаточно лёгкой операцией, которая почти ничего не задействует. 1 observable, 1+ subscriber. Хотя конечно руками напрямую будет ещё быстрее.


                          В случае React+Redux будет через чур много телодвижений. Страдает и производительность и энерго-эффективность (ноутбуки, мобильники, планшеты).


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


                          в контейнер с селектором

                          Звучит как магия какая-то. Селекторы позволяют избежать обновления ссылок на вышестоящие объекты в иерархии? Хм, я думал это просто такая мемоизация, против тяжёлых вычислений. Всё никак не доберусь до них, чтобы изучить их детальнее.


                          Ну т.е. это не хак, это фича.

                          В какой-то степени да. Но в мутабельном случае куда проще: arr.replace(oldV, newV). И никаких забот. И нет, весь список при этом обновляться не должен. Во всяком случае knockout умеет избегать этого.


                          1. raveclassic
                            24.02.2017 14:17
                            +1

                            Давайте представим, что программист ударился головой об угол и решил, что mouseover координаты хорошо бы держать в store. В случае vue, knockout это будет достаточно лёгкой операцией, которая почти ничего не задействует. 1 observable, 1+ subscriber. Хотя конечно руками напрямую будет ещё быстрее.

                            В случае React+Redux будет через чур много телодвижений. Страдает и производительность и энерго-эффективность (ноутбуки, мобильники, планшеты).
                            Ну вы кладете в стейт отдельный объект под это дело, и те контейнеры, которым эти координаты нужны, через селектор их достают. Те, которым не нужны, не достают и не перерендериваются.

                            много-много-много лишних сравнений и аллокаций (virtualdom мы shallow-строим заного на любой сдвиг мыши).
                            В большинстве случаев сравнивать необходимо только в контейнерах, а обычно их не очень много, и в соседних редьюсерах на том же уровне, что и изменяемое значение. Ну, селекторы еще, но они хорошо мемоизируются.

                            Селекторы позволяют избежать обновления ссылок на вышестоящие объекты в иерархии?
                            Нет, селектор позволяет выбрать из стейта то, что действительно нужно для компонента. Таким образом, если ему координаты мыши не нужны, то он и перерендериваться не будет, так как в селекторе они не достаются.

                            Но в мутабельном случае куда проще: arr.replace(oldV, newV). И никаких забот. И нет, весь список при этом обновляться не должен. Во всяком случае knockout умеет избегать этого.
                            А я же и не говорю, что проще, а только, что эта проблема успешно решается. Да, с небольшим оверхедом в виде определенной структуры данных. Кстати, что-то я не припомню метода replace ;)


                          1. vintage
                            24.02.2017 15:12
                            +1

                            Если от этого списка зависит другой список (а как правило это так. частый случай — фильтрация), то он неизбежно будет обновлён. Так что эта оптимизация в КО, фактически бесполезна. Да и обычно не это место является бутылочным горлышком. Основные узкие места — DOM и GC. На GC плохо влияет большое число долгоживущих объектов (создать и тут же уничтожить объект — сейчас почти ничего не стоит). На DOM — объём выводимых данных и количество изменений в "приаттаченных" узлах (пока не добавили в документ, узлы можно очень дёшево изменять). Реакт позволяет по минимуму трогать DOM, но если скажешь ему отрендерить 10000 элементов, он послушно это и сделает. повесив вкладку на пару секунд.


                            1. faiwer
                              24.02.2017 15:40

                              Не понял причём тут фильтрация. Я говорил про замену одного элемента в массиве (даже про .replace упомянул). Не понял пассажа про DOM, лишних изменений в DOM вроде бы ни одна из реактивных библиотек не вносит. Известных мне подходов 2: большая и тяжёлая обвязка вокруг каждого изменяемого значения (или одна на группу), либо множество сравнений для поиска отличий.


                              1. vintage
                                24.02.2017 16:04

                                При том, что изменяем мы один список, а выводим его производный:


                                const tasks_filtered = ko.computed( ()=> tasks_all().filter( filter() ) )

                                И хоть tasks_all мы соптимизировали до push вместо concat. tasks_all неизбежно будет сгенерирован заново.А если ещё и учесть, что списки обычно не в рантайме меняются, а в каком-либо персистентном хранилище (сервер, локальная база, ссылка и тд), то и tasks_all соптимизировать толком не получится.


                                Наоборот, лишние изменения в DOM вносит почти почти любая библиотека. Например, выводите вы список на 100 элементов, а в видимую область помещаются только 20. Зачем обновлять те, что не видны? Это самые настоящие лишние обращения к DOM.


                                1. faiwer
                                  24.02.2017 16:13

                                  Вы обсуждаете явно что-то другое, а не то, что имею ввиду я. Впрочем, как обычно. Забудьте уже про ваш filter, я про него и не заикался. У меня куда чаще стоит задача изменения одного элемента в списке, нежели изменение самого списка. И реактивная библиотека на основе observable может быть устроена таким образом, что подписаться в ней для массива можно на изменение конкретных записей, а не на весь список. А DOM-binding-и просто обязаны такой инструмент использовать. Правда старая реализация в Knockout-е мне не понравилась, т.к. она всё равно делала многочисленные сравнения, вместо точечных замен. И я не в курсе, поменяли ли они это поведение или нет.


                                  А ну и ваш virtual scrolling уже и правда всех достал (как и глупые benchmark-и на его основе). То, что вы внесли его в свою реактивную библиотеку ещё не означает:


                                  • что его нельзя подключить в другие;
                                  • что это вообще обязательная задача для реактивной-библиотеки;
                                  • что о нём нужно упоминать повсеместно;


                                  1. vintage
                                    24.02.2017 19:50

                                    В том-то и дело, что забыть про фильтр не получается (если мы, конечно, не сферический бенчмарк в вакууме гоняем), так как он неизбежно будет, и новый список будет зависеть от внутреннего состояния каждого элемента. Ну, если, конечно костылей не понавставлять.


                                    Судя по бенчмаркам, KO неплохо так ускорили.


                                    И вас не затруднит объяснить, чего это virtual-dom вас не достал, а lazy-rendering достал? Обе технологии делают одну и то же задачу — уменьшают число лишней работы, которую можно не делать. Только если первая делает это лишь за счёт минимизации создания новых узлов путём повторного использования уже существующих, то вторая в дополнение к этому, позволяет минимизировать число этих узлов. Более того, за счёт реактивности минимизируется и число загружаемых и подготавливаемых данных, что тоже даёт плюс к отзывчивости приложения.


                                    что его нельзя подключить в другие;

                                    Прикрутить-то можно что угодно, к чему угодно. Вопрос в цене этого прикручивания.


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


                                    Ну вот, недавно Тинькофф пиарился, что перевёл сайт на реакт:


                                    https://www.tinkoff.ru/invest/news/ — 4 секунды (9 на мобилке) только лишь рендеринга, чтобы пользователь увидел два простых меню, короткую новость с парой картинок и криво запиленный ленивый список новостей.


                                    Для сравнения:


                                    http://mol.js.org/app/habhub/четверть секунды рендеринга на компе (секунда на мобилке) и вы уже можете читать ленту из огромных статей. И для этого не потребовалось ничего "прикручивать", а значит даже джуниор быстро сделает отзывчивое приложение, с чем не справилась команда профессионалов из известного банка.


                                    что это вообще обязательная задача для реактивной-библиотеки;

                                    Это, разумеется, задача библиотеки рендеринга, но она может быть реализована по двум противоположным схемам:


                                    • push, как React, где сначала ты готовишь все данные, а библиотека их отображает. Тут ленивость если и возможна, то только ручками — следим за вьюшкой, запрашиваем данные из модели. И это довольно сложно, когда содержимое страницы — не простой список с фиксированной высотой колонок, а что-то комплексное.


                                    • pull, как в $mol_view, где сначала ты говоришь, что ты хочешь отобразить и откуда брать данные, а вьюшка уже решает какие данные и когда ей нужны. Это позволяет практически не думать о ленивости, но при том её получать.

                                    что о нём нужно упоминать повсеместно;

                                    То есть о бестолковых virtual-dom, streams и flux можно на каждом углу упоминать, а о действительно крутой фиче, гарантирующей хороший уровень отзывчивости независимо от сложности приложения — нет? Почему?


                                    1. faiwer
                                      24.02.2017 20:15
                                      +2

                                      И вас не затруднит объяснить, чего это virtual-dom вас не достал, а lazy-rendering достал?

                                      Дело лично в вас. Ничего против lazy-rendering я не имею. И имею опыт в написании своего.


                                      а о действительно крутой фиче, гарантирующей хороший уровень отзывчивости независимо от сложности приложения — нет? Почему?

                                      Ну если вы перестанете лезть в каждый 1-й JS топик с $mol и lazy-rendering-ом, а вместо этого напишете детальную статью, где сравните сущ-ие библиотеки/плагины для известных реактивных либ, с тем как это реализовано у вас, и каковы результаты, от этого будет куда больше толку.


                                      Насколько я понимаю, никто не мешает написать на React компоненту, в которой вся чёрная магия по lazy-loading-у уже будет сделана, и вам останется только написать что-то вроде <LazyList list={list} component={component}/> и оно полетит.


                                      При условии, конечно, что модель вирт. скролла подойдёт к данной модели. Т.к. тот вирт. скролл, который потребовалось написать мне в прошлом году, едва ли где-то ещё можно встретить в виде готового пакета\плагина и пр… У меня там произвольная иерархическая структура с боооольшим кол-вом элементов (на 2-3 порядка большим, чем можно было бы показать), которая при том может лихо динамически меняться, и нет решительно никакой возможности узнать высоты DOM-элементов до их непосредственного рендера. Да и после их реальные высоты "протухают" после первого же изменения.


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


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


                                      1. raveclassic
                                        24.02.2017 20:40
                                        +1

                                        Насколько я понимаю, никто не мешает написать на React компоненту, в которой вся чёрная магия по lazy-loading-у уже будет сделана
                                        Подробно не смотрел, но, вроде как, вот это изделеие может в динамические размеры элементов, судя по демке.


                                        1. vintage
                                          24.02.2017 21:40

                                          Нет, там не динамическая, а разная высота строк. Достаточно уменьшить ширину страницы и контент начнёт налезать друг на друга.


                                      1. vintage
                                        24.02.2017 21:25

                                        Насколько я понимаю, никто не мешает написать на React компоненту, в которой вся чёрная магия по lazy-loading-у уже будет сделана, и вам останется только написать что-то вроде <LazyList list={list} component={component}/> и оно полетит.

                                        Ваш пример с иерархией и разными высотами тут не взлетит и придётся пилить 2000 строк. И не такой уж это и редкий кейс, на самом деле, если реализуется адаптивный дизайн, где высота плавает в зависимости от размеров экрана и содержимого. $mol_list же будет рендерить лениво даже иерархию. Другое дело, что если список огромный, то скроллинг в самый конец приведёт к его полному рендерингу, но в этом случае лучше пользователю предоставить удобные фильтры/сортировки, чем заставлять его искать иголку в стоге сена, мотая скролл. Впрочем, есть полноценная реализация virtual-scroll в виде $mol_grid, но она, по понятным причинам, требует предопределённой (но возможно разной) высоты строк.


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

                                        Неделю назад я как раз написал статью про полезный инструмент написанный на $mol, однако, народ увидел в ней лишь "опять вы химичите с ленивыми бенчмарками", хотя я даже и не заикался про "необычайную скорость $mol". Так что толку точно больше не будет, к сожалению. Печально всё это.


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


                                        Забавно слушать как разработчики с соседнего проекта плачут из-за многословности флюкса и необходимости вытаскивать очередное состояние в редукс, но боятся $mol как огня с его верблюжьим именованием. Вот уж главная проблема веб-разработки.


                                        1. faiwer
                                          24.02.2017 23:20
                                          +1

                                          Ну, собственно, о чём я и веду речь. У вас есть полу-решение, для полу-задач. Ок. Детально комментировать лень. Если интересно отпишу в личку.


                                          "опять вы химичите с ленивыми бенчмарками"

                                          Ну дык вы же продолжаете заниматься этой пургой. Кто к вам виноват? ССЗБ. Бенчмарки нужно писать беспристрастно и честно, а не сравнивая совершенно разные задачи. Вот какое может быть отношение к человеку, который так "читит"? Вы правда хотите, чтобы после такого epic-fail-а вас воспринимали всерьёз?


                                          Печально всё это.
                                          Но всё бестолку

                                          А вам не кажется, что вы что-то делаете не правильно? Стоя на асфальте, обутым в лыжи, странно удивляться косым взглядам. Начните писать статьи нормальным беспристрастным языком ? толку будет много больше. А до тех пор пока вы пытаетесь других разработчиков лишь передразнивать и вообще продолжать так ёрничать ? наивно ожидать какого-то делового подхода к вашим трудам.


                                          А пока, что в комментариях, что в ваших статьях, я вижу лишь, гхм, Дартаньяна, окружённого… несмышлёнными людьми.


                                          Вот уж главная проблема веб-разработки.

                                          Я не думаю, что вы главная проблема веб-разработки ;) Есть проблемы и поважнее.


                                          1. vintage
                                            25.02.2017 01:07

                                            Это решение для 80% задач. Для остальных 20% есть довольно простое API, позволяющее реализовать любую другую форму ленивости.


                                            Очень жаль, что вы не слушаете, что я говорю. Почему вы не считаете virtual-dom читерским по отношению к реализациям с текстовыми шаблонами? Почему вы не требуете от этого читерского ленивого реакта "честной" реализации с полным удалением всех узлов, созданием их снова и восстановлением позиции скролла и фокуса, как у "честного" SAPUI5?


                                            А, впрочем, вы правы, я трачу свою жизнь впустую, пытаясь в одиночку что-то изменить в тенденциозной индустрии.


                            1. MrCheater
                              24.02.2017 17:05
                              +1

                              Реакт позволяет по минимуму трогать DOM, но если скажешь ему отрендерить 10000 элементов, он послушно это и сделает. повесив вкладку на пару секунд.

                              Скоро React переедет на новый движок Fiber, и такого больше не будет происходить. https://github.com/acdlite/react-fiber-architecture



                              1. vintage
                                24.02.2017 20:22

                                О, вангую, через год в js, наконец, появится нативное апи для файберов.


                            1. Riim
                              24.02.2017 17:56

                              На GC плохо влияет большое число долгоживущих объектов (создать и тут же уничтожить объект — сейчас почти ничего не стоит)

                              Откуда ты узнаёшь всё это?)
                              Что значит долгоживущих? Достаточно чтобы он просто жил и это уже будет влиять на GC или влияет его удаление после того как он долго пожил?


                              1. vintage
                                24.02.2017 20:08
                                +3

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


                                https://hacks.mozilla.org/2014/09/generational-garbage-collection-in-firefox/


                    1. movl
                      23.02.2017 15:46
                      +1

                      Соглашусь что тут разные подходы в целом, оверхед сравнивать можно долго и ответа что лучше в этом плане скорей всего получить не получится, да и спорить наверное бессмысленно. Просто с начала прозвучала мысль, что изменяемые структуры в контексте одного глобального хранилища состояния однозначно плохо. С чем не могу согласится. Ну и подход во vuex мне понравился больше, чем в redux, но это уже очень субъективная оценка. Спасибо за подробный ответ.


        1. k12th
          22.02.2017 11:08
          +1

          Если честно, я плаваю в redux-терминологии. Для меня редьюсер не выглядит так, будто он принимает очередь, а выглядит так, будто принимает стейт и возвращает объект, замещающий какую-то часть в стейте.


          1. raveclassic
            22.02.2017 11:47
            +2

            Имелась в виду аналогия со списком экшенов. Если представить их в виде массива (есть популярный подход представления такие вещей в виде потоков — rxjs), то на нем можно выполнить операцию reduce, которая пробежится по всем экшенам, аккумулируя стейт, и в итоге вернет конечный. От этого и название — reducer, так как так называются передаваемые в reduce коллбэки.

            UPDATE: более того, у redux-редьюсера даже сигнатура та же: (state, action) => state.


            1. k12th
              22.02.2017 11:58

              Не буду делать вид, что понял, зачем экшены живут в списке, и в чем тут сходство сигнатуры, когда редьюсеры в JS имеют вид (accumulator, currentValue, index, array) => value, но за объяснение спасибо.


              1. raveclassic
                22.02.2017 12:11
                +1

                Скорей не списке, а в потоке/стриме (привет, rx). А по поводу сигнатуры, если откинуть индекс и ссылку на изначальный массив, то получится то же самое. Ведь стейт — и есть аккумулятор, а элементы в стриме — экшены, а редьюсер аккумулирует стейт, пробегаясь по экшенам.

                UPDATE: точнее redux аккмулирует стейт, применяя редьюсер к стриму экшенов.
                UPDATE2: само собой, это все терминология, и никаких стримов в redux нет


                1. k12th
                  22.02.2017 12:27

                  Окей, я понял, спасибо. По прежнему не согласен (стримов нет; а отбрасывать-то можно что угодно, но ведь (state) => state они редьюсером не называют), хотя бы корни терминологии ясны.


                  А стримы, наверное, возникают, когда делаешь time-travel: берешь initial state и проходишь по массиву случившихся экшенов, чтобы получить состояние в желаемой точке.


                  1. raveclassic
                    22.02.2017 12:37

                    А стримы, наверное, возникают, когда делаешь time-travel: берешь initial state и проходишь по массиву случившихся экшенов, чтобы получить состояние в желаемой точке.
                    Именно! А можно пойти дальше и представить в виде стрима вообще весь жизненный цикл приложения, тогда reduce на любом срезе этого стрима вернет конкретный саккумулированный стейт.


                    1. VasilioRuzanni
                      22.02.2017 12:43

                      Совершенно верно. Только для этого вовсе не обязательно представлять экшены именно в виде стрима — просто последовательность экшенов, неважно каким образом запущенных. Стрим удобен для контекста (если хотите реактивный стиль), редьюсеру ж по барабану, как он там вызывается.


          1. Fen1kz
            22.02.2017 17:31

            Я плохо выразился. Имел ввиду "некая последовательность action'ов". Не важно, массивом, стримом или по-одному (из UI, например).


            Ну и сигнатура совпадает:
            (accumulator (aka state), currentValue (aka action)) => updatedAccumulator (aka updated state)


    1. movl
      22.02.2017 02:22
      +2

      Есть мутации и есть действия. Мутации — синхронные и чистые, редьюсеры, так сказать, или транзакции. Действия — могут быть асинхронными и могут выполнять несколько мутаций, допустим начало загрузки документа, обработка результата и окончание загрузки можно уместить в одно действие. Мутации вызываются через commit, действия вызываются через dispatch, что звучит логично, как по мне. В общем это разные команды.


      Middleware заменено на plugins во второй версии, но это не мутации, а скорее плагины — это сущности для перехвата мутаций.


      1. VasilioRuzanni
        22.02.2017 12:37
        +1

        Мутации — синхронные и чистые, редьюсеры, так сказать, или транзакции.

        А что значит «чистые»? В примере в статье меняется переданный в качестве аргумента объект — или под чистотой вы не имеете в виду «pure function»?

        И плюс, между мутациями и редьюсерами фундаментальная разница. Редьюсеры как раз-таки «pure», это чисто функции, они ничего не меняют, а возвращают новый стейт.


        1. movl
          22.02.2017 12:47

          Я ошибся, это не чистые функции, а скорее наоборот функции меняющие состояние. Отсюда и использование глагола commit.


          1. raveclassic
            22.02.2017 12:56

            Да, только вот commit заканчивает декларативную транзакцию, а тут стор меняется на ходу и сразу.


            1. raveclassic
              22.02.2017 13:13
              +1

              Перечитал повнимательней, и я не прав. Коммит вызывается из экшена, а изменение происходит в самой мутации.


    1. movl
      22.02.2017 02:29
      +1

      Ну и кстати вот обсуждение этого, если интересно, конечно.


    1. ipavlenko
      22.02.2017 07:51
      +2

      И то и другое слово изначально — глаголы.
      Требовалось два глагола для асинхронных и синхронных методов.

      dispatch action (асинхронные)
      commit mutation (синхронные)

      Evan очень ратует за декларативность и детерминизм в именовании, за что ему огромное спасибо.
      Я бы относился к Vue и всей линейке технологий рядом — как к ревью и рефакторингу более старых технологий.

      Evan переосмысливает и пере-создаёт технологии, которые без такого ревью и переосмысления — захломятся и обветшают (да-да, без переосмысления интерес к React-у уйдёт, как когда-нибудь и Vue). Но хорошо, что есть такие ребята.

      В общем, я рад, что Evan даёт новые имена уже готовым концепциям.
      Мне эти имена даже больше нравятся.


      1. ipavlenko
        22.02.2017 07:52

        Промахнулся.
        Это ответ на вопрос: «А какой смысл называть mutation reducer'ом?»


  1. vintage
    22.02.2017 01:34
    +2

    Научите меня писать статьи, которым не ставят минусов. Пожалуйста, очень надо.


    1. Sirion
      22.02.2017 10:04

      Вы ведь понимаете, что после этого коммента минусы появятся?)


      1. vintage
        22.02.2017 10:32
        -1

        Вы мне льстите, я не на столько умный и коварный.


  1. ConceptDesigner
    22.02.2017 08:04
    -1

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


    1. k12th
      22.02.2017 11:15

      Тут в основном декомпозиция идет на уровне собственно бизнес-логики, которая разбивается на небольшие, хорошо изолированные функции.


      Плюс есть т.н. «модули», когда вся бизнес-логика, относящаяся к некоторой части состояния, выносится и группируется отдельно.


  1. ChernikovP
    22.02.2017 08:05

    Навскидку похоже на Redux + reselect. Интересно, тут тоже используется мемоизация в getter-ах и неизменяемость store-а?


    1. faiwer
      22.02.2017 08:36

      и неизменяемость store-а

      mutations: {
              ADD_NOTE(state, note) {
                  state.notes.push(note)
              }
          }

      похоже, что нет


      1. ipavlenko
        22.02.2017 13:22

        Точно нет


    1. k12th
      22.02.2017 11:21
      +1

      Не знаю начет Redux + reselect, но похоже на MobX. Иммутабельности нет, vue наоборот не любит, когда вы заменяете объект целиком.


      С мемоизацией сложный вопрос. С одной стороны в явном виде ее нету. С другой стороны, при использовании геттеров ожидаемым образом — то есть в качестве computed-свойств в компонентах — vue сама следит, какие computed-свойства пересчитать, то есть какие геттеры вызвать.


  1. VolCh
    22.02.2017 11:08

    А подход MobX можно использовать без кучи тупого кода? В смысле стор делать полноценным объектом и мутации его вызывая его методами?


    1. k12th
      22.02.2017 11:29
      +1

      Для этого vuex не нужен, скармливаете любой объект vue.js, она сама будет следить за свойствами. Вместо геттеров — computed (и watch), вместо экшнов — methods.


      vuex, в некоторой степени, дань моде на ФП подход в UI; в отличие от React тут действительно есть реактивность и UI перерисовывается без создания нового объекта.


      1. VolCh
        22.02.2017 11:56

        Я имею в виду не «вместо», а обертки, создаваемые автоматически, прозрачно для компонента, он дергает геттер, не подозревая, что это computed.

        Угу, уже понял, но лично мне не по душе ФП с разделением состояния, доступа к нему и его мутации в глобальных вещах, ООП подход ближе, а ФП только там где нет сложной логики состояния, те де рндереры реакта.


        1. k12th
          22.02.2017 12:03

          Так нет никакого «вместо», под капотом это так и работает.


          Как я уже и сказал, можно спокойно управлять стейтом без vuex и ФП.


  1. climenty
    24.02.2017 13:42

    Проясните момент, вы предлагаете использовать Vue.js и Vuex? Хорошо, я того же мнения.
    А зачем вы приводите пример для устаревшей версии Vue.js? 2.0 вполне зарелизилась.
    Ибо: "$dispatch and $broadcast have been removed in favor of more explicitly cross-component communication"

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


    1. sfi0zy
      24.02.2017 13:48

      А зачем вы приводите пример для устаревшей версии Vue.js? Ибо: "$dispatch and $broadcast have been removed in favor of more explicitly cross-component communication"

      Вы немного ошиблись — это другой dispatch (убрали вот этот).


      1. climenty
        24.02.2017 13:55

        А, это их собственный метод, спасибо, понятно.