Răsfoiți Sursa

[DONE, ready for merge; additional proof-reading/styling is welcome] Translate v2 docs in Russian. (#447)

* russian docs translation started

* docs/ru/actions.md translated

* docs/ru/api.md translated

* docs/ru/forms.md translated

* api translation fix

* docs/ru/getters.md translated

* docs/ru/getting-started.md translated

* docs/ru/hot-reload.md translated

* docs/ru/installation.md translated

* docs/ru/intro.md translated

* docs/ru/modules.md translated

* docs/ru/README.md translated

* docs/ru/strict.md translated

* docs/ru/structure.md translated

* docs/ru/SUMMARY.md translated (O_O for some reason it happens to be exactly the same as README.md)

* docs/ru/mutations.md translated

* docs/ru/plugins.md translated

* docs/ru/state.md translated

* docs/ru/testing.md translated

* docs/ru/actions.md styling/proof-reading

* docs/ru/api.md styling/proof-reading

* docs/ru/forms.md styling/proof-reading

* docs/ru/getters.md styling/proof-reading

* docs/ru/getting-started.md styling/proof-reading

* docs/ru/hot-reload.md styling/proof-reading

* docs/ru/installation.md styling/proof-reading

* docs/ru/intro.md styling/proof-reading

* docs/ru/modules.md styling/proof-reading

* docs/ru/mutations.md styling/proof-reading

* docs/ru/plugins.md styling/proof-reading

* docs/ru/state.md styling/proof-reading

* docs/ru/strict.md styling/proof-reading

* docs/ru/structure.md styling/proof-reading

* docs/ru/testing.md styling/proof-reading

* docs/ru/intro.md typo fix

* Copyediting RU documentation

* Fix typo

* Change wording around alternative approaches to data handling

* Fix typo

* Fix typo

* Better wording for linters

* Fix header and change wording

* fix typo

* typos reported by @ipelekhan are fixed
Grigoriy Beziuk 8 ani în urmă
părinte
comite
d4f7a0f532

+ 1 - 0
docs/LANGS.md

@@ -1,4 +1,5 @@
 * [2.0 - English](en/)
 * [2.0 - 简体中文](zh-cn/)
 * [2.0 - Français](fr/)
+* [2.0 - Русский](ru/)
 * [1.0 Docs](old/)

+ 22 - 0
docs/ru/README.md

@@ -0,0 +1,22 @@
+# Vuex
+
+> Внимание: это — документация для версии vuex@2.x.
+
+- [Ищете документацию для 1.0?](https://github.com/vuejs/vuex/tree/1.0/docs)
+- [Release Notes](https://github.com/vuejs/vuex/releases)
+- [Установка](installation.md)
+- [Что такое Vuex?](intro.md)
+- [Начало работы](getting-started.md)
+- Основные концепции
+  - [Состояние](state.md)
+  - [Геттеры](getters.md)
+  - [Мутации](mutations.md)
+  - [Действия](actions.md)
+  - [Модули](modules.md)
+- [Структура приложения](structure.md)
+- [Плагины](plugins.md)
+- [Strict Mode](strict.md)
+- [Обработка форм](forms.md)
+- [Тестирование](testing.md)
+- [Горячая замена](hot-reload.md)
+- [Справочник API](api.md)

+ 22 - 0
docs/ru/SUMMARY.md

@@ -0,0 +1,22 @@
+# Vuex
+
+> Внимание: это — документация для версии vuex@2.x.
+
+- [Ищете документацию для 1.0?](https://github.com/vuejs/vuex/tree/1.0/docs)
+- [Release Notes](https://github.com/vuejs/vuex/releases)
+- [Установка](installation.md)
+- [Что такое Vuex?](intro.md)
+- [Начало работы](getting-started.md)
+- Основные концепции
+  - [Состояние](state.md)
+  - [Геттеры](getters.md)
+  - [Мутации](mutations.md)
+  - [Действия](actions.md)
+  - [Модули](modules.md)
+- [Структура приложения](structure.md)
+- [Плагины](plugins.md)
+- [Strict Mode](strict.md)
+- [Обработка форм](forms.md)
+- [Тестирование](testing.md)
+- [Горячая замена](hot-reload.md)
+- [Справочник API](api.md)

+ 175 - 0
docs/ru/actions.md

@@ -0,0 +1,175 @@
+# Действия
+
+Действия — похожи на мутации, с несколькими отличиями:
+
+- Вместо того чтобы напрямую менять состояние, действия инициируют мутации.
+- Действия могут использоваться для асинхронных операций.
+
+Зарегистрируем простое действие:
+
+``` 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 }, payload) {
+    // сохраним находящиеся на данный момент в корзине товары
+    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({
+      add: 'increment' // проксирует this.add() в this.$store.dispatch('increment')
+    })
+  }
+}
+```
+
+### Композиция действий
+
+Раз действия зачастую асинхронны, то как узнать, что действие уже завершилось? И, что важнее, как быть со связанными между собой действиями при организации более сложных асинхронных потоков?
+
+Для начала стоит вспомнить, что `store.dispatch` возвращает значение, равное результату вызванного обработчика действия, что позволяет использовать 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() возвращают промисы
+
+actions: {
+  async actionA ({ commit }) {
+    commit('gotData', await getData())
+  },
+  async actionB ({ dispatch, commit }) {
+    await dispatch('actionA') // дожидаемся завершения действия actionA
+    commit('gotOtherData', await getOtherData())
+  }
+}
+```
+
+> `store.dispatch` может вызывать несколько обработчиков действий в различных модулях одновременно. В этом случае возвращаемым значением будет Promise, разрешающийся после разрешения всех вызванных обработчиков.

+ 177 - 0
docs/ru/api.md

@@ -0,0 +1,177 @@
+# Справочник API
+
+### Vuex.Store
+
+``` js
+import Vuex from 'vuex'
+
+const store = new Vuex.Store({ ...options })
+```
+
+### Опции конструктора Vuex.Store
+
+- **state**
+
+  - тип: `Object`
+
+    Корневой объект состояния хранилища Vuex.
+
+    [Подробнее](state.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
+    }
+    ```
+
+    [Подробнее](actions.md)
+
+- **getters**
+
+  - тип: `{ [key: string]: Function }`
+
+    Регистрирует геттеры, используемые в хранилище. Геттер-функции при вызове получают следующие аргументы:
+
+    ```
+    state,     // при использовании модулей — локальный state модуля
+    getters,   // то же, что и store.getters
+    rootState  // то же, что и store.state
+    ```
+    Зарегистрированные геттеры далее доступны в `store.getters`.
+
+    [Подробнее](getters.md)
+
+- **modules**
+
+  - тип: `Object`
+
+    Объект, содержащий подмодули для помещения в хранилище, в формате:
+
+    ``` js
+    {
+      key: {
+        state,
+        mutations,
+        actions?,
+        getters?,
+        modules?
+      },
+      ...
+    }
+    ```
+
+    Каждый модуль может содержать `state` и `mutations`, как и корневое хранилище. Состояние модуля будет прикреплёно к корневому, по указанному ключу. Мутации и геттеры модуля получают при вызове первым аргументом только локальное состояние, а не корневое. При вызове действий `context.state` аналогичным образом указывает на локальное состояние модуля.
+
+    [Подробнее](modules.md)
+
+- **plugins**
+
+  - тип: `Array<Function>`
+
+    Массив функций-плагинов, которые будут применены к хранилищу. Плагины попросту получают хранилище в качестве единственного аргумента, и могут как отслеживать мутации (для сохранения исходящих данных, логирования, или отладки) или инициировать их (для обработки входящих данных, например вебсокетов или observables).
+
+    [Подробнее](plugins.md)
+
+- **strict**
+
+  - тип: `Boolean`
+  - default: `false`
+
+    Заставляет хранилище Vuex использовать strict mode. В strict mode любые изменения состояния, происходящие за пределами обработчиков мутаций, будут выбрасывать ошибки.
+
+    [Подробнее](strict.md)
+
+### Свойства инстанса Vuex.Store
+
+- **state**
+
+  - тип: `Object`
+
+    Корневое состояние. Только для чтения.
+
+- **getters**
+
+  - тип: `Object`
+
+    Зарегистрированные геттеры. Только для чтения.
+
+### Методы инстанса Vuex.Store
+
+- **`commit(type: string, payload?: any) | commit(mutation: Object)`**
+
+  Запускает мутацию. [Подробнее](mutations.md)
+
+- **`dispatch(type: string, payload?: any) | dispatch(action: Object)`**
+
+  Инициирует действие. Возвращает то же значение, что и вызванный обработчик действия, или Promise, если вызывается несколько обработчиков. [Подробнее](actions.md)
+
+- **`replaceState(state: Object)`**
+
+  Позволяет заменить корневое состояние хранилища. Используйте только для гидрации состояния / функционала "машины времени".
+
+- **`watch(getter: Function, cb: Function, options?: Object)`**
+
+  Устанавливает наблюдение за возвращаемым значением геттера, вызывая коллбэк в случае его изменения. Геттер получает единственный параметр состояние хранилища. Возможно указание дополнительного объекта опций, с такими же параметрами как и у метода `vm.$watch` корневой библиотеки Vue.
+
+  Для прекращения наблюдения, необходимо вызвать возвращённую функцию-хэндлер.
+
+- **`subscribe(handler: Function)`**
+
+  Подписывается на мутации хранилища. `handler` вызывается после каждой мутации и получает в качестве параметров дескриптор мутации и состояние после мутации:
+
+  ``` js
+  store.subscribe((mutation, state) => {
+    console.log(mutation.type)
+    console.log(mutation.payload)
+  })
+  ```
+
+  Чаще всего используется в плагинах. [Подробнее](plugins.md)
+
+- **`registerModule(path: string | Array<string>, module: Module)`**
+
+  Регистрирует динамический модуль. [Подробнее](modules.md#dynamic-module-registration)
+
+- **`unregisterModule(path: string | Array<string>)`**
+
+  Разрегистрирует динамический модуль. [Подробнее](modules.md#dynamic-module-registration)
+
+- **`hotUpdate(newOptions: Object)`**
+
+  Осуществляет горячую замену действий и мутаций. [Подробнее](hot-reload.md)
+
+### Вспомогательные функции для связывания с компонентами
+
+- **`mapState(map: Array<string> | Object): Object`**
+
+  Создаёт проксирующие вычисляемые свойства компонента, возвращающие поддерево state'а хранилища Vuex [Подробнее](state.md#the-mapstate-helper)
+
+- **`mapGetters(map: Array<string> | Object): Object`**
+
+  Создаёт проксирующие вычисляемые свойства компонента, проксирующие доступ к геттерам. [Подробнее](getters.md#the-mapgetters-helper)
+
+- **`mapActions(map: Array<string> | Object): Object`**
+
+  Создаёт проксирующие методы компонента, позволяющие диспетчеризировать действия. [Подробнее](actions.md#dispatching-actions-in-components)
+
+- **`mapMutations(map: Array<string> | Object): Object`**
+
+  Создаёт проксирующие методы компонента, позволяющие инициировать мутации. [Подробнее](mutations.md#commiting-mutations-in-components)

+ 19 - 0
docs/ru/book.json

@@ -0,0 +1,19 @@
+{
+  "gitbook": "2.x.x",
+  "plugins": ["edit-link", "prism", "-highlight", "github"],
+  "pluginsConfig": {
+    "edit-link": {
+      "base": "https://github.com/vuejs/vuex/tree/dev/docs",
+      "label": "Редактировать Эту Страницу"
+    },
+    "github": {
+      "url": "https://github.com/vuejs/vuex/"
+    }
+  },
+  "links": {
+    "sharing": {
+      "facebook": false,
+      "twitter": false
+    }
+  }
+}

+ 58 - 0
docs/ru/forms.md

@@ -0,0 +1,58 @@
+# Обработка форм
+
+Если вы используете 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` мы таким образом упускаем. В качестве альтернативы можно предложить использование двунаправленного вычисляемого свойства с сеттером:
+
+``` js
+// ...
+computed: {
+  message: {
+    get () {
+      return this.$store.state.obj.message
+    },
+    set (value) {
+      this.$store.commit('updateMessage', value)
+    }
+  }
+}
+```
+

+ 92 - 0
docs/ru/getters.md

@@ -0,0 +1,92 @@
+
+# Геттеры
+
+Иногда может понадобится доступ к производным данным, основывающимся на состоянии хранилища: например, к отфильтрованной версии списка или количеству элементов в нём:
+
+``` 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
+  }
+}
+```
+
+### Вспомогательная функция `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/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/yyx990803/n9jmu5v7/).
+
+Далее мы более подробно обсудим каждую из основных концепций, начиная с [Состояния](state.md)

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

@@ -0,0 +1,44 @@
+# Горячая замена
+
+Vuex поддерживает горячую замену мутаций, модулей, действий и геттеров в момент разработки с помощью [Webpack Hot Module Replacement API](https://webpack.github.io/docs/hot-module-replacement.html). Аналогичный функционал в 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) позволяет посмотреть на горячую замену в реальной жизни.

BIN
docs/ru/images/flow.png


BIN
docs/ru/images/vuex.png


+ 42 - 0
docs/ru/installation.md

@@ -0,0 +1,42 @@
+# Установка
+
+### Скачать напрямую или использовать CDN
+
+[https://unpkg.com/vuex](https://unpkg.com/vuex)
+
+[Unpkg.com](https://unpkg.com) предоставляет CDN-ссылки на содержимое NPM-пакетов. Приведённая выше ссылка всегда будет указывать на самый свежий релиз Vuex, доступный в NPM. Кроме того, можно указать конкретную версию или тег, например `https://unpkg.com/vuex@2.0.0`.
+
+Подключите `vuex` после Vue, и установка произойдёт автоматически:
+
+``` html
+<script src="/path/to/vue.js"></script>
+<script src="/path/to/vuex.js"></script>
+```
+
+### NPM
+
+``` bash
+npm install vuex
+```
+
+Если вы используете модули, установите Vuex явным образом командой `Vue.use()`:
+
+``` js
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+```
+
+При использовании глобальных тегов `<script>` в этом нет необходимости.
+
+### Использование версии в разработке
+
+Если вы хотите использовать самую новую dev-сборку, придётся вручную склонировать репозиторий с GitHub и запустить сборку:
+
+``` bash
+git clone https://github.com/vuejs/vuex.git node_modules/vuex
+cd node_modules/vuex
+npm install
+npm run build
+```

+ 63 - 0
docs/ru/intro.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="max-width:450px;" src="./images/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](./images/vuex.png)
+
+### В каких случаях следует использовать Vuex?
+
+Vuex помогает управлять совместно используемым состоянием ценой привнесения новых концепций и вспомогательного кода. Кратковременная продуктивность страдает во благо долгосрочной.
+
+Если вам ещё не доводилось создавать крупномасштабные одностраничные приложения, Vuex может показаться многословным и обескураживающим. Это нормально — несложные приложения вполне могут обойтись и без Vuex. Возможно, вам вполне хватит простой [глобальной шины событий](http://vuejs.org/guide/components.html#Non-Parent-Child-Communication). Но если вы создаёте SPA среднего или крупного размера, вероятно вам уже приходилось сталкиваться с ситуациями, заставляющими задуматься о методах более эффективного управления состоянием приложения за пределами компонентов Vue, и Vuex в таком случае может оказаться вполне естественным следующим шагом. Вот неплохая цитата от Дэна Абрамова, автора Redux:
+
+> Flux-библиотеки подобны очкам: если они вам действительно нужны, вы на этот счёт не сомневаетесь.

+ 138 - 0
docs/ru/modules.md

@@ -0,0 +1,138 @@
+# Модули
+
+Из-за использования единого дерева состояния, все глобальные данные приложения оказываются помещены в один большой объект. По мере роста приложения, хранилище может существенно раздуться.
+
+Чтобы помочь в этой беде, 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: {
+    incrementIfOdd ({ state, commit }) {
+      if (state.count % 2 === 1) {
+        commit('increment')
+      }
+    }
+  }
+}
+```
+
+Кроме того, в геттеры корневое состояние передаётся 3-м параметром:
+
+``` js
+const moduleA = {
+  // ...
+  getters: {
+    sumWithRootCount (state, getters, rootState) {
+      return state.count + rootState.count
+    }
+  }
+}
+```
+
+### Пространства имён
+
+Обратите внимание, что действия, мутации и геттеры, определённые внутри модулей, тем не менее регистрируются в **глобальном пространстве имён** — это позволяет нескольким модулям реагировать на один и тот же тип мутации или действия. Избежать конфликта пространства имён вы можете, указывая для них префикс или суффикс. При создании пригодных для повторного использования модулей Vuex, пожалуй, так поступать даже нужно — кто знает, в каком окружении их будут использовать? Например, предположим что мы создаём модуль `todos`:
+
+``` js
+// types.js
+
+// определим названия геттеров, действий и мутаций как константы
+// используя название модуля (`todos`) в качестве префикса
+export const DONE_COUNT = 'todos/DONE_COUNT'
+export const FETCH_ALL = 'todos/FETCH_ALL'
+export const TOGGLE_DONE = 'todos/TOGGLE_DONE'
+```
+
+``` js
+// modules/todos.js
+import * as types from '../types'
+
+// теперь используем определённые выше константы
+const todosModule = {
+  state: { todos: [] },
+
+  getters: {
+    [types.DONE_COUNT] (state) {
+      // ...
+    }
+  },
+
+  actions: {
+    [types.FETCH_ALL] (context, payload) {
+      // ...
+    }
+  },
+
+  mutations: {
+    [types.TOGGLE_DONE] (state, payload) {
+      // ...
+    }
+  }
+}
+```
+
+### Динамическая регистрация модулей
+
+Вы можете зарегистрировать модуль уже и **после** того, как хранилище было создано, используя метод `store.registerModule`:
+
+``` js
+store.registerModule('myModule', {
+  // ...
+})
+```
+
+Состояние модуля будет доступно как `store.state.myModule`.
+
+Динамическая регистрация модулей позволяет другим плагинам Vue также использовать Vuex для управления своим состоянием, добавляя модуль к хранилищу данных приложения. Например, библиотека [`vuex-router-sync`](https://github.com/vuejs/vuex-router-sync) интегрирует vue-router во vuex, отражая изменение текущего пути приложения в динамически присоединённом модуле.
+
+Удалить динамически зарегистрированный модуль можно с помощью `store.unregisterModule(moduleName)`. Обратите внимание, что статические (определённые на момент создания хранилища) модули при помощи этого метода удалить не получится.

+ 186 - 0
docs/ru/mutations.md

@@ -0,0 +1,186 @@
+# Мутации
+
+Единственным способом изменения состояния хранилища во 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
+  }
+}
+```
+
+### Молчаливые мутации
+
+> Замечание: Эта возможность вероятно будет помечена как нерекоммендованная к использованию после появления функционала фильтрации мутаций в devtools.
+
+По умолчанию, каждая мутация попадает в плагины (например, в devtools). Иногда, впрочем, не хочется, чтобы плагины записывали каждое изменение состояния. Множественные мутации, происходящие в течении короткого периода времени не всегда необходимо отслеживать. В таких случаях существует возможность передать в `store.commit` третий параметр, чтобы "заставить замолчать" эту конкретную мутацию и сделать её невидимой в плагинах:
+
+``` js
+store.commit('increment', {
+  amount: 1
+}, { silent: true })
+
+// при использовании объектного синтаксиса
+store.commit({
+  type: 'increment',
+  amount: 1
+}, { silent: true })
+```
+
+### Мутации следуют правилам реактивности 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({
+      add: 'increment' // this.add() будет вызывать this.$store.commit('increment')
+    })
+  }
+}
+```
+
+### О действиях
+
+Привнесение асинхронности в мутации могло бы изрядно затруднить понимание логики программы. Например, если вызываются два метода, оба с асинхронными коллбэками, изменяющими состояние приложения — как предсказать, какой из коллбэков будет вызван первым? Именно поэтому концепции изменений и асинхронности рассматриваются по отдельности. Во Vuex, **мутации — это синхронные транзакции**:
+
+``` js
+store.commit('increment')
+// все изменения состояния, вызываемые мутацией "increment",
+// к этому моменту уже должны произойти.
+```
+
+Для обработки асинхронных операций существуют [Действия](actions.md).

+ 120 - 0
docs/ru/plugins.md

@@ -0,0 +1,120 @@
+# Плагины
+
+Хранилища 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.github.io/docs/list-of-plugins.html#defineplugin) для 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, // автоматически раскрывать залогированные мутации
+  transformer (state) {
+    // обработать состояние перед логированием
+    // например, позволяет рассматривать только конкретное поддерево
+    return state.subTree
+  },
+  mutationTransformer (mutation) {
+    // мутации логируются в формате { type, payload },
+    // но это можно изменить
+    return mutation.type
+  }
+})
+```
+
+Логирующий плагин можно включить также и используя отдельный тег `<script>`, помещающий функцию `createVuexLogger` в глобальное пространство имён.
+
+Обратите внимание, что этот плагин делает слепки состояний, поэтому использовать его стоит только на этапе разработки.

+ 109 - 0
docs/ru/state.md

@@ -0,0 +1,109 @@
+# Состояние
+
+### Единое дерево состояния
+
+Vuex использует **единое дерево состояния** - таким образом, один-единственный объект содержит всё глобальное состояние приложения и служит "единственным источником истины". Кроме того, это значит, что обычно для каждого приложения существует только одно хранилище. Благодаря единому дереву можно легко найти нужную часть состояния или делать слепки всего состояния приложения для отладки.
+
+Единое дерево состояния не конфликтует с концепцией модульности — в последующих главах мы расскажем, как можно разбить хранилище на подмодули.
+
+### Передача состояния Vuex в компоненты Vue
+
+Так как же отобразить состояние хранилища в компонентах Vue? Поскольку хранилище Vuex реактивно, проще всего использовать [вычисляемые свойства](http://vuejs.org/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({
+    // arrow-функции позволяют писать код очень лаконично
+    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-3 ECMAScript proposal) мы можем изрядно упростить запись:
+
+``` js
+computed: {
+  localComputed () { /* ... */ },
+  // результаты работы mapState будут добавлены в уже существующий объект
+  ...mapState({
+    // ...
+  })
+}
+```
+
+### Компоненты всё ещё могут иметь локальное состояние
+
+То что вы используете Vuex не значит, что нужно выносить в хранилище **всё** состояние приложения. Поместив большую часть логики во Vuex, вы сделаете мутации более красноречивыми и удобными для отладки, но это иногда может привести к излишней многословности и ненужному усложнению логики. Если состояние компонента полностью локально, выносить его во Vuex может быть бессмысленно. Конечное решение всегда остаётся на усмотрение разработчика и зависит от потребностей конкретного приложения.

+ 25 - 0
docs/ru/strict.md

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

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

+ 216 - 0
docs/ru/testing.md

@@ -0,0 +1,216 @@
+# Тестирование
+
+В основном предметом модульного тестирования во Vuex являются мутации и действия.
+
+### Тестирование мутаций
+
+Мутации тестировать довольно просто, так как они представляют из себя всего лишь чистые функции, поведение которых полностью зависит от переданных параметров. Может пригодится возможность ES2015-модулей для самостоятельного именованного экспорта мутаций, наряду с экспортом самого хранилища из файла `store.js`:
+
+``` js
+const state = { ... }
+
+// именованный экспорт мутаций отдельно от самого хранилища
+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 = ({ dispatch }) => {
+  dispatch('REQUEST_PRODUCTS')
+  shop.getProducts(products => {
+    dispatch('RECEIVE_PRODUCTS', products)
+  })
+}
+```
+
+``` js
+// actions.spec.js
+
+// для inline-загрузчиков используйте синтаксис require
+// и inject-loader, возвращающий фабрику модулей, помогающую
+// подменять зависимости
+import { expect } from 'chai'
+const actionsInjector = require('inject!./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]
+    expect(mutation.type).to.equal(type)
+    if (payload) {
+      expect(mutation.payload).to.deep.equal(payload)
+    }
+    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)
+  })
+})
+```
+
+### Тестирование геттеров
+
+Геттеры, занимающиеся сложными вычислениями, тоже неплохо бы тестировать. Как и с мутациями, тут всё просто:
+
+Пример тестирования геттера:
+
+``` 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',
+        exclude: /node_modules/
+      }
+    ]
+  }
+}
+```
+
+Затем, в терминале:
+
+``` bash
+webpack
+mocha test-bundle.js
+```
+
+#### Запуск в браузерах
+
+1. Установите `mocha-loader`
+2. Измените `entry` в приведённой выше конфигурации Webpack на `'mocha!babel!./test.js'`.
+3. Запустите `webpack-dev-server`, используя эту конфигурацию
+4. Откройте в браузере `localhost:8080/webpack-dev-server/test-bundle`.
+
+#### Запуск в браузерах при помощи Karma и karma-webpack
+
+Обратитесь к [документации vue-loader](http://vue-loader.vuejs.org/en/workflow/testing.html).