# Основы реактивности
В разделе в примерах кода используется синтаксис однофайловых компонентов
# Объявление реактивного состояния
Реактивное состояние из объекта JavaScript создаётся с помощью метода reactive
:
import { reactive } from 'vue'
// реактивное состояние
const state = reactive({
count: 0
})
2
3
4
5
6
Метод reactive
— эквивалент Vue.observable()
API во Vue 2.x, переименованный чтобы избежать путаницы с observables из RxJS. Возвращаемое состояние здесь будет являться реактивным объектом. Реактивное преобразование «глубокое» — оно будет влиять на все вложенные свойства переданного объекта.
Основной сценарий использования реактивного состояния во Vue — использование во время отрисовки. Благодаря отслеживанию зависимостей, представление будет обновляться автоматически при изменениях реактивного состояния.
Это сама суть системы реактивности Vue. Когда в компоненте возвращается объект из data()
, то под капотом он уже становится реактивным с помощью reactive()
. Шаблон будет скомпилирован в render-функцию, которая использует эти реактивные свойства.
Подробнее о методе reactive
можно узнать в разделе Основы API реактивности.
# Создание автономных ссылок на реактивные значения
Представьте случай, когда есть отдельное примитивное значение (например, строка) и необходимо сделать её реактивной. Конечно, можно сделать объект с одним свойством, значением которого будет эта строка, а затем передать его в reactive
. Но для этого у Vue уже есть метод, который сделает то же самое — ref
:
import { ref } from 'vue'
const count = ref(0)
2
3
ref
вернёт реактивный объект, который можно изменять и который служит реактивной ссылкой (ref, от слова reference) для внутреннего значения, которое он хранит — откуда и происходит его имя. Этот объект содержит только одно свойство с именем value
:
import { ref } from 'vue'
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
2
3
4
5
6
7
# Разворачивание ref-ссылок
Когда ref-ссылка возвращается в качестве свойства для контекста отрисовки (объект, возвращаемый из setup()) и доступ к свойству осуществляется в шаблоне, то ссылка будет автоматически разворачиваться во внутреннее значение. Указывать .value
в шаблоне требуется только для вложенных ref-ссылок:
<template>
<div>
<span>{{ count }}</span>
<button @click="count ++">Увеличить счётчик</button>
<button @click="nested.count.value ++">Увеличить вложенный счётчик</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return {
count,
nested: {
count
}
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Совет
Если обращаться к реальному экземпляру объекта не потребуется, то можно обернуть его в метод reactive
:
nested: reactive({
count
})
2
3
# Доступ в реактивных объектах
При доступе к ref
или мутировании как свойства реактивного объекта, автоматически она будет разворачиваться во внутреннее значение и вести себя как обычное свойство:
const count = ref(0)
const state = reactive({
count
})
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
2
3
4
5
6
7
8
9
Если новая ссылка присваивается к свойству, связанному с существующей ссылкой, то она просто заменит собой старую ссылку:
const otherCount = ref(2)
state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1
2
3
4
5
Разворачивание ссылки происходит лишь в том случае, если она вложена в реактивный Object
. Никакого разворачивания выполняться не будет, когда ссылка предоставляет доступ к Array
или нативной коллекции, например Map
(opens new window):
const books = reactive([ref('Руководство по Vue 3')])
// потребуется указывать .value
console.log(books[0].value)
const map = reactive(new Map([['count', ref(0)]]))
// потребуется указывать .value
console.log(map.get('count').value)
2
3
4
5
6
7
# Деструктурирование реактивного состояния
При необходимости использовать лишь несколько свойств из большого реактивного объекта, может возникнуть соблазн воспользоваться деструктурированием из ES6 (opens new window) для получения нужных свойств:
import { reactive } from 'vue'
const book = reactive({
author: 'Команда Vue',
year: '2022',
title: 'Руководство Vue 3',
description: 'Вы его читаете прямо сейчас ;)',
price: 'free'
})
let { author, title } = book
2
3
4
5
6
7
8
9
10
11
К сожалению, при таком деструктурировании реактивность обоих свойств будет потеряна. Для таких случаев необходимо сначала преобразовать реактивный объект в набор ссылок. Эти ссылки сохранят реактивную связь с исходным объектом:
import { reactive, toRefs } from 'vue'
const book = reactive({
author: 'Команда Vue',
year: '2022',
title: 'Руководство Vue 3',
description: 'Вы его читаете прямо сейчас ;)',
price: 'free'
})
let { author, title } = toRefs(book)
title.value = 'Детальное руководство по Vue 3' // нужно .value потому что title теперь ссылка
console.log(book.title) // 'Детальное руководство по Vue 3'
2
3
4
5
6
7
8
9
10
11
12
13
14
Подробнее о refs
можно узнать в разделе API реактивных ссылок.
# Предотвращение изменений реактивных объектов с помощью readonly
Иногда необходимо отслеживать изменения реактивного объекта (ref
или reactive
), но при этом запретить его изменения из определённого места приложения. Например, когда используем реактивный объект, внедряемый через provide, и хотим предотвратить попытки изменений там, где он будет внедряться. Для таких случаев можно создать прокси «только для чтения» исходного объекта:
import { reactive, readonly } from 'vue'
const original = reactive({ count: 0 })
const copy = readonly(original)
// изменение оригинала будет вызывать наблюдатели, которые отслеживают копию
original.count++
// изменение копии не пройдёт и будет отображено предупреждение
copy.count++ // warning: "Set operation on key 'count' failed: target is readonly."
2
3
4
5
6
7
8
9
10
11