Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(ru): add Vuex 4.x translation #38

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 65 additions & 1 deletion docs/.vitepress/config.js
Expand Up @@ -23,6 +23,11 @@ module.exports = {
lang: 'pt-BR',
title: 'Vuex',
description: 'Gerenciamento de Estado Centralizado para Vue.js'
},
'/ru/': {
lang: 'ru',
title: 'Vuex',
description: 'Централизованное управление состоянием для Vue.js'
}
},

Expand Down Expand Up @@ -275,7 +280,66 @@ module.exports = {
]
}
]
}
},

'/ru/': {
label: 'Русский',
selectText: 'Переводы',
editLinkText: 'Изменить эту страницу на GitHub',
lastUpdated: 'Последнее обновление',

nav: [
{ text: 'Руководство', link: '/ru/guide/' },
{ text: 'Справочник API', link: '/ru/api/' },
{ text: 'История изменений', link: 'https://github.com/vuejs/vuex/releases' },
{
text: 'v4.x',
items: [
{ text: 'v3.x', link: 'https://v3.vuex.vuejs.org/ru/' }
]
}
],

sidebar: [
{
text: 'Введение',
children: [
{ text: 'Что такое Vuex?', link: '/ru/' },
{ text: 'Установка', link: '/ru/installation' },
{ text: 'Введение', link: '/ru/guide/' }
]
},
{
text: 'Основные понятия',
children: [
{ text: 'Состояние', link: '/ru/guide/state' },
{ text: 'Геттеры', link: '/ru/guide/getters' },
{ text: 'Мутации', link: '/ru/guide/mutations' },
{ text: 'Действия', link: '/ru/guide/actions' },
{ text: 'Модули', link: '/ru/guide/modules' }
]
},
{
text: 'Продвинутые темы',
children: [
{ text: 'Структура приложения', link: '/ru/guide/structure' },
{ text: 'Composition API', link: '/ru/guide/composition-api' },
{ text: 'Плагины', link: '/ru/guide/plugins' },
{ text: 'Строгий режим (strict mode)', link: '/ru/guide/strict' },
{ text: 'Работа c формами', link: '/ru/guide/forms' },
{ text: 'Тестирование', link: '/ru/guide/testing' },
{ text: 'Горячая перезагрузка', link: '/ru/guide/hot-reload' },
{ text: 'Поддержка TypeScript', link: '/ru/guide/typescript-support' },
]
},
{
text: 'Руководство по миграции',
children: [
{ text: 'Миграция на 4.0 с 3.x', link: '/ru/guide/migrating-to-4-0-from-3-x' }
]
}
]
},
}
}
}
6,998 changes: 6,998 additions & 0 deletions docs/public/ru/flow.ai

Large diffs are not rendered by default.

Binary file added docs/public/ru/flow.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3,475 changes: 3,475 additions & 0 deletions docs/public/ru/vuex.ai

Large diffs are not rendered by default.

Binary file added docs/public/ru/vuex.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
398 changes: 398 additions & 0 deletions docs/ru/api/index.md

Large diffs are not rendered by default.

180 changes: 180 additions & 0 deletions docs/ru/guide/actions.md
@@ -0,0 +1,180 @@
# Действия

<div class="scrimba"><a href="https://scrimba.com/p/pnyzgAP/c6ggR3cG" target="_blank" rel="noopener noreferrer">Пройдите этот урок на Scrimba</a></div>

Действия — похожи на мутации с несколькими отличиями:

* Вместо того, чтобы напрямую менять состояние, действия инициируют мутации;
* Действия могут использоваться для асинхронных операций.

Зарегистрируем простое действие:

```js
const store = createStore({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
increment(context) {
context.commit('increment')
}
}
})
```

Обработчики действий получают объект контекста, содержащий те же методы и свойства, что и сам экземпляр хранилища, так что можно вызвать `context.commit` для инициирования мутации или обратиться к состоянию и геттерам через `context.state` и `context.getters`. Можно даже вызывать другие действия через `context.dispatch`. Позднее при рассмотрении [модулей](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, разрешающийся после разрешения всех вызванных обработчиков.
62 changes: 62 additions & 0 deletions docs/ru/guide/composition-api.md
@@ -0,0 +1,62 @@
# Composition API

Чтобы получить доступ к хранилищу в хуке `setup`, можно использовать функцию `useStore`. Это эквивалент получения `this.$store` внутри компонента с помощью Options API.

```js
import { useStore } from 'vuex'

export default {
setup () {
const store = useStore()
}
}
```

## Доступ к состоянию и геттерам

Чтобы получить доступ к состоянию и геттерам, нужно создать `вычисляемые` ref-ссылки для сохранения реактивности. Это эквивалентно созданию вычисляемых свойств с помощью Options API.

```js
import { computed } from 'vue'
import { useStore } from 'vuex'

export default {
setup () {
const store = useStore()

return {
// доступ к состоянию в вычисляемой функции
count: computed(() => store.state.count),

// доступ к геттеру в вычисляемой функции
double: computed(() => store.getters.double)
}
}
}
```

## Доступ к мутациям и действиям

При доступе к мутациям и действиям, можно просто указать методы `commit` и `dispatch` внутри `setup` хука.

```js
import { useStore } from 'vuex'

export default {
setup () {
const store = useStore()

return {
// доступ к мутациям
increment: () => store.commit('increment'),

// доступ к действиям
asyncIncrement: () => store.dispatch('asyncIncrement')
}
}
}
```

## Примеры

Ознакомьтесь с [примером Composition API](https://github.com/vuejs/vuex/tree/4.0/examples/composition) чтобы увидеть пример приложения, использующих Vuex и Vue Composition API.
64 changes: 64 additions & 0 deletions docs/ru/guide/forms.md
@@ -0,0 +1,64 @@
# Работа с формами

<div class="scrimba"><a href="https://scrimba.com/p/pnyzgAP/cqKRgEC9" target="_blank" rel="noopener noreferrer">Пройдите этот урок на Scrimba</a></div>

При использовании строгого режима Vuex может показаться неочевидным как использовать `v-model` с частью состояния Vuex:

``` html
<input v-model="obj.message">
```

Предположим, что `obj` — вычисляемое свойство, которое просто возвращает ссылку на объект из хранилища. В таком случае, `v-model` будет пытаться напрямую изменять значение `obj.message` при действиях пользователя. В строгом режиме такие изменения спровоцируют ошибку, поскольку они происходят вне обработчиков мутаций Vuex.

Для работы с Vuex в такой ситуации, следует привязать значение к `<input>` и отслеживать его изменения по событию `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)
}
}
}
```