Răsfoiți Sursa

docs: Russian translation moved to VuePress (#1279)

Alexander Sokolov 7 ani în urmă
părinte
comite
e72935ac8c

+ 46 - 0
docs/.vuepress/config.js

@@ -14,6 +14,11 @@ module.exports = {
       lang: 'ja',
       title: 'Vuex',
       description: 'Vue.js のための集中状態管理'
+    },
+    '/ru/': {
+      lang: 'ru',
+      title: 'Vuex',
+      description: 'Централизованное управление состоянием для Vue.js'
     }
   },
   serviceWorker: true,
@@ -143,6 +148,47 @@ module.exports = {
           '/ja/guide/testing',
           '/ja/guide/hot-reload'
         ]
+      },
+      '/ru/': {
+        label: 'Русский',
+        selectText: 'Languages',
+        editLinkText: 'Изменить эту страницу на GitHub',
+        nav: [
+          {
+            text: 'Руководство',
+            link: '/ru/guide/'
+          },
+          {
+            text: 'Справочник API',
+            link: '/ru/api/'
+          },
+          {
+            text: 'История изменений',
+            link: 'https://github.com/vuejs/vuex/releases'
+          }
+        ],
+        sidebar: [
+          '/ru/installation',
+          '/ru/',
+          '/ru/guide/',
+          {
+            title: 'Основные понятия',
+            collapsable: false,
+            children: [
+              '/ru/guide/state',
+              '/ru/guide/getters',
+              '/ru/guide/mutations',
+              '/ru/guide/actions',
+              '/ru/guide/modules'
+            ]
+          },
+          '/ru/guide/structure',
+          '/ru/guide/plugins',
+          '/ru/guide/strict',
+          '/ru/guide/forms',
+          '/ru/guide/testing',
+          '/ru/guide/hot-reload'
+        ]
       }
     }
   }

+ 3 - 0
docs/.vuepress/public/_redirects

@@ -8,3 +8,6 @@
 
 /ja/api.html  /ja/api/
 /ja/* /ja/guide/:splat
+
+/ru/api.html  /ru/api/
+/ru/* /ru/guide/:splat

+ 63 - 0
docs/ru/README.md

@@ -0,0 +1,63 @@
+# Что такое Vuex?
+
+Vuex — это **паттерн управления состоянием и библиотека** для приложений на Vue.js. Он служит центральным хранилищем данных для всех компонентов приложения и обеспечивает предсказуемость изменения данных при помощи определённых правил. Кроме того, Vuex интегрируется с официальным [расширением инструментов разработчика](https://github.com/vuejs/vue-devtools) Vue, предоставляя "из коробки" такие продвинутые возможности, как "машину времени" при отладке и экспорт/импорт слепков состояния данных.
+
+### Что такое "паттерн управления состоянием"?
+
+Давайте начнём с простого приложения, реализующего счётчик с использованием Vue:
+
+```js
+new Vue({
+  // состояние
+  data() {
+    return {
+      count: 0
+    };
+  },
+  // представление
+  template: `
+    <div>{{ count }}</div>
+  `,
+  // действия
+  methods: {
+    increment() {
+      this.count++;
+    }
+  }
+});
+```
+
+Это самостоятельное приложение состоит из следующих частей:
+
+* **Состояние** — "источник истины", управляющий приложением;
+* **Представление** — декларативно заданное отображение **состояния**;
+* **Действия** — возможные пути изменения состояния приложения в ответ на взаимодействие пользователя с **представлением**.
+
+Вот простейшее представление концепции "однонаправленного потока данных":
+
+<p style="text-align: center; margin: 2em">
+  <img style="width:100%;max-width:450px;" src="/flow.png">
+</p>
+
+Простота, к сожалению, быстро исчезает при появлении **нескольких компонентов, основывающихся на одном и том же состоянии**, когда:
+
+* Несколько представлений могут зависеть от одной и той же части состояния приложения.
+* Действия из разных представлений могут оказывать влияние на одни и те же части состояния приложения.
+
+Разбираясь с первой проблемой, вам придётся передавать одни и те же данные в несколько глубоко вложенных компонентов. Это часто сложно и неприятно, а для соседних компонентов такое и вовсе не сработает. Решая вторую проблему, приходится обращаться напрямую к родителям и потомкам компонента, или синхронизировать изменения с другими местами в приложении событиями. Оба подхода хрупки и быстро приводят к появлению кода, который невозможно поддерживать.
+
+Так почему бы не вынести всё общее состояние приложения из компонентов в глобальный синглтон? При использовании этого подхода, дерево компонентов превращается в одно большое "представление", а каждый компонент получает доступ к состоянию приложения, наряду с возможностью вызывать действия для изменения состояния, независимо от расположения этого компонента в дереве.
+
+Кроме того, чётко определяя и разделяя концепции, возникающие при управлении состоянием, и требуя соблюдения некоторых правил, мы улучшаем структурированность и поддерживаемость нашего кода.
+
+Такова основная идея, лежащая в основе 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 и использующей его производительную систему реактивных обновлений.
+
+![vuex](/vuex.png)
+
+### В каких случаях использовать Vuex?
+
+Vuex помогает управлять совместно используемым состоянием ценой привнесения новых концепций и вспомогательного кода. Кратковременная продуктивность страдает во благо долгосрочной.
+
+Если вам ещё не доводилось создавать крупномасштабные одностраничные приложения, Vuex может показаться многословным и обескураживающим. Это нормально — несложные приложения вполне могут обойтись и без Vuex. Возможно, вам вполне хватит простой [глобальной шины событий](https://ru.vuejs.org/v2/guide/state-management.html). Но если вы создаёте SPA среднего или крупного размера, вероятно вам уже приходилось сталкиваться с ситуациями, заставляющими задуматься о методах более эффективного управления состоянием приложения за пределами компонентов Vue, и Vuex в таком случае может оказаться вполне естественным следующим шагом. Вот неплохая цитата от Дэна Абрамова, автора Redux:
+
+> Flux-библиотеки подобны очкам: если они вам действительно нужны, вы на этот счёт не сомневаетесь.

+ 263 - 0
docs/ru/api/README.md

@@ -0,0 +1,263 @@
+---
+sidebar: auto
+---
+
+# Справочник API
+
+## Vuex.Store
+
+```js
+import Vuex from 'vuex';
+
+const store = new Vuex.Store({ ...options });
+```
+
+## Опции конструктора Vuex.Store
+
+### state
+
+* тип: `Object | Function`
+
+  Корневой объект состояния хранилища Vuex. [Подробнее](state.md)
+
+  Если вы передаёте функцию, возвращающую объект, то возвращаемый объект будет использован в качестве корневого состояния. Это может быть полезным, если вы хотите повторно использовать объект состояния, особенно при повторном использовании модулей. [Подробнее](modules.md#повторное-использование-модулей)
+
+### mutations
+
+* тип: `{ [type: string]: Function }`
+
+  Регистрирует доступные для хранилища мутации. Обработчики мутаций первым аргументом всегда получают `state` (при использовании модулей это будет локальный state модуля). Вторым аргументом передаётся "нагрузка" (`payload`), если она есть.
+
+  [Подробнее](mutations.md)
+
+### actions
+
+* тип: `{ [type: string]: Function }`
+
+  Регистрирует действия хранилища. В функции-обработчики передаётся объект `context`, со следующими свойствами:
+
+  ```js
+  {
+    state, // то же, что и `store.state`, или локальный state при использовании модулей
+      rootState, // то же, что и `store.state`, только при использовании модулей
+      commit, // то же, что и `store.commit`
+      dispatch, // то же, что и `store.dispatch`
+      getters, // то же, что и `store.getters`
+      rootGetters; // то же, что и `store.getters`, только в модулях
+  }
+  ```
+
+  И также получает вторым аргументом `payload` если нагрузка была передана.
+
+  [Подробнее](actions.md)
+
+### getters
+
+* тип: `{ [key: string]: Function }`
+
+  Регистрирует геттеры, используемые в хранилище. Геттер-функции при вызове получают следующие аргументы:
+
+  ```
+  state,     // при использовании модулей — локальный state модуля
+  getters    // то же, что и store.getters
+  ```
+
+  При определении в модуле
+
+  ```
+  state,       // при использовании модулей — локальный state модуля
+  getters,     // локальные геттеры текущего модуля
+  rootState,   // глобальный state
+  rootGetters  // все геттеры
+  ```
+
+  Зарегистрированные геттеры далее доступны в `store.getters`.
+
+  [Подробнее](getters.md)
+
+### modules
+
+* тип: `Object`
+
+  Объект, содержащий подмодули для помещения в хранилище, в формате:
+
+  ```js
+  {
+    key: {
+      state,
+      namespaced?,
+      mutations?,
+      actions?,
+      getters?,
+      modules?
+    },
+    ...
+  }
+  ```
+
+  Каждый модуль может содержать `state` и `mutations`, как и корневое хранилище. Состояние модуля будет прикреплено к корневому, по указанному ключу. Мутации и геттеры модуля получают при вызове первым аргументом только локальное состояние, а не корневое. При вызове действий `context.state` аналогичным образом указывает на локальное состояние модуля.
+
+  [Подробнее](modules.md)
+
+### plugins
+
+* тип: `Array<Function>`
+
+  Массив функций-плагинов, которые будут применены к хранилищу. Плагины попросту получают хранилище в качестве единственного аргумента, и могут как отслеживать мутации (для сохранения исходящих данных, логирования или отладки) или инициировать их (для обработки входящих данных, например, веб-сокетов или наблюдателей).
+
+  [Подробнее](plugins.md)
+
+### strict
+
+* тип: `Boolean`
+* по умолчанию: `false`
+
+  Заставляет хранилище Vuex использовать strict mode. В strict mode любые изменения состояния, происходящие за пределами обработчиков мутаций, будут выбрасывать ошибки.
+
+  [Подробнее](strict.md)
+
+## Свойства экземпляра Vuex.Store
+
+### state
+
+* тип: `Object`
+
+  Корневое состояние. Только для чтения.
+
+### getters
+
+* тип: `Object`
+
+  Зарегистрированные геттеры. Только для чтения.
+
+## Методы экземпляра Vuex.Store
+
+### commit
+
+* `commit(type: string, payload?: any, options?: Object)`
+* `commit(mutation: Object, options?: Object)`
+
+Запускает мутацию. `options` может содержать опцию `root: true` что разрешает совершать корневые (root) мутации [в модулях со своим пространством имён](modules.md#пространства-имён). [Подробнее](mutations.md)
+
+### dispatch
+
+* `dispatch(type: string, payload?: any, options?: Object)`
+* `dispatch(action: Object, options?: Object)`
+
+Инициирует действие. `options` может содержать опцию `root: true` что разрешает совершать корневые (root) действия [в модулях со своим пространством имён](modules.md#пространства-имён). Возвращает Promise который разрешает все обработчики инициируемых действий. [Подробнее](actions.md)
+
+### replaceState
+
+* `replaceState(state: Object)`
+
+Позволяет заменить корневое состояние хранилища. Используйте только для гидрации состояния / функциональности "машины времени".
+
+### watch
+
+* `watch(fn: Function, callback: Function, options?: Object): Function`
+
+Реактивно отслеживает возвращаемое значение `fn`, и вызывает коллбэк в случае изменений. `fn` получает состояние хранилища первым аргументом, и геттеры вторым аргументом. Принимает опциональный объект с настройками, с такими же параметрами как и у метода Vue `vm.$watch`.
+
+Для прекращения наблюдения, необходимо вызвать возвращаемую функцию обработчик.
+
+### subscribe
+
+* `subscribe(handler: Function): Function`
+
+Подписывается на мутации хранилища. Обработчик `handler` вызывается после каждой мутации и получает в качестве параметров дескриптор мутации и состояние после мутации:
+
+```js
+store.subscribe((mutation, state) => {
+  console.log(mutation.type);
+  console.log(mutation.payload);
+});
+```
+
+Для остановки слежения необходимо вызвать возвращаемую функцию.
+
+Чаще всего используется в плагинах. [Подробнее](plugins.md)
+
+### subscribeAction
+
+* `subscribeAction(handler: Function): Function`
+
+> Добавлено в версии 2.5.0
+
+Подписывается на действие хранилища. Обработчик `handler` вызывается после каждого действия и получает в качестве параметров дескриптов действия и текущее состояние хранилища:
+
+```js
+store.subscribeAction((action, state) => {
+  console.log(action.type);
+  console.log(action.payload);
+});
+```
+
+Для остановки слежения необходимо вызвать возвращаемую функцию.
+
+Чаще всего используется в плагинах. [Подробнее](plugins.md)
+
+### registerModule
+
+* `registerModule(path: string | Array<string>, module: Module, options?: Object)`
+
+Регистрирует динамический модуль. [Подробнее](modules.md#динамическая-регистрация-модулей)
+
+`options` может иметь опцию `preserveState: true`, что позволяет сохранить предыдущее состояние. Полезно с рендерингом на стороне сервера.
+
+### unregisterModule
+
+* `unregisterModule(path: string | Array<string>)`
+
+Разрегистрирует динамический модуль. [Подробнее](modules.md#динамическая-регистрация-модулей)
+
+### hotUpdate
+
+* `hotUpdate(newOptions: Object)`
+
+Осуществляет горячую замену действий и мутаций. [Подробнее](hot-reload.md)
+
+## Вспомогательные функции для компонентов
+
+### mapState
+
+* `mapState(namespace?: string, map: Array<string> | Object<string | function>): Object`
+
+Создаёт проксирующие вычисляемые свойства компонента, возвращающие поддерево state'а хранилища Vuex [Подробнее](state.md#вспомогательная-функция-mapstate)
+
+Первый аргумент опционально может быть строкой с указанным namespace. [Подробнее](modules.md#подключение-с-помощью-вспомогательных-функций-к-пространству-имён)
+
+Второй аргумент вместо объекта может быть функцией. `function(state: any)`
+
+### mapGetters
+
+* `mapGetters(namespace?: string, map: Array<string> | Object<string>): Object`
+
+Создаёт проксирующие вычисляемые свойства компонента, проксирующие доступ к геттерам. [Подробнее](getters.md#вспомогательная-функция-mapgetters)
+
+Первый аргумент опционально может быть строкой с указанным namespace. [Подробнее](modules.md#подключение-с-помощью-вспомогательных-функций-к-пространству-имён)
+
+### mapActions
+
+* `mapActions(namespace?: string, map: Array<string> | Object<string | function>): Object`
+
+Создаёт проксирующие методы компонента, позволяющие диспетчеризировать действия. [Подробнее](actions.md#диспетчеризация-действий-в-компонентах)
+
+Первый аргумент опционально может быть строкой с указанным namespace. [Подробнее](modules.md#подключение-с-помощью-вспомогательных-функций-к-пространству-имён)
+
+Второй аргумент вместо объекта может быть функцией. `function(dispatch: function, ...args: any[])`
+
+### mapMutations
+
+* `mapMutations(namespace?: string, map: Array<string> | Object<string | function>): Object`
+
+Создаёт проксирующие методы компонента, позволяющие инициировать мутации. [Подробнее](mutations.md#вызов-мутаций-в-компонентах)
+
+Первый аргумент опционально может быть строкой с указанным namespace. [Подробнее](modules.md#подключение-с-помощью-вспомогательных-функций-к-пространству-имён)
+
+Второй аргумент вместо объекта может быть функцией. `function(commit: function, ...args: any[])`
+
+### createNamespacedHelpers
+
+* `createNamespacedHelpers(namespace: string): Object`
+
+Создаёт вспомогательные функции для связывания с компонентами для указанного пространства имён. Возвращаемый объект содержит `mapState`, `mapGetters`, `mapActions` и `mapMutations`, которые связаны с указанным пространством имён. [Подробнее](modules.md#подключение-с-помощью-вспомогательных-функций-к-пространству-имён)

+ 42 - 0
docs/ru/guide/README.md

@@ -0,0 +1,42 @@
+# Введение
+
+В центре любого Vuex-приложения находится **хранилище**. "Хранилище" — это, упрощённо говоря, контейнер, который хранит **состояние** вашего приложения. Два момента отличают хранилище Vuex от простого глобального объекта:
+
+1. Хранилища Vuex реактивны. Если компоненты Vue зависят от их состояния, изменение состояния хранилища спровоцирует соответствующие изменения компонентов.
+
+2. Непосредственное изменение состояния хранилища запрещено. Единственный способ внести изменения — явно **вызвать мутацию**. Этот подход позволяет быть уверенным, что каждое изменение оставляет в системе след и даёт возможность использовать инструменты, позволяющие лучше понять работу приложения.
+
+### Простейшее Хранилище
+
+> **ЗАМЕЧАНИЕ:** Мы будем использовать синтаксис ES2015 для примеров кода на всём протяжении этой документации. Если вы с ним ещё не разобрались, [сейчас самое время](https://babeljs.io/docs/learn-es2015/)!
+
+После [установки](../installation.md) Vuex, давайте создадим хранилище. Всё довольно просто: нужно лишь указать исходное состояние и мутации:
+
+``` js
+// Удостоверьтесь, что вызвали Vue.use(Vuex) в коде до этого, если используете модульный сборщик
+
+const store = new Vuex.Store({
+  state: {
+    count: 0
+  },
+  mutations: {
+    increment (state) {
+      state.count++
+    }
+  }
+})
+```
+
+Теперь мы можем получить доступ к объекту состояния `store.state` или вызвать изменение состояния методом `store.commit`:
+
+``` js
+store.commit('increment')
+
+console.log(store.state.count) // -> 1
+```
+
+Ещё раз заметим: мы вызываем мутацию, вместо того чтобы напрямую изменить `store.state.count`, потому что мы хотим явным образом отслеживать изменения. Это простое архитектурное соглашение делает намерения более очевидными и упрощает понимание изменений состояния приложения при чтении кода. Кроме того, этот подход позволяет реализовать инструменты для логирования каждой мутации, создания моментальных слепков состояния приложения и даже применения "машины времени" при отладке.
+
+Using store state in a component simply involves returning the state within a computed property, because the store state is reactive. Triggering changes simply means committing mutations in component methods.
+
+Вот пример [простейшего приложения Vuex, реализующего счётчик](https://jsfiddle.net/n9jmu5v7/1269/).

+ 178 - 0
docs/ru/guide/actions.md

@@ -0,0 +1,178 @@
+# Действия
+
+Действия — похожи на мутации с несколькими отличиями:
+
+* Вместо того, чтобы напрямую менять состояние, действия инициируют мутации;
+* Действия могут использоваться для асинхронных операций.
+
+Зарегистрируем простое действие:
+
+```js
+const store = new Vuex.Store({
+  state: {
+    count: 0
+  },
+  mutations: {
+    increment(state) {
+      state.count++;
+    }
+  },
+  actions: {
+    increment(context) {
+      context.commit('increment');
+    }
+  }
+});
+```
+
+Обработчики действий получают объект контекста, содержащий те же методы и свойства, что и сам экземпляр хранилища, так что вы можете вызвать `context.commit` для инициирования мутации или обратиться к состоянию и геттерам через `context.state` и `context.getters`. Позднее при рассмотрении [Модулей](modules.md) мы увидим, однако, что этот контекст — не то же самое, что экземпляр хранилища.
+
+На практике для упрощения кода часто используется [деструктуризация аргументов](https://github.com/lukehoban/es6features#destructuring) из ES2015 (особенно при необходимости многократного вызова `commit`):
+
+```js
+actions: {
+  increment ({ commit }) {
+    commit('increment')
+  }
+}
+```
+
+### Диспетчеризация действий
+
+Действия запускаются методом `store.dispatch`:
+
+```js
+store.dispatch('increment');
+```
+
+На первый взгляд может выглядеть глупо: если мы хотим увеличить значение count, почему бы просто не вызвать `store.commit('increment')` напрямую? Помните что **мутации должны быть синхронными**? Для действий такого ограничения нет. Внутри действий можно выполнять **асинхронные** операции:
+
+```js
+actions: {
+  incrementAsync ({ commit }) {
+    setTimeout(() => {
+      commit('increment')
+    }, 1000)
+  }
+}
+```
+
+Действия поддерживают тот же формат для передачи нагрузки, а также объектный синтаксис:
+
+```js
+// вызов с нагрузкой
+store.dispatch('incrementAsync', {
+  amount: 10
+});
+
+// объектный синтаксис
+store.dispatch({
+  type: 'incrementAsync',
+  amount: 10
+});
+```
+
+Более приближённым к реальности примером действий будет формирование заказа на основе состояния корзины покупок. Логика такого действия включает в себя **вызов асинхронного API** и **инициализацию нескольких мутаций**:
+
+```js
+actions: {
+  checkout ({ commit, state }, products) {
+    // сохраним находящиеся на данный момент в корзине товары
+    const savedCartItems = [...state.cart.added]
+    // инициируем запрос и "оптимистично" очистим корзину
+    commit(types.CHECKOUT_REQUEST)
+    // предположим, что API магазина позволяет передать коллбэки
+    // для обработки успеха и неудачи при формировании заказа
+    shop.buyProducts(
+      products,
+      // обработка успешного исхода
+      () => commit(types.CHECKOUT_SUCCESS),
+      // обработка неудачного исхода
+      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
+    )
+  }
+}
+```
+
+Таким образом удаётся организовать поток асинхронных операций, записывая побочные эффекты действий в виде мутаций состояния.
+
+### Диспетчеризация действий в компонентах
+
+Диспетчеризировать действия в компонентах можно при помощи `this.$store.dispatch('xxx')` или используя вспомогательную функцию `mapActions`, создающую локальные псевдонимы для действий в виде методов компонента (требуется наличие корневого `$store`):
+
+```js
+import { mapActions } from 'vuex'
+
+export default {
+  // ...
+  methods: {
+    ...mapActions([
+      'increment' // проксирует `this.increment()` в `this.$store.dispatch('increment')`
+
+      // `mapActions` также поддерживают нагрузку (payloads):
+      'incrementBy' // проксирует `this.incrementBy(amount)` в `this.$store.dispatch('incrementBy', amount)`
+    ]),
+    ...mapActions({
+      add: 'increment' // проксирует `this.add()` в `this.$store.dispatch('increment')`
+    })
+  }
+};
+```
+
+### Композиция действий
+
+Раз действия зачастую асинхронны, то как узнать, что действие уже завершилось? И, что важнее, как быть со связанными между собой действиями при организации более сложных асинхронных потоков?
+
+Первое, что нужно знать — `store.dispatch` может обрабатывать Promise, возвращаемый обработчиком действия, и также возвращает Promise:
+
+```js
+actions: {
+  actionA ({ commit }) {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        commit('someMutation')
+        resolve()
+      }, 1000)
+    })
+  }
+}
+```
+
+Теперь можно сделать так:
+
+```js
+store.dispatch('actionA').then(() => {
+  // ...
+});
+```
+
+А в другом действии — так:
+
+```js
+actions: {
+  // ...
+  actionB ({ dispatch, commit }) {
+    return dispatch('actionA').then(() => {
+      commit('someOtherMutation')
+    })
+  }
+}
+```
+
+Наконец, если мы используем [async / await](https://tc39.github.io/ecmascript-asyncawait/), то можем компоновать наши действия следующим образом:
+
+```js
+// предположим, что `getData()` и `getOtherData()` возвращают Promise
+
+actions: {
+  async actionA ({ commit }) {
+    commit('gotData', await getData())
+  },
+  async actionB ({ dispatch, commit }) {
+    await dispatch('actionA') // дожидаемся завершения действия `actionA`
+    commit('gotOtherData', await getOtherData())
+  }
+}
+```
+
+> `store.dispatch` может вызывать несколько обработчиков действий в различных модулях одновременно. В этом случае возвращаемым значением будет Promise, разрешающийся после разрешения всех вызванных обработчиков.

+ 13 - 0
docs/ru/guide/core-concepts.md

@@ -0,0 +1,13 @@
+# Основные понятия
+
+В этих главах мы изучим основные понятия Vuex:
+
+* [Состояние](state.md)
+* [Геттеры](getters.md)
+* [Мутации](mutations.md)
+* [Действия](actions.md)
+* [Модули](modules.md)
+
+Для использования vuex необходимо глубокое понимание всех этих понятий.
+
+Давайте начнём.

+ 62 - 0
docs/ru/guide/forms.md

@@ -0,0 +1,62 @@
+# Работа с формами
+
+Если вы используете Vuex в strict mode, привязать `v-model` к состоянию, хранимому во Vue, может быть немного непросто:
+
+```html
+<input v-model="obj.message">
+```
+
+Предположим, что `obj` — это вычисляемое свойство, возвращающее ссылку на объект из хранилища. В таком случае, `v-model` будет пытаться напрямую изменить значение `obj.message` в ответ на действия пользователя. В strict mode это спровоцирует ошибку, поскольку это изменение происходит вне обработчика мутации Vuex.
+
+Чтобы подружить Vuex с такой ситуацией, следует однонаправленно связать атрибут `value` элемента `<input>` с полем объекта, а для учёта изменений использовать событие `change`:
+
+```html
+<input :value="message" @input="updateMessage">
+```
+
+```js
+// ...
+computed: {
+  ...mapState({
+    message: state => state.obj.message
+  })
+},
+methods: {
+  updateMessage (e) {
+    this.$store.commit('updateMessage', e.target.value)
+  }
+}
+```
+
+А вот и обработчик мутаций:
+
+```js
+// ...
+mutations: {
+  updateMessage (state, message) {
+    state.obj.message = message
+  }
+}
+```
+
+### Двухнаправленные вычисляемые свойства
+
+Заметно, что получившаяся выше запись — куда многословнее, чем используемая в связке `v-model` с локальным состоянием, да и некоторые полезные возможности `v-model` мы таким образом упускаем. В качестве альтернативы можно предложить использование двунаправленного вычисляемого свойства с сеттером:
+
+```html
+<input v-model="message">
+```
+
+```js
+// ...
+computed: {
+  message: {
+    get () {
+      return this.$store.state.obj.message
+    },
+    set (value) {
+      this.$store.commit('updateMessage', value)
+    }
+  }
+}
+```

+ 116 - 0
docs/ru/guide/getters.md

@@ -0,0 +1,116 @@
+# Геттеры
+
+Иногда может понадобится доступ к производным данным, основывающимся на состоянии хранилища: например, к отфильтрованной версии списка или количеству элементов в нём:
+
+```js
+computed: {
+  doneTodosCount () {
+    return this.$store.state.todos.filter(todo => todo.done).length
+  }
+}
+```
+
+Если эта функциональность требуется более чем одному компоненту, понадобится либо дублировать функцию, либо выносить её в совместно используемый хелпер и импортировать в нескольких местах. Оба эти подхода далеки от идеала.
+
+Vuex позволяет определять в хранилище «геттеры». Вы можете считать их вычисляемыми свойствами для хранилища. Как и вычисляемые свойства, результаты геттера кэшируются, основываясь на своих зависимостях и будут пересчитаны только тогда, когда изменится одна из его зависимостей.
+
+Геттеры получают первым аргументом ссылку на состояние хранилища:
+
+```js
+const store = new Vuex.Store({
+  state: {
+    todos: [
+      { id: 1, text: '...', done: true },
+      { id: 2, text: '...', done: false }
+    ]
+  },
+  getters: {
+    doneTodos: state => {
+      return state.todos.filter(todo => todo.done);
+    }
+  }
+});
+```
+
+### Стиль обращения как к свойствам
+
+Геттеры доступны в объекте `store.getters`, и вы можете получить доступ к значениям как для обычных свойств:
+
+```js
+store.getters.doneTodos; // -> [{ id: 1, text: '...', done: true }]
+```
+
+Вторым аргументом передаётся список всех геттеров:
+
+```js
+getters: {
+  // ...
+  doneTodosCount: (state, getters) => {
+    return getters.doneTodos.length;
+  };
+}
+```
+
+```js
+store.getters.doneTodosCount; // -> 1
+```
+
+Теперь мы можем легко использовать их внутри любых компонентов:
+
+```js
+computed: {
+  doneTodosCount () {
+    return this.$store.getters.doneTodosCount
+  }
+}
+```
+
+Обратите внимание, что геттеры, доступ к которым выполняется как к свойствам, будут кэшироваться как часть системы реактивности Vue.
+
+### Стиль обращения как к методам
+
+Вы также можете передавать аргументы геттерам возвращая функцию. Это особенно полезно, если вы хотите вернуть элемент (или часть элементов) массива по переданному критерию:
+
+```js
+getters: {
+  // ...
+  getTodoById: state => id => {
+    return state.todos.find(todo => todo.id === id);
+  };
+}
+```
+
+```js
+store.getters.getTodoById(2); // -> { id: 2, text: '...', done: false }
+```
+
+Обратите внимание, что геттеры, доступ к которым выполняется как к методам, будут запускаться каждый раз при их вызове, а результаты не будут кэшироваться.
+
+### Вспомогательная функция `mapGetters`
+
+Функция `mapGetters` попросту проксирует геттеры хранилища через локальные вычисляемые свойства компонента:
+
+```js
+import { mapGetters } from 'vuex';
+
+export default {
+  // ...
+  computed: {
+    // примешиваем геттеры в вычисляемые свойства оператором расширения
+    ...mapGetters([
+      'doneTodosCount',
+      'anotherGetter'
+      // ...
+    ])
+  }
+};
+```
+
+Если вы хотите использовать при проксировании другое имя, примените объектный синтаксис:
+
+```js
+...mapGetters({
+  // проксируем `this.doneCount` в `store.getters.doneTodosCount`
+  doneCount: 'doneTodosCount'
+})
+```

+ 44 - 0
docs/ru/guide/getting-started.md

@@ -0,0 +1,44 @@
+# Введение
+
+В центре любого Vuex-приложения находится **хранилище**. "Хранилище" — это, упрощённо говоря, контейнер, который хранит **состояние** вашего приложения. Два момента отличают хранилище Vuex от простого глобального объекта:
+
+1.  Хранилища Vuex реактивны. Если компоненты Vue зависят от их состояния, изменение состояния хранилища спровоцирует соответствующие изменения компонентов.
+
+2.  Непосредственное изменение состояния хранилища запрещено. Единственный способ внести изменения — явно **вызвать мутацию**. Этот подход позволяет быть уверенным, что каждое изменение оставляет в системе след и даёт возможность использовать инструменты, позволяющие лучше понять работу приложения.
+
+### Простейшее хранилище
+
+> **ПРИМЕЧАНИЕ:** Мы будем использовать синтаксис ES2015 для примеров кода на всём протяжении этой документации. Если вы с ним ещё не разобрались, [сейчас самое время](https://babeljs.io/docs/learn-es2015/)!
+
+После [установки](installation.md) Vuex, давайте создадим хранилище. Всё довольно просто: нужно лишь указать исходное состояние и мутации:
+
+```js
+// Удостоверьтесь, что вызвали Vue.use(Vuex) в коде до этого, если используете модульный сборщик
+
+const store = new Vuex.Store({
+  state: {
+    count: 0
+  },
+  mutations: {
+    increment(state) {
+      state.count++;
+    }
+  }
+});
+```
+
+Теперь мы можем получить доступ к объекту состояния `store.state` или вызвать изменение состояния методом `store.commit`:
+
+```js
+store.commit('increment');
+
+console.log(store.state.count); // -> 1
+```
+
+Ещё раз заметим: мы вызываем мутацию, вместо того чтобы напрямую изменить `store.state.count`, потому что мы хотим явным образом отслеживать изменения. Это простое архитектурное соглашение делает намерения более очевидными и упрощает понимание изменений состояния приложения при чтении кода. Кроме того, этот подход позволяет реализовать инструменты для логирования каждой мутации, создания моментальных слепков состояния приложения и даже применения "машины времени" при отладке.
+
+Поскольку хранилище реактивно, для использования его состояния в компонентах достаточно просто создать вычисляемые свойства. Изменения состояния можно вызывать, инициализируя мутации в методах компонентов.
+
+Вот пример [простейшего приложения Vuex, реализующего счётчик](https://jsfiddle.net/n9jmu5v7/1269/).
+
+Далее мы подробно рассмотрим все основные понятия, начиная с [состояния](state.md)

+ 44 - 0
docs/ru/guide/hot-reload.md

@@ -0,0 +1,44 @@
+# Горячая перезагрузка
+
+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()`:
+
+```js
+// store.js
+import Vue from 'vue'
+import Vuex from 'vuex'
+import mutations from './mutations'
+import moduleA from './modules/a'
+
+Vue.use(Vuex)
+
+const state = { ... }
+
+const store = new Vuex.Store({
+  state,
+  mutations,
+  modules: {
+    a: moduleA
+  }
+})
+
+if (module.hot) {
+  // рассматриваем действия и мутации как модули для горячей замены
+  module.hot.accept(['./mutations', './modules/a'], () => {
+    // запрашиваем обновлённые модули
+    // (нужно указать .default из-за формата вывода Babel 6)
+    const newMutations = require('./mutations').default
+    const newModuleA = require('./modules/a').default
+    // заменяем старые действия и мутации новыми
+    store.hotUpdate({
+      mutations: newMutations,
+      modules: {
+        a: newModuleA
+      }
+    })
+  })
+}
+```
+
+[Пример counter-hot](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot) позволяет посмотреть на горячую перезагрузку в реальной жизни.

+ 317 - 0
docs/ru/guide/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'
+    };
+  }
+  // мутации, действия, геттеры...
+};
+```

+ 173 - 0
docs/ru/guide/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).

+ 124 - 0
docs/ru/guide/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`
+});
+```
+
+Логирующий плагин также можно включить напрямую используя отдельный тег `<script>`, помещающий функцию `createVuexLogger` в глобальное пространство имён.
+
+Обратите внимание, что этот плагин делает слепки состояний, поэтому использовать его стоит только на этапе разработки.

+ 109 - 0
docs/ru/guide/state.md

@@ -0,0 +1,109 @@
+# Состояние
+
+### Единое дерево состояния
+
+Vuex использует **единое дерево состояния** — таким образом, один-единственный объект содержит всё глобальное состояние приложения и служит "единственным источником истины". Кроме того, это значит, что обычно для каждого приложения существует только одно хранилище. Благодаря единому дереву можно легко найти нужную часть состояния или делать слепки всего состояния приложения для отладки.
+
+Единое дерево состояния не конфликтует с концепцией модульности — в последующих главах мы расскажем, как можно разбить хранилище на подмодули.
+
+### Передача состояния Vuex в компоненты Vue
+
+Так как же отобразить состояние хранилища в компонентах Vue? Поскольку хранилище Vuex реактивно, проще всего использовать [вычисляемые свойства](https://ru.vuejs.org/v2/guide/computed.html):
+
+```js
+// создадим компонент-счётчик:
+const Counter = {
+  template: `<div>{{ count }}</div>`,
+  computed: {
+    count() {
+      return store.state.count;
+    }
+  }
+};
+```
+
+Любые изменения `store.state.count` вызовут обновление вычисляемого свойства, которые инициируют соответствующие обновления в DOM.
+
+Однако этот паттерн заставляет компонент явно полагаться на глобальный синглтон хранилища. При использовании модульной системы, это потребует импортирования хранилища в каждый компонент, использующий глобальное состояние и приведет к усложнению тестирования.
+
+Vuex предоставляет механизм "инъекции" хранилища всем потомкам компонента, у которого указана опция `store` (предварительно необходимо вызвать `Vue.use(Vuex)`):
+
+```js
+const app = new Vue({
+  el: '#app',
+  // указываем хранилище в опции "store", что обеспечит
+  // доступ к нему также и для всех дочерних компонентов
+  store,
+  components: { Counter },
+  template: `
+    <div class="app">
+      <counter></counter>
+    </div>
+  `
+});
+```
+
+Указывая опцию `store` для корневого экземпляра, мы обеспечиваем доступ к хранилищу всем дочерним компонентам в `this.$store`. Давайте обновим наш пример со счётчиком:
+
+```js
+const Counter = {
+  template: `<div>{{ count }}</div>`,
+  computed: {
+    count() {
+      return this.$store.state.count;
+    }
+  }
+};
+```
+
+### Вспомогательная функция `mapState`
+
+Если компонент использует множество свойств или геттеров хранилища, объявление доступа к ним всем вручную может заставить изрядно заскучать, да и код получится многословный. Чтобы обойти эту проблему, можно использовать хелпер `mapState`, автоматически генерирующий вычисляемые свойства, проксирующие доступ к состоянию и геттерам хранилища:
+
+```js
+// с полной сборкой можно использовать как Vuex.mapState
+import { mapState } from 'vuex';
+
+export default {
+  // ...
+  computed: mapState({
+    // стрелочные функции позволяют писать код очень лаконично
+    count: state => state.count,
+
+    // передача строки 'count' эквивалентна записи `state => state.count`
+    countAlias: 'count',
+
+    // если требуется доступ и к локальному состоянию, нужно использовать традиционную функцию
+    countPlusLocalState(state) {
+      return state.count + this.localCount;
+    }
+  })
+};
+```
+
+В простых случаях в `mapState` можно передать и просто массив строк:
+
+```js
+computed: mapState([
+  // проксирует через this.count доступ к store.state.count
+  'count'
+]);
+```
+
+### Оператор распространения объектов
+
+Обратите внимание: `mapState` возвращает объект. Как же быть, если нам нужны и локальные вычисляемые свойства? Обычно в таких случаях приходилось использовать вспомогательные утилиты для слияния объектов, и передавать уже результат их работы в `computed`. Однако, применив [оператор распространения объектов](https://github.com/sebmarkbage/ecmascript-rest-spread) (находящегося в статусе stage-4 ECMAScript proposal) мы можем изрядно упростить запись:
+
+```js
+computed: {
+  localComputed () { /* ... */ },
+  // результаты работы mapState будут добавлены в уже существующий объект
+  ...mapState({
+    // ...
+  })
+}
+```
+
+### Компоненты всё ещё могут иметь локальное состояние
+
+То что вы используете Vuex не значит, что нужно выносить в хранилище **всё** состояние приложения. Поместив большую часть логики во Vuex, вы сделаете мутации более красноречивыми и удобными для отладки, но это иногда может привести к излишней многословности и ненужному усложнению логики. Если состояние компонента полностью локально, выносить его во Vuex может быть бессмысленно. Конечное решение всегда остаётся на усмотрение разработчика и зависит от потребностей конкретного приложения.

+ 25 - 0
docs/ru/guide/strict.md

@@ -0,0 +1,25 @@
+# Строгий режим (strict mode)
+
+Для включения строгого режима просто укажите `strict: true` при создании хранилища Vuex:
+
+```js
+const store = new Vuex.Store({
+  // ...
+  strict: true
+});
+```
+
+В строгом режиме любая попытка внесения изменений в состояние Vuex, кроме мутаций, будет выбрасывать ошибку. Это гарантирует, что все мутации состояния будут явно отслеживаться через инструменты отладки.
+
+### Разработка vs production
+
+**Не используйте строгий режим в production!** Строгий режим запускает глубокое отслеживание за деревом состояния приложения в синхронном режиме для обнаружения несоответствующих мутаций, и это может быть затратным для производительности, когда совершается большое количество мутаций. Убедитесь, что выключили этот режим в production чтобы избежать ухудшения производительности.
+
+Аналогично плагинам, при использовании инструментов сборки это можно сделать так:
+
+```js
+const store = new Vuex.Store({
+  // ...
+  strict: process.env.NODE_ENV !== 'production'
+});
+```

+ 32 - 0
docs/ru/guide/structure.md

@@ -0,0 +1,32 @@
+# Структура приложения
+
+В действительности Vuex не накладывает каких-то значительных ограничений на используемую структуру кода. Однако, он требует соблюдения нескольких высокоуровневых принципов:
+
+1.  Глобальное состояние приложения должно содержаться в глобальном хранилище;
+
+2.  Единственным механизмом изменения этого состояния являются **мутации**, являющиеся синхронными транзакциями;
+
+3.  Асинхронные операции инкапсулируются в **действия** или их комбинации.
+
+Покуда вы следуете этим правилам, можно использовать любую структуру проекта. Если ваш файл хранилища становится слишком большим, просто начните выносить действия, мутации и геттеры в отдельные файлы.
+
+Для любого нетривиального приложения скорее всего понадобится использование модулей. Вот пример возможной структуры проекта:
+
+```bash
+├── index.html
+├── main.js
+├── api
+│   └── ... # абстракции для выполнения запросов к API
+├── components
+│   ├── App.vue
+│   └── ...
+└── store
+    ├── index.js          # здесь мы собираем модули и экспортируем хранилище
+    ├── actions.js        # корневые действия
+    ├── mutations.js      # корневые мутации
+    └── modules
+        ├── cart.js       # модуль корзины
+        └── products.js   # модуль товаров
+```
+
+Для справки можно использовать [пример корзины покупок](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart).

+ 258 - 0
docs/ru/guide/testing.md

@@ -0,0 +1,258 @@
+# Тестирование
+
+В основном предметом модульного тестирования во Vuex являются мутации и действия.
+
+### Тестирование мутаций
+
+Мутации тестировать довольно просто, так как они представляют из себя всего лишь простые функции, поведение которых полностью зависит от переданных параметров. Один трюк заключается в том, что если вы используете модули ES2015 и помещаете свои мутации в файле `store.js`, то помимо экспорта по умолчанию, вы должны экспортировать мутации с помощью именованного экспорта:
+
+```js
+const state = { ... }
+
+// именованный экспорт `mutations` отдельно от самого хранилища
+export const mutations = { ... }
+
+export default new Vuex.Store({
+  state,
+  mutations
+})
+```
+
+Пример тестирования мутаций с использованием Mocha + Chai (хотя вы не ограничены этими библиотеками и можете использовать любые другие):
+
+```js
+// mutations.js
+export const mutations = {
+  increment: state => state.count++
+};
+```
+
+```js
+// mutations.spec.js
+import { expect } from 'chai';
+import { mutations } from './store';
+
+// деструктурирующее присваивание из `mutations`
+const { increment } = mutations;
+
+describe('mutations', () => {
+  it('INCREMENT', () => {
+    // фиксируем состояние
+    const state = { count: 0 };
+    // применяем мутацию
+    increment(state);
+    // оцениваем результат
+    expect(state.count).to.equal(1);
+  });
+});
+```
+
+### Тестирование действий
+
+Действия тестировать несколько сложнее, поскольку они могут обращаться ко внешним API. При тестировании действий обычно приходится заниматься подделкой внешних объектов — например, вызовы к API можно вынести в отдельный сервис, и в рамках тестов этот сервис подменить поддельным. Для упрощения имитации зависимостей можно использовать webpack и [inject-loader](https://github.com/plasticine/inject-loader) для сборки файлов тестов.
+
+Пример тестирования асинхронного действия:
+
+```js
+// actions.js
+import shop from '../api/shop';
+
+export const getAllProducts = ({ commit }) => {
+  commit('REQUEST_PRODUCTS');
+  shop.getProducts(products => {
+    commit('RECEIVE_PRODUCTS', products);
+  });
+};
+```
+
+```js
+// actions.spec.js
+
+// для inline-загрузчиков используйте синтаксис require
+// и inject-loader, возвращающий фабрику модулей, помогающую
+// подменять зависимости
+import { expect } from 'chai';
+const actionsInjector = require('inject-loader!./actions');
+
+// создаём поддельную зависимость
+const actions = actionsInjector({
+  '../api/shop': {
+    getProducts(cb) {
+      setTimeout(() => {
+        cb([
+          /* поддельный ответ от сервера */
+        ]);
+      }, 100);
+    }
+  }
+});
+
+// вспомогательная функция для тестирования действия, которое должно вызывать известные мутации
+const testAction = (action, payload, state, expectedMutations, done) => {
+  let count = 0;
+
+  // поддельная функция вызова мутаций
+  const commit = (type, payload) => {
+    const mutation = expectedMutations[count];
+
+    try {
+      expect(type).to.equal(mutation.type);
+      if (payload) {
+        expect(payload).to.deep.equal(mutation.payload);
+      }
+    } catch (error) {
+      done(error);
+    }
+
+    count++;
+    if (count >= expectedMutations.length) {
+      done();
+    }
+  };
+
+  // вызываем действие с поддельным хранилищем и аргументами
+  action({ commit, state }, payload);
+
+  // проверяем, были ли инициированы мутации
+  if (expectedMutations.length === 0) {
+    expect(count).to.equal(0);
+    done();
+  }
+};
+
+describe('actions', () => {
+  it('getAllProducts', done => {
+    testAction(
+      actions.getAllProducts,
+      null,
+      {},
+      [
+        { type: 'REQUEST_PRODUCTS' },
+        {
+          type: 'RECEIVE_PRODUCTS',
+          payload: {
+            /* поддельный ответ */
+          }
+        }
+      ],
+      done
+    );
+  });
+});
+```
+
+Если у вас есть шпионы (spies), доступные в тестовой среде (например, через [Sinon.JS](http://sinonjs.org/)), вы можете использовать их вместо вспомогательной функции `testAction`:
+
+```js
+describe('actions', () => {
+  it('getAllProducts', () => {
+    const commit = sinon.spy();
+    const state = {};
+
+    actions.getAllProducts({ commit, state });
+
+    expect(commit.args).to.deep.equal([
+      ['REQUEST_PRODUCTS'],
+      [
+        'RECEIVE_PRODUCTS',
+        {
+          /* mocked response */
+        }
+      ]
+    ]);
+  });
+});
+```
+
+### Тестирование геттеров
+
+Геттеры, занимающиеся сложными вычислениями, тоже неплохо бы тестировать. Как и с мутациями, тут всё просто:
+
+Пример тестирования геттера:
+
+```js
+// getters.js
+export const getters = {
+  filteredProducts(state, { filterCategory }) {
+    return state.products.filter(product => {
+      return product.category === filterCategory;
+    });
+  }
+};
+```
+
+```js
+// getters.spec.js
+import { expect } from 'chai';
+import { getters } from './getters';
+
+describe('getters', () => {
+  it('filteredProducts', () => {
+    // поддельное состояние
+    const state = {
+      products: [
+        { id: 1, title: 'Apple', category: 'fruit' },
+        { id: 2, title: 'Orange', category: 'fruit' },
+        { id: 3, title: 'Carrot', category: 'vegetable' }
+      ]
+    };
+    // поддельный параметр геттера
+    const filterCategory = 'fruit';
+
+    // получаем результат выполнения тестируемого геттера
+    const result = getters.filteredProducts(state, { filterCategory });
+
+    // проверяем результат
+    expect(result).to.deep.equal([
+      { id: 1, title: 'Apple', category: 'fruit' },
+      { id: 2, title: 'Orange', category: 'fruit' }
+    ]);
+  });
+});
+```
+
+### Запуск тестов
+
+Если вы должным образом соблюдаете правила написания мутаций и действий, результирующие тесты не должны зависеть от API браузера. Поэтому их можно просто собрать webpack'ом и запустить в Node. С другой стороны, можно использовать `mocha-loader` или Karma + `karma-webpack`, и запускать тесты в реальных браузерах.
+
+#### Запуск в Node
+
+Используйте следующую конфигурацию webpack (в сочетании с соответствующим [`.babelrc`](https://babeljs.io/docs/usage/babelrc/)):
+
+```js
+// webpack.config.js
+module.exports = {
+  entry: './test.js',
+  output: {
+    path: __dirname,
+    filename: 'test-bundle.js'
+  },
+  module: {
+    loaders: [
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        exclude: /node_modules/
+      }
+    ]
+  }
+};
+```
+
+Затем в терминале:
+
+```bash
+webpack
+mocha test-bundle.js
+```
+
+#### Запуск в браузерах
+
+1.  Установите `mocha-loader`
+2.  Измените `entry` в приведённой выше конфигурации Webpack на `'mocha-loader!babel-loader!./test.js'`.
+3.  Запустите `webpack-dev-server`, используя эту конфигурацию
+4.  Откройте в браузере `localhost:8080/webpack-dev-server/test-bundle`.
+
+#### Запуск в браузерах при помощи Karma и karma-webpack
+
+Обратитесь к [документации vue-loader](https://vue-loader.vuejs.org/ru/workflow/testing.html).

+ 77 - 0
docs/ru/installation.md

@@ -0,0 +1,77 @@
+# Установка
+
+### Скачивание напрямую / CDN
+
+[https://unpkg.com/vuex](https://unpkg.com/vuex)
+
+<!--email_off-->
+
+[Unpkg.com](https://unpkg.com) предоставляет CDN-ссылки на содержимое NPM-пакетов. Приведённая выше ссылка всегда будет указывать на самый свежий релиз Vuex, доступный в NPM. Кроме того, можно указать конкретную версию или тег, например `https://unpkg.com/vuex@2.0.0`.
+
+<!--/email_off-->
+
+Подключите `vuex` после Vue, и установка произойдёт автоматически:
+
+```html
+<script src="/path/to/vue.js"></script>
+<script src="/path/to/vuex.js"></script>
+```
+
+### 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);
+```
+
+При использовании глобальных тегов `<script>` в этом нет необходимости.
+
+### Promise
+
+Vuex для работы необходимы [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises). Если браузеры, которые вы поддерживаете не реализуют Promise (например, IE), то вы можете использовать библиотеку-полифилл, такую как [es6-promise](https://github.com/stefanpenner/es6-promise).
+
+Вы можете подключить её через CDN:
+
+```html
+<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script>
+```
+
+Тогда `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
+```