From 97fd3047ff580b63469f7e54bbec89a03fe66d7d Mon Sep 17 00:00:00 2001 From: Alex-Sokolov Date: Tue, 28 Nov 2017 22:39:35 +0300 Subject: [PATCH 01/26] =?UTF-8?q?structure.md=20=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D0=BF=D0=B5=D1=87?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/structure.md b/docs/ru/structure.md index 05cc82a60..1574b5bb2 100644 --- a/docs/ru/structure.md +++ b/docs/ru/structure.md @@ -6,7 +6,7 @@ 2. Единственным механизмом изменения этого состояния являются **мутации**, являющиеся синхронными транзакциями; -3. Асинхронные операции инкапсулирутся в **действия**, или их комбинации. +3. Асинхронные операции инкапсулируются в **действия**, или их комбинации. Покуда вы следуете этим правилам, можно использовать любую структуру проекта. Если ваш файл хранилища становится слишком большим, просто начните выносить действия, мутации и геттеры в отдельные файлы. From be971170711363138dbc090727ee8641916a9d99 Mon Sep 17 00:00:00 2001 From: Alex-Sokolov Date: Tue, 28 Nov 2017 22:50:47 +0300 Subject: [PATCH 02/26] =?UTF-8?q?actions.md=20=D0=BC=D0=B5=D0=BB=D0=BA?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/actions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/actions.md b/docs/ru/actions.md index c45a3c6bd..22efc0555 100644 --- a/docs/ru/actions.md +++ b/docs/ru/actions.md @@ -81,7 +81,7 @@ actions: { const savedCartItems = [...state.cart.added] // инициируем запрос и "оптимистично" очистим корзину commit(types.CHECKOUT_REQUEST) - // предположим, что API магазина позволяет передать колбэки + // предположим, что API магазина позволяет передать коллбэки // для обработки успеха и неудачи при формировании заказа shop.buyProducts( products, From 1a7b02f4e8715a9b885585e1b4942e7cd14a8e6c Mon Sep 17 00:00:00 2001 From: Alex-Sokolov Date: Tue, 28 Nov 2017 22:51:01 +0300 Subject: [PATCH 03/26] =?UTF-8?q?api.md=20=D0=BC=D0=B5=D0=BB=D0=BA=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/api.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/ru/api.md b/docs/ru/api.md index 64e43c1b4..b7817a650 100644 --- a/docs/ru/api.md +++ b/docs/ru/api.md @@ -16,7 +16,7 @@ const store = new Vuex.Store({ ...options }) Корневой объект состояния хранилища Vuex. [Подробнее](state.md) - Если вы передаёте функцию, возвращающую объект, то возвращаемый объект будет использован в качестве корневого состояния. Это может быть полезным, если вы хотите повторно использовать объект состояния, особенно при повтоном использовании модулей. [Подробнее](modules.md#повторное-использование-модулей) + Если вы передаёте функцию, возвращающую объект, то возвращаемый объект будет использован в качестве корневого состояния. Это может быть полезным, если вы хотите повторно использовать объект состояния, особенно при повторном использовании модулей. [Подробнее](modules.md#повторное-использование-модулей) - **mutations** @@ -89,7 +89,7 @@ const store = new Vuex.Store({ ...options }) } ``` - Каждый модуль может содержать `state` и `mutations`, как и корневое хранилище. Состояние модуля будет прикреплёно к корневому, по указанному ключу. Мутации и геттеры модуля получают при вызове первым аргументом только локальное состояние, а не корневое. При вызове действий `context.state` аналогичным образом указывает на локальное состояние модуля. + Каждый модуль может содержать `state` и `mutations`, как и корневое хранилище. Состояние модуля будет прикреплено к корневому, по указанному ключу. Мутации и геттеры модуля получают при вызове первым аргументом только локальное состояние, а не корневое. При вызове действий `context.state` аналогичным образом указывает на локальное состояние модуля. [Подробнее](modules.md) @@ -142,7 +142,7 @@ const store = new Vuex.Store({ ...options }) Устанавливает наблюдение за возвращаемым значением геттера, вызывая коллбэк в случае его изменения. Геттер получает состояние хранилища первым аргументом, и геттеры вторым аргументом. Возможно указание дополнительного объекта опций, с такими же параметрами как и у метода `vm.$watch` корневой библиотеки Vue. - Для прекращения наблюдения, необходимо вызвать возвращённую функцию-хэндлер. + Для прекращения наблюдения, необходимо вызвать возвращаемую функцию обработчик. - **`subscribe(handler: Function)`** @@ -161,7 +161,7 @@ const store = new Vuex.Store({ ...options }) > Добавлено в версии 2.5.0 - Подписывается на действие хранилища. Обработчик `handler` вызывается после каждого дейсвтия и получает в качестве параметров дескриптов действия и текущее состояние хранилища: + Подписывается на действие хранилища. Обработчик `handler` вызывается после каждого действия и получает в качестве параметров дескриптов действия и текущее состояние хранилища: ``` js store.subscribeAction((action, state) => { From 921fb57bb5e79a383ded021c5b9d036fe7e503e9 Mon Sep 17 00:00:00 2001 From: Alex-Sokolov Date: Tue, 28 Nov 2017 22:51:18 +0300 Subject: [PATCH 04/26] =?UTF-8?q?intro.md=20=D0=BC=D0=B5=D0=BB=D0=BA=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/intro.md b/docs/ru/intro.md index ad9871519..0ee425115 100644 --- a/docs/ru/intro.md +++ b/docs/ru/intro.md @@ -44,7 +44,7 @@ new Vue({ - Несколько представлений могут зависеть от одной и той же части состояния приложения - Действия из разных представлений могут оказывать влияние на одни и те же части состояния приложения -Разбираясь с первой проблемой, вам придётся передавать одни и те же данные в несколько глубоко вложенных компонентов. Это часто сложно и неприятно, а для соседних компонентов такое и вовсе не сработает. Решая вторую проблему, приходится обращаться напрямую к родителям и потомкам компонента, или синхронизовать изменения с другими местами в приложении событиями. Оба подхода хрупки и быстро приводят к появлению кода, который невозможно поддерживать. +Разбираясь с первой проблемой, вам придётся передавать одни и те же данные в несколько глубоко вложенных компонентов. Это часто сложно и неприятно, а для соседних компонентов такое и вовсе не сработает. Решая вторую проблему, приходится обращаться напрямую к родителям и потомкам компонента, или синхронизировать изменения с другими местами в приложении событиями. Оба подхода хрупки и быстро приводят к появлению кода, который невозможно поддерживать. Так почему бы не вынести всё общее состояние приложения из компонентов в глобальный синглтон? При использовании этого подхода, дерево компонентов превращается в одно большое "представление", а каждый компонент получает доступ к состоянию приложения, наряду с возможностью вызывать действия для изменения состояния, независимо от расположения этого компонента в дереве. From 9569122f32d4cc4f769556b96088e4a04da3958f Mon Sep 17 00:00:00 2001 From: Alex-Sokolov Date: Tue, 28 Nov 2017 22:51:28 +0300 Subject: [PATCH 05/26] =?UTF-8?q?state.md=20=D0=BC=D0=B5=D0=BB=D0=BA=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/state.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/state.md b/docs/ru/state.md index d2c00d196..7c2621947 100644 --- a/docs/ru/state.md +++ b/docs/ru/state.md @@ -67,7 +67,7 @@ import { mapState } from 'vuex' export default { // ... computed: mapState({ - // arrow-функции позволяют писать код очень лаконично + // стрелочные функции позволяют писать код очень лаконично count: state => state.count, // передача строки 'count' эквивалентна записи `state => state.count` From eb89908d7968f2f6d940d0ba35e9414498faa883 Mon Sep 17 00:00:00 2001 From: Alex-Sokolov Date: Tue, 28 Nov 2017 22:51:39 +0300 Subject: [PATCH 06/26] =?UTF-8?q?strict.md=20=D0=BC=D0=B5=D0=BB=D0=BA?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/strict.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/strict.md b/docs/ru/strict.md index 0950868da..fc2bcb9af 100644 --- a/docs/ru/strict.md +++ b/docs/ru/strict.md @@ -9,7 +9,7 @@ const store = new Vuex.Store({ }) ``` -В строгом режиме любая попытка внесения изменений в состояние Vuex кроме мутаций будет выбрасывать ошибку. Это гарантирует, что все мутации состояния будут явно отслежены через инструменты отладки. +В строгом режиме любая попытка внесения изменений в состояние Vuex кроме мутаций будет выбрасывать ошибку. Это гарантирует, что все мутации состояния будут явно отслеживаться через инструменты отладки. ### Разработка vs. production From 5db4d3fa0d0628d3a335954c86dcf910689ffe20 Mon Sep 17 00:00:00 2001 From: Alex-Sokolov Date: Tue, 28 Nov 2017 22:51:47 +0300 Subject: [PATCH 07/26] =?UTF-8?q?testing.md=20=D0=BC=D0=B5=D0=BB=D0=BA?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/testing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ru/testing.md b/docs/ru/testing.md index 52e5df3f2..98e8fe009 100644 --- a/docs/ru/testing.md +++ b/docs/ru/testing.md @@ -85,7 +85,7 @@ const actions = actionsInjector({ } }) -// вспомогательная фукнция для тестирования действия, которое должно вызывать известные мутации +// вспомогательная функция для тестирования действия, которое должно вызывать известные мутации const testAction = (action, payload, state, expectedMutations, done) => { let count = 0 @@ -181,7 +181,7 @@ describe('getters', () => { #### Запуск в Node -Используйте следующий конфиг webpack (в сочетании с соответствующим [`.babelrc`](https://babeljs.io/docs/usage/babelrc/)): +Используйте следующую конфигурацию webpack (в сочетании с соответствующим [`.babelrc`](https://babeljs.io/docs/usage/babelrc/)): ``` js // webpack.config.js From 4220dc6fe04a83f7654bd3b12bccc8040918c60e Mon Sep 17 00:00:00 2001 From: Alex-Sokolov Date: Mon, 1 Jan 2018 23:10:52 +0300 Subject: [PATCH 08/26] =?UTF-8?q?actions.md=20=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/actions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/actions.md b/docs/ru/actions.md index 22efc0555..680ca54e2 100644 --- a/docs/ru/actions.md +++ b/docs/ru/actions.md @@ -45,7 +45,7 @@ actions: { store.dispatch('increment') ``` -На первый взгляд может выглядеть глупо: если мы хотим инкрементировать переменную count, почему бы просто не вызвать `store.commit('increment')` напрямую? Запомните, что **мутации должны быть синхронными**. Действия же этим ограничением не скованы. Внутри действия можно выполнять **асинхронные** операции: +На первый взгляд может выглядеть глупо: если мы хотим увеличить значение count, почему бы просто не вызвать `store.commit('increment')` напрямую? Помните что **мутации должны быть синхронными**? Для действий такого ограничения нет. Внутри действий можно выполнять **асинхронные** операции: ``` js actions: { From 7ec590aa7942d5ad65433f0c87a454b12af08e1a Mon Sep 17 00:00:00 2001 From: Alex-Sokolov Date: Mon, 1 Jan 2018 23:14:05 +0300 Subject: [PATCH 09/26] =?UTF-8?q?testing.md=20=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/testing.md b/docs/ru/testing.md index 98e8fe009..cc7c52c50 100644 --- a/docs/ru/testing.md +++ b/docs/ru/testing.md @@ -4,7 +4,7 @@ ### Тестирование мутаций -Мутации тестировать довольно просто, так как они представляют из себя всего лишь простые функции, поведение которых полностью зависит от переданных параметров. Может пригодится возможность ES2015-модулей для самостоятельного именованного экспорта мутаций, наряду с экспортом самого хранилища из файла `store.js`: +Мутации тестировать довольно просто, так как они представляют из себя всего лишь простые функции, поведение которых полностью зависит от переданных параметров. Один трюк заключается в том, что если вы используете модули ES2015 и помещаете свои мутации в файле `store.js`, то помимо экспорта по умолчанию, вы должны экспортировать мутации с помощью именованного экспорта: ``` js const state = { ... } From 43490101b3541dc7d7daf8c9316685b52b7c3a19 Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Wed, 3 Jan 2018 08:41:35 +0300 Subject: [PATCH 10/26] Fix typos. ru --- docs/ru/actions.md | 4 ++-- docs/ru/api.md | 4 ++-- docs/ru/getters.md | 4 ++-- docs/ru/getting-started.md | 4 ++-- docs/ru/hot-reload.md | 2 +- docs/ru/intro.md | 4 ++-- docs/ru/modules.md | 2 +- docs/ru/mutations.md | 2 +- docs/ru/plugins.md | 8 ++++---- docs/ru/strict.md | 6 +++--- docs/ru/structure.md | 4 ++-- docs/ru/testing.md | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/ru/actions.md b/docs/ru/actions.md index 680ca54e2..4cec3a54d 100644 --- a/docs/ru/actions.md +++ b/docs/ru/actions.md @@ -98,7 +98,7 @@ actions: { ### Диспетчеризация действий в компонентах -Диспетчеризовать действия в компонентах можно при помощи `this.$store.dispatch('xxx')`, или используя вспомогательную функцию `mapActions`, создающую локальные псевдонимы для действий в виде методов компонента (требуется наличие корневого `$store`): +Диспетчеризировать действия в компонентах можно при помощи `this.$store.dispatch('xxx')` или используя вспомогательную функцию `mapActions`, создающую локальные псевдонимы для действий в виде методов компонента (требуется наличие корневого `$store`): ``` js import { mapActions } from 'vuex' @@ -159,7 +159,7 @@ actions: { } ``` -Наконец, если мы используем [async / await](https://tc39.github.io/ecmascript-asyncawait/), мы можем компоновать наши действия следующим образом: +Наконец, если мы используем [async / await](https://tc39.github.io/ecmascript-asyncawait/), то можем компоновать наши действия следующим образом: ``` js // предположим, что `getData()` и `getOtherData()` возвращают Promise diff --git a/docs/ru/api.md b/docs/ru/api.md index b7817a650..2cd6a7ae3 100644 --- a/docs/ru/api.md +++ b/docs/ru/api.md @@ -97,7 +97,7 @@ const store = new Vuex.Store({ ...options }) - тип: `Array` - Массив функций-плагинов, которые будут применены к хранилищу. Плагины попросту получают хранилище в качестве единственного аргумента, и могут как отслеживать мутации (для сохранения исходящих данных, логирования, или отладки) или инициировать их (для обработки входящих данных, например вебсокетов или observables). + Массив функций-плагинов, которые будут применены к хранилищу. Плагины попросту получают хранилище в качестве единственного аргумента, и могут как отслеживать мутации (для сохранения исходящих данных, логирования или отладки) или инициировать их (для обработки входящих данных, например, веб-сокетов или наблюдателей). [Подробнее](plugins.md) @@ -136,7 +136,7 @@ const store = new Vuex.Store({ ...options }) - **`replaceState(state: Object)`** - Позволяет заменить корневое состояние хранилища. Используйте только для гидрации состояния / функционала "машины времени". + Позволяет заменить корневое состояние хранилища. Используйте только для гидрации состояния / функциональности "машины времени". - **`watch(getter: Function, cb: Function, options?: Object)`** diff --git a/docs/ru/getters.md b/docs/ru/getters.md index 9918065f6..f34f637e8 100644 --- a/docs/ru/getters.md +++ b/docs/ru/getters.md @@ -10,7 +10,7 @@ computed: { } ``` -Если этот функционал требуется более чем одному компоненту, понадобится либо дублировать функцию, либо выносить её в совместно используемый хелпер и импортировать в нескольких местах. Оба эти подхода далеки от идеала. +Если эта функциональность требуется более чем одному компоненту, понадобится либо дублировать функцию, либо выносить её в совместно используемый хелпер и импортировать в нескольких местах. Оба эти подхода далеки от идеала. Vuex позволяет определять в хранилище «геттеры». Вы можете считать их вычисляемыми свойствами для хранилища. Как и вычисляемые свойства, результаты геттера кэшируются, основываясь на своих зависимостях и будут пересчитаны только тогда, когда изменится одна из его зависимостей. @@ -53,7 +53,7 @@ getters: { store.getters.doneTodosCount // -> 1 ``` -В компонентах геттеры можно использовать например таким образом: +В компонентах геттеры можно использовать, например, таким образом: ``` js computed: { diff --git a/docs/ru/getting-started.md b/docs/ru/getting-started.md index 615d081cd..66ccefbd9 100644 --- a/docs/ru/getting-started.md +++ b/docs/ru/getting-started.md @@ -4,7 +4,7 @@ 1. Хранилища Vuex реактивны. Если компоненты Vue зависят от их состояния, изменение состояния хранилища спровоцирует соответствующие изменения компонентов. -2. Непосредственное изменение состояния хранилища запрещено. Единственный способ внести изменения — явно **вызвать мутацию**. Этот подход позволяет быть уверенным, что каждое изменение оставляет в системе след, и даёт возможность использовать инструменты, позволяющие лучше понять работу приложения. +2. Непосредственное изменение состояния хранилища запрещено. Единственный способ внести изменения — явно **вызвать мутацию**. Этот подход позволяет быть уверенным, что каждое изменение оставляет в системе след и даёт возможность использовать инструменты, позволяющие лучше понять работу приложения. ### Простейшее Хранилище @@ -27,7 +27,7 @@ const store = new Vuex.Store({ }) ``` -Теперь мы можем получить доступ к объекту состояния `store.state`, или вызвать изменение состояния методом `store.commit`: +Теперь мы можем получить доступ к объекту состояния `store.state` или вызвать изменение состояния методом `store.commit`: ``` js store.commit('increment') diff --git a/docs/ru/hot-reload.md b/docs/ru/hot-reload.md index 942347131..d7591f11a 100644 --- a/docs/ru/hot-reload.md +++ b/docs/ru/hot-reload.md @@ -1,6 +1,6 @@ # Горячая замена -Vuex поддерживает горячую замену мутаций, модулей, действий и геттеров в момент разработки с помощью [webpack Hot Module Replacement API](https://webpack.js.org/guides/hot-module-replacement/). Аналогичный функционал в Browserify достижим при использовании плагина [browserify-hmr](https://github.com/AgentME/browserify-hmr/). +Vuex поддерживает горячую замену мутаций, модулей, действий и геттеров в момент разработки с помощью [webpack Hot Module Replacement API](https://webpack.js.org/guides/hot-module-replacement/). Аналогичная функциональность в Browserify достижима при использовании плагина [browserify-hmr](https://github.com/AgentME/browserify-hmr/). Для мутаций и модулей необходимо использовать метод API `store.hotUpdate()`: diff --git a/docs/ru/intro.md b/docs/ru/intro.md index 0ee425115..a266f3c44 100644 --- a/docs/ru/intro.md +++ b/docs/ru/intro.md @@ -1,6 +1,6 @@ # Что такое Vuex? -Vuex - это **паттерн управления состоянием и библиотека** для приложений на Vue.js. Он служит центральным хранилищем данных для всех компонентов приложения и обеспечивает предсказуемость изменения данных при помощи определённых правил. Кроме того, Vuex интегрируется с официальным [расширением инструментов разработчика](https://github.com/vuejs/vue-devtools) Vue, предоставляя "из коробки" такие продвинутые возможности как "машину времени" при отладке и экспорт/импорт слепков состояния данных. +Vuex - это **паттерн управления состоянием и библиотека** для приложений на Vue.js. Он служит центральным хранилищем данных для всех компонентов приложения и обеспечивает предсказуемость изменения данных при помощи определённых правил. Кроме того, Vuex интегрируется с официальным [расширением инструментов разработчика](https://github.com/vuejs/vue-devtools) Vue, предоставляя "из коробки" такие продвинутые возможности, как "машину времени" при отладке и экспорт/импорт слепков состояния данных. ### Что такое "паттерн управления состоянием"? @@ -48,7 +48,7 @@ new Vue({ Так почему бы не вынести всё общее состояние приложения из компонентов в глобальный синглтон? При использовании этого подхода, дерево компонентов превращается в одно большое "представление", а каждый компонент получает доступ к состоянию приложения, наряду с возможностью вызывать действия для изменения состояния, независимо от расположения этого компонента в дереве. -Кроме того, чётко определяя и разделяя концепции, возникающие при управлении состоянием и требуя соблюдения некоторых правил, мы улучшаем структурированность и поддерживаемость нашего кода. +Кроме того, чётко определяя и разделяя концепции, возникающие при управлении состоянием, и требуя соблюдения некоторых правил, мы улучшаем структурированность и поддерживаемость нашего кода. Такова основная идея, лежащая в основе Vuex, вдохновлённого [Flux](https://facebook.github.io/flux/docs/overview.html), [Redux](http://redux.js.org/) и [Архитектурой Elm](https://guide.elm-lang.org/architecture/). В отличие от других паттернов, Vuex реализован в виде библиотеки, специально заточенной на использование совместно с Vue.js и использующей его производительную систему реактивных обновлений. diff --git a/docs/ru/modules.md b/docs/ru/modules.md index a57b9615d..a462df996 100644 --- a/docs/ru/modules.md +++ b/docs/ru/modules.md @@ -2,7 +2,7 @@ Из-за использования единого дерева состояния, все глобальные данные приложения оказываются помещены в один большой объект. По мере роста приложения, хранилище может существенно раздуться. -Чтобы помочь в этой беде, Vuex позволяет разделять хранилище на **модули**. Каждый модуль может содержать собственное состояние, мутации, действия, геттеры, и даже встроенные подмодули — структура фрактальна: +Чтобы помочь в этой беде, Vuex позволяет разделять хранилище на **модули**. Каждый модуль может содержать собственное состояние, мутации, действия, геттеры и даже встроенные подмодули — структура фрактальна: ``` js const moduleA = { diff --git a/docs/ru/mutations.md b/docs/ru/mutations.md index 7945a7e0e..7898b648b 100644 --- a/docs/ru/mutations.md +++ b/docs/ru/mutations.md @@ -1,6 +1,6 @@ # Мутации -Единственным способом изменения состояния хранилища во Vuex являются мутации. Мутации во Vuex очень похожи на события: каждая мутация имеет строковый **тип** и **функцию-обработчик**. В этом обработчике и происходят собственно изменения состояния, переданного в функцию первым аргументом: +Единственным способом изменения состояния хранилища во Vuex являются мутации. Мутации во Vuex очень похожи на события: каждая мутация имеет строковый **тип** и **функцию-обработчик**. В этом обработчике и происходят, собственно, изменения состояния, переданного в функцию первым аргументом: ``` js const store = new Vuex.Store({ diff --git a/docs/ru/plugins.md b/docs/ru/plugins.md index c15586f77..9dc9e5a01 100644 --- a/docs/ru/plugins.md +++ b/docs/ru/plugins.md @@ -25,7 +25,7 @@ const store = new Vuex.Store({ Плагинам не разрешается напрямую изменять состояние приложения — как и компоненты, они могут только вызывать изменения опосредованно, используя мутации. -Вызывая мутации, плагин может синхронизировать источник данных с хранилищем данных в приложении. Например, для синхронизации хранилища с вебсокетом (пример намеренно упрощён, в реальной ситуации у `createPlugin` были бы дополнительные опции): +Вызывая мутации, плагин может синхронизировать источник данных с хранилищем данных в приложении. Например, для синхронизации хранилища с веб-сокетом (пример намеренно упрощён, в реальной ситуации у `createPlugin` были бы дополнительные опции): ``` js export default function createWebSocketPlugin (socket) { @@ -103,8 +103,8 @@ const store = new Vuex.Store({ const logger = createLogger({ collapsed: false, // автоматически раскрывать залогированные мутации filter (mutation, stateBefore, stateAfter) { - // возвращает `true` если мутация должна быть залогирована - // `mutation` это объект `{ type, payload }` + // возвращает `true`, если мутация должна быть залогирована + // `mutation` - это объект `{ type, payload }` return mutation.type !== "aBlacklistedMutation" }, transformer (state) { @@ -121,6 +121,6 @@ const logger = createLogger({ }) ``` -Логирующий плагин можно включить также и используя отдельный тег ` + +``` + +### NPM + +```bash +npm install vuex --save +``` + +### Yarn + +```bash +yarn add vuex +``` + +Если вы используете систему сборки, установите Vuex явным образом командой `Vue.use()`: + +```js +import Vue from "vue"; +import Vuex from "vuex"; + +Vue.use(Vuex); +``` + +При использовании глобальных тегов ` +``` + +Тогда `window.Promise` будет доступен автоматически. + +Если вы предпочитаете использовать менеджер пакетов, такой как NPM или Yarn, установите пакет с помощью следующей команды: + +```bash +npm install es6-promise --save # NPM +yarn add es6-promise # Yarn +``` + +Кроме того, добавьте строку ниже в любое место вашего кода перед использованием Vuex: + +```js +import "es6-promise/auto"; +``` + +### Версия для разработки + +Если вы хотите использовать самую новую dev-сборку `vuex`, то придётся вручную склонировать репозиторий с GitHub и запустить сборку: + +```bash +git clone https://github.com/vuejs/vuex.git node_modules/vuex +cd node_modules/vuex +npm install +npm run build +``` diff --git a/docs/ru/modules.md b/docs/ru/modules.md new file mode 100644 index 000000000..ea71eeb76 --- /dev/null +++ b/docs/ru/modules.md @@ -0,0 +1,317 @@ +# Модули + +Из-за использования единого дерева состояния, все глобальные данные приложения оказываются помещены в один большой объект. По мере роста приложения, хранилище может существенно раздуться. + +Чтобы помочь в этой беде, Vuex позволяет разделять хранилище на **модули**. Каждый модуль может содержать собственное состояние, мутации, действия, геттеры и даже встроенные подмодули — структура фрактальна: + +```js +const moduleA = { + state: { ... }, + mutations: { ... }, + actions: { ... }, + getters: { ... } +} + +const moduleB = { + state: { ... }, + mutations: { ... }, + actions: { ... } +} + +const store = new Vuex.Store({ + modules: { + a: moduleA, + b: moduleB + } +}) + +store.state.a // -> состояние модуля `moduleA` +store.state.b // -> состояние модуля `moduleB` +``` + +### Локальное состояние модулей + +Первым аргументом, который получают мутации и геттеры, будет **локальное состояние модуля**. + +```js +const moduleA = { + state: { count: 0 }, + mutations: { + increment(state) { + // `state` указывает на локальное состояние модуля + state.count++; + } + }, + + getters: { + doubleCount(state) { + return state.count * 2; + } + } +}; +``` + +Аналогично, `context.state` в действиях также указывает на локальное состояние модуля, а корневое — доступно в `context.rootState`: + +```js +const moduleA = { + // ... + actions: { + incrementIfOddOnRootSum({ state, commit, rootState }) { + if ((state.count + rootState.count) % 2 === 1) { + commit("increment"); + } + } + } +}; +``` + +Кроме того, в геттеры корневое состояние передаётся 3-м параметром: + +```js +const moduleA = { + // ... + getters: { + sumWithRootCount(state, getters, rootState) { + return state.count + rootState.count; + } + } +}; +``` + +### Пространства имён + +По умолчанию действия, мутации и геттеры внутри модулей регистрируются в **глобальном пространстве имён** — это позволяет нескольким модулям реагировать на тот же тип мутаций/действий. + +Если вы хотите сделать модули более самодостаточными и готовыми для переиспользования, вы можете создать его с собственным пространством имён, указав опцию `namespaced: true`. Когда модуль будет зарегистрирован, все его геттеры, действия и мутации будут автоматически связаны с этим пространством имён, основываясь на пути по которому зарегистрирован модуль. Например: + +```js +const store = new Vuex.Store({ + modules: { + account: { + namespaced: true, + + // содержимое модуля + state: { ... }, // состояние модуля автоматически вложено и не зависит от опции пространства имён + getters: { + isAdmin () { ... } // -> getters['account/isAdmin'] + }, + actions: { + login () { ... } // -> dispatch('account/login') + }, + mutations: { + login () { ... } // -> commit('account/login') + }, + + // вложенные модули + modules: { + // наследует пространство имён из родительского модуля + myPage: { + state: { ... }, + getters: { + profile () { ... } // -> getters['account/profile'] + } + }, + + // большая вложенность с собственным пространством имён + posts: { + namespaced: true, + + state: { ... }, + getters: { + popular () { ... } // -> getters['account/posts/popular'] + } + } + } + } + } +}) +``` + +Геттеры и действия с собственным пространством имён будут получать свои локальные `getters`, `dispatch` и `commit`. Другими словами, вы можете использовать содержимое модуля без написания префиксов в том же модуле. Переключения между пространствами имён не влияет на код внутри модуля. + +#### Доступ к глобальному содержимому в модулях со своим пространством имён + +Если вы хотите использовать глобальное состояние и геттеры, `rootState` и `rootGetters` передаются 3-м и 4-м аргументами в функции геттеров, а также как свойства в объекте `context`, передаваемом в функции действий. + +Для запуска действий или совершения мутаций в глобальном пространстве имён нужно добавить `{ root: true }` 3-м аргументом в `dispatch` и `commit`. + +```js +modules: { + foo: { + namespaced: true, + + getters: { + // `getters` ограничены геттерами данного модуля + // вы можете использовать rootGetters из 4-го аргумента геттеров + someGetter (state, getters, rootState, rootGetters) { + getters.someOtherGetter // -> 'foo/someOtherGetter' + rootGetters.someOtherGetter // -> 'someOtherGetter' + }, + someOtherGetter: state => { ... } + }, + + actions: { + // dispatch и commit также ограничены данным модулем + // они принимают опцию `root` для вызова в глобальном пространстве имён + someAction ({ dispatch, commit, getters, rootGetters }) { + getters.someGetter // -> 'foo/someGetter' + rootGetters.someGetter // -> 'someGetter' + + dispatch('someOtherAction') // -> 'foo/someOtherAction' + dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction' + + commit('someMutation') // -> 'foo/someMutation' + commit('someMutation', null, { root: true }) // -> 'someMutation' + }, + someOtherAction (ctx, payload) { ... } + } + } +} +``` + +#### Регистрация глобального действия в модуле с собственным пространством имён + +Если вы хотите зарегистрировать глобальное действие в модуле с собственным пространством имён, вы можете пометить его с помощью `root: true` и поместить определение действия в функцию `handler`. Например: + +```js +{ + actions: { + someOtherAction ({dispatch}) { + dispatch('someAction') + } + }, + modules: { + foo: { + namespaced: true, + + actions: { + someAction: { + root: true, + handler (namespacedContext, payload) { ... } // -> 'someAction' + } + } + } + } +} +``` + +#### Подключение с помощью вспомогательных функций к пространству имён + +Подключение модуля со своим пространством имён к компонентам с помощью вспомогательных функций `mapState`, `mapGetters`, `mapActions` и `mapMutations` это может выглядеть подобным образом: + +```js +computed: { + ...mapState({ + a: state => state.some.nested.module.a, + b: state => state.some.nested.module.b + }) +}, +methods: { + ...mapActions([ + 'some/nested/module/foo', + 'some/nested/module/bar' + ]) +} +``` + +В таких случаях вы можете передать строку с пространством имён в качестве первого аргумента к вспомогательным функциям, тогда все привязки будут выполнены в контексте этого модуля. Пример выше можно упростить до: + +```js +computed: { + ...mapState('some/nested/module', { + a: state => state.a, + b: state => state.b + }) +}, +methods: { + ...mapActions('some/nested/module', [ + 'foo', + 'bar' + ]) +} +``` + +Кроме того, вы можете создать вспомогательные функции с помощью `createNamespacedHelpers`. Она возвращает объект, в котором все вспомогательные функции для связывания с компонентами будут указывать на переданное пространство имён: + +```js +import { createNamespacedHelpers } from "vuex"; + +const { mapState, mapActions } = createNamespacedHelpers("some/nested/module"); + +export default { + computed: { + // будет указывать на `some/nested/module` + ...mapState({ + a: state => state.a, + b: state => state.b + }) + }, + methods: { + // будет указывать на `some/nested/module` + ...mapActions(["foo", "bar"]) + } +}; +``` + +#### Уточнение для разработчиков плагинов + +Вас может обеспокоить непредсказуемость пространства имён для ваших модулей, когда вы создаёте [плагин](plugins.md) с собственными модулями и возможностью пользователям добавлять их в хранилище Vuex. Ваши модули будут также помещены в пространство имён, если пользователи плагина добавляют ваши модули в модуль со своим пространством имён. Чтобы приспособиться к этой ситуации, вам может потребоваться получить значение пространства имён через настройки плагина: + +```js +// получение значения пространства имён через options +// и возвращение функции плагина Vuex +export function createPlugin(options = {}) { + return function(store) { + // добавление пространства имён к модулям плагина + const namespace = options.namespace || ""; + store.dispatch(namespace + "pluginAction"); + }; +} +``` + +### Динамическая регистрация модулей + +Вы можете зарегистрировать модуль уже и **после** того, как хранилище было создано, используя метод `store.registerModule`: + +```js +// регистрация модуля `myModule` +store.registerModule("myModule", { + // ... +}); + +// регистрация вложенного модуля `nested/myModule` +store.registerModule(["nested", "myModule"], { + // ... +}); +``` + +Состояние модуля будет доступно как `store.state.myModule` и `store.state.nested.myModule`. + +Динамическая регистрация модулей позволяет другим плагинам Vue также использовать Vuex для управления своим состоянием, добавляя модуль к хранилищу данных приложения. Например, библиотека [`vuex-router-sync`](https://github.com/vuejs/vuex-router-sync) интегрирует vue-router во vuex, отражая изменение текущего пути приложения в динамически присоединённом модуле. + +Удалить динамически зарегистрированный модуль можно с помощью `store.unregisterModule(moduleName)`. Обратите внимание, что статические (определённые на момент создания хранилища) модули при помощи этого метода удалить не получится. + +Вероятно, вы хотите сохранить предыдущее состояние при регистрации нового модуля, например сохранить состояние из приложения с рендерингом на стороне сервера. Вы можете этого добиться с помощью опции `preserveState`: `store.registerModule('a', module, { preserveState: true })` + +### Повторное использование модулей + +Иногда нам может потребоваться создать несколько экземпляров модуля, например: + +* Создание нескольких хранилищ, которые используются одним модулем (например, чтобы [избегать синглтонов с сохранением состояния в SSR](https://ssr.vuejs.org/ru/structure.html#избегайте-синглтонов-с-состоянием) при использовании опции `runInNewContext` в значении `false` или `'once'`); +* Регистрация модуля несколько раз в одном хранилище. + +Если мы используем просто объект для определения состояния модуля, тогда этот объект состояния будет использоваться по ссылке и вызывать загрязнение состояния хранилища / модуля при его мутациях. + +Это фактически та же самая проблема с `data` внутри компонентов Vue. Таким образом решение будет таким же — использовать функцию для объявления состояния модуля (поддержка добавлена в версии 2.3.0+): + +```js +const MyReusableModule = { + state() { + return { + foo: "bar" + }; + } + // мутации, действия, геттеры... +}; +``` diff --git a/docs/ru/mutations.md b/docs/ru/mutations.md new file mode 100644 index 000000000..09307feba --- /dev/null +++ b/docs/ru/mutations.md @@ -0,0 +1,173 @@ +# Мутации + +Единственным способом изменения состояния хранилища во Vuex являются мутации. Мутации во Vuex очень похожи на события: каждая мутация имеет строковый **тип** и **функцию-обработчик**. В этом обработчике и происходят, собственно, изменения состояния, переданного в функцию первым аргументом: + +```js +const store = new Vuex.Store({ + state: { + count: 1 + }, + mutations: { + increment(state) { + // изменяем состояние + state.count++; + } + } +}); +``` + +Вызывать функцию-обработчик напрямую — нельзя. Это больше похоже на обработку события: "Когда мутация типа `increment` инициирована, вызывается этот обработчик". Чтобы инициировать обработку мутации, необходимо вызвать `store.commit`, указав её тип: + +```js +store.commit("increment"); +``` + +### Мутации с нагрузкой + +При вызове `store.commit` в мутацию можно также передать дополнительный параметр, называемый **нагрузкой (`payload`)**: + +```js +// ... +mutations: { + increment (state, n) { + state.count += n + } +} +``` + +```js +store.commit("increment", 10); +``` + +В большинстве случаев нагрузка будет объектом, содержащим несколько полей. Запись мутаций в таком случае становится более описательной: + +```js +// ... +mutations: { + increment (state, payload) { + state.count += payload.amount + } +} +``` + +```js +store.commit("increment", { + amount: 10 +}); +``` + +### Объектный синтаксис + +Другой способ вызвать мутацию — это передать в commit единственный параметр, в котором `type` указан напрямую: + +```js +store.commit({ + type: "increment", + amount: 10 +}); +``` + +При использовании объектной записи, объект передаётся в качестве нагрузки целиком, так что обработчик остаётся тем же самым: + +```js +mutations: { + increment (state, payload) { + state.count += payload.amount + } +} +``` + +### Мутации следуют правилам реактивности Vue + +Поскольку состояние хранилища Vuex — это реактивная переменная Vue, при возникновении мутации зависящие от этого состояния компоненты Vue обновляются автоматически. Кроме того, это значит, что мутации Vuex имеют те же самые подводные камни, что и реактивность в обычном Vue: + +1. Лучше инициализировать изначальное состояние хранилища, указав все поля в самом начале. + +2. При добавлении новых свойств объекту необходимо либо: + +* Использовать `Vue.set(obj, 'newProp', 123)`, или + +* Целиком заменить старый объект новым. Например, используя [синтаксис расширения объектов](https://github.com/sebmarkbage/ecmascript-rest-spread) из stage-3, можно написать так: + + ```js + state.obj = { ...state.obj, newProp: 123 }; + ``` + +### Использование констант для обозначения типов мутаций + +В различных вариантах реализации Flux этот подход используется весьма часто. Вынесите все константы с типами мутаций и действий в отдельный файл, чтобы было проще использовать линтеры и другие инструменты, а также чтобы дать читателям возможность с первого взгляда понять, какие мутации возможны в приложении: + +```js +// mutation-types.js +export const SOME_MUTATION = "SOME_MUTATION"; +``` + +```js +// store.js +import Vuex from 'vuex' +import { SOME_MUTATION } from './mutation-types' + +const store = new Vuex.Store({ + state: { ... }, + mutations: { + // "вычисляемые имена" из ES2015 позволяют использовать + // константу в качестве имени функции + [SOME_MUTATION] (state) { + // здесь будет изменяться состояние + } + } +}) +``` + +Тем не менее, использовать константы для указания типов мутаций совершенно необязательно, хотя это и может оказаться полезным в крупных проектах. + +### Мутации должны быть синхронными + +Нужно помнить одно важное правило: **обработчики мутаций обязаны быть синхронными**. Почему? Рассмотрим пример: + +```js +mutations: { + someMutation (state) { + api.callAsyncMethod(() => { + state.count++ + }) + } +} +``` + +Теперь представьте, что вы отлаживаете приложение и смотрите в лог мутаций в инструментах разработчика. Для каждой залогированной мутации devtools должен сохранить слепки состояния приложения "до" и "после" её наступления. Однако, асинхронный коллбэк внутри приведённой выше мутации делает это невозможным: мутация-то уже записана, и у devtools нет никакой возможности знать, что далее будет вызван коллбэк, а, значит, и инициируемые им изменения становится, по сути дела, невозможно отследить. + +### Вызов мутаций в компонентах + +Мутации можно вызывать из кода компонентов, используя `this.$store.commit('xxx')`, или применяя хелпер `mapMutations`, который проксирует вызовы `store.commit` через методы компонентов (для этого требуется наличие корневой ссылки на хранилище `$store`): + +```js +import { mapMutations } from "vuex"; + +export default { + // ... + methods: { + ...mapMutations([ + "increment", // `this.increment()` будет вызывать `this.$store.commit('increment')` + + // mapMutations также поддерживает нагрузку: + "incrementBy" // `this.incrementBy(amount)` будет вызывать `this.$store.commit('incrementBy', amount)` + ]), + ...mapMutations({ + add: "increment" // `this.add()` будет вызывать `this.$store.commit('increment')` + }) + } +}; +``` + +### О действиях + +Привнесение асинхронности в мутации могло бы изрядно затруднить понимание логики программы. Например, если вызываются два метода, оба с асинхронными коллбэками, изменяющими состояние приложения — как предсказать, какой из коллбэков будет вызван первым? Именно поэтому концепции изменений и асинхронности рассматриваются по отдельности. Во Vuex **мутации — это синхронные транзакции**: + +```js +store.commit("increment"); +// все изменения состояния, вызываемые мутацией "increment", +// к этому моменту уже должны произойти. +``` + +Для обработки асинхронных операций существуют [Действия](actions.md). diff --git a/docs/ru/plugins.md b/docs/ru/plugins.md new file mode 100644 index 000000000..103ece31e --- /dev/null +++ b/docs/ru/plugins.md @@ -0,0 +1,124 @@ +# Плагины + +Хранилища Vuex принимают опцию `plugins`, предоставляющую хуки для каждой мутации. Vuex-плагин — это просто функция, получающая хранилище в качестве единственного параметра: + +```js +const myPlugin = store => { + // вызывается после инициализации хранилища + store.subscribe((mutation, state) => { + // вызывается после каждой мутации + // мутация передаётся в формате `{ type, payload }`. + }); +}; +``` + +Используются плагины так: + +```js +const store = new Vuex.Store({ + // ... + plugins: [myPlugin] +}); +``` + +### Вызов мутаций из плагинов + +Плагинам не разрешается напрямую изменять состояние приложения — как и компоненты, они могут только вызывать изменения опосредованно, используя мутации. + +Вызывая мутации, плагин может синхронизировать источник данных с хранилищем данных в приложении. Например, для синхронизации хранилища с веб-сокетом (пример намеренно упрощён, в реальной ситуации у `createPlugin` были бы дополнительные опции): + +```js +export default function createWebSocketPlugin(socket) { + return store => { + socket.on("data", data => { + store.commit("receiveData", data); + }); + store.subscribe(mutation => { + if (mutation.type === "UPDATE_DATA") { + socket.emit("update", mutation.payload); + } + }); + }; +} +``` + +```js +const plugin = createWebSocketPlugin(socket); + +const store = new Vuex.Store({ + state, + mutations, + plugins: [plugin] +}); +``` + +### Снятие слепков состояния + +Иногда плагину может потребоваться "снять слепок" состояния приложения или сравнить состояния "до" и "после" мутации. Для этого используйте глубокое копирование объекта состояния: + +```js +const myPluginWithSnapshot = store => { + let prevState = _.cloneDeep(store.state); + store.subscribe((mutation, state) => { + let nextState = _.cloneDeep(state); + + // сравнение `prevState` и `nextState`... + + // сохранение состояния для следующей мутации + prevState = nextState; + }); +}; +``` + +**Плагины, снимающие слепки, должны использоваться только на этапе разработки.** При использовании webpack или Browserify, мы можем отдать этот момент на их откуп: + +```js +const store = new Vuex.Store({ + // ... + plugins: process.env.NODE_ENV !== "production" ? [myPluginWithSnapshot] : [] +}); +``` + +Плагин будет использоваться по умолчанию. В production-окружении вам понадобится [DefinePlugin](https://webpack.js.org/plugins/define-plugin/) для webpack, или [envify](https://github.com/hughsk/envify) для Browserify, чтобы изменить значение `process.env.NODE_ENV !== 'production'` на `false` в финальной сборке. + +### Встроенный плагин логирования + +> Если вы используете [vue-devtools](https://github.com/vuejs/vue-devtools), вам он скорее всего не понадобится + +В комплекте с Vuex идёт плагин логирования, который можно использовать при отладке: + +```js +import createLogger from "vuex/dist/logger"; + +const store = new Vuex.Store({ + plugins: [createLogger()] +}); +``` + +Функция `createLogger` принимает следующие опции: + +```js +const logger = createLogger({ + collapsed: false, // автоматически раскрывать залогированные мутации + filter(mutation, stateBefore, stateAfter) { + // возвращает `true`, если мутация должна быть залогирована + // `mutation` — это объект `{ type, payload }` + return mutation.type !== "aBlacklistedMutation"; + }, + transformer(state) { + // обработать состояние перед логированием + // например, позволяет рассматривать только конкретное поддерево + return state.subTree; + }, + mutationTransformer(mutation) { + // мутации логируются в формате `{ type, payload }`, + // но это можно изменить + return mutation.type; + }, + logger: console // реализация API `console`, по умолчанию `console` +}); +``` + +Логирующий плагин также можно включить напрямую используя отдельный тег `