Реактивные данные
Реактивность — самое фундаментальное понятие в Vue3.
В этой главе вы поймёте, что такое реактивность, и научитесь использоватьrefиreactiveдля управления данными, а также реализуете функциональность фильтрации по категориям.
Что такое реактивность?
Реактивность означает:Когда данные меняются, страница обновляется автоматически.。
Традиционный подход на JS выглядит так: после изменения переменной вам приходится вручную обновлять DOM.
Реактивная система Vue3 автоматически отслеживает изменения данных и уведомляет представление о необходимости обновления — вам нужно заботиться только о данных, а не о DOM.
Пример
import { ref } from 'vue'
// Переменные, обёрнутые в ref, реактивны
const count = ref(0)
// После изменения count, {{ count }} в шаблоне обновляется автоматически
function increment() {
count.value++ // Примечание: в JS для доступа нужен .value
}
</script>
<template>
<p>Количество кликов: {{ count }}</p>
<!-- Автоматически разворачивается в шаблоне, .value не нужен -->
<button @click="increment">+1</button>
</template>
После нажатия кнопки число, отображаемое на странице, автоматически меняется — это реактивность.
Вам не нужно писать ничего,document.querySelectorилиinnerHTMLчтобы обновить страницу.
ref — Обработка примитивных типов
ref— наиболее часто используемый реактивный API в Vue3, подходящий для обёртки данных примитивных типов.
Примитивные типы включают: строки, числа, булевы значения, null, undefined.
Синтаксис
Пример
import { ref } from 'vue'
// Создание ref
const name = ref('EXAMPLE') // Строка
const age = ref(18) // Число
const isVip = ref(false) // Логическое значение
// Чтение .value
console.log(name.value) // "EXAMPLE"
console.log(age.value) // 18
// Изменение .value
name.value = 'Hello EXAMPLE'
age.value++
isVip.value = !isVip.value
</script>
<template>
<p>Имя: {{ name }}</p>
<!-- В шаблонах ref автоматически разворачивается, .value не нужен -->
<p>Возраст: {{ age }}</p>
<p>VIP:{{ isVip }}</p>
</template>
Самое важное, что нужно запомнить:В JS-коде нужно .value, но не в шаблонах.Это самая распространённая ошибка новичков.
reactive — Работа с объектами и массивами
reactiveПодходит для оборачивания данных ссылочного типа, таких как объекты или массивы.
В отличие от ref, reactive не нуждается в.value.value, и вы можете напрямую обращаться к свойствам.
Пример
import { reactive } from 'vue'
// Оборачиваем объект с помощью reactive
const user = reactive({
name: 'Xiaoming',
age: 20,
hobbies: ['programming', 'running']
})
// Прямой доступ и изменение, .value не нужен
console.log(user.name) // "Сяо Мин"
user.name = 'Xiao Hua' // Реактивное обновление
user.hobbies.push('reading') // Операции с массивами тоже реактивны
</script>
<template>
<p>Имя: {{ user.name }}</p>
<p>Возраст: {{ user.age }}</p>
<ul>
<li v-for="(hobby, index) in user.hobbies" :key="index">
{{ hobby }}
</li>
</ul>
</template>
Два ограничения reactive
| Ограничение | Описание | Решение |
|---|---|---|
| Можно оборачивать только объектные типы | Нельзя передавать примитивные типы, такие как строки, числа | Используйте ref для примитивных типов |
| Нельзя заменить целиком | Нельзя присвоить весь объект новому объекту | Используйте Object.assign() или переключитесь на ref |
Пример
// Ошибка: нельзя заменить реактивный объект целиком
const state = reactive({ count: 0 })
// state = { count: 1 } // Это не сработает, реактивность будет потеряна
// Правильный подход 1: изменяйте свойства вместо замены целиком
state.count = 1
// Правильный подход 2: используйте ref, чтобы обернуть объект (ref позволяет заменить его целиком)
const stateRef = ref({ count: 0 })
stateRef.value = { count: 1 } // Это работает! Потому что .value у ref можно заменить
ref vs reactive: Когда что использовать
| Сценарий | Рекомендация | Пример |
|---|---|---|
| Одиночная строка/число/логическое значение | ref | const name = ref('小明') |
| Одиночный массив | ref | const list = ref([]) |
| Одиночный объект | Работают и ref, и reactive | const user = ref({}) |
| Несколько несвязанных состояний | Использовать ref для каждого | Один ref на строку |
| Группа тесно связанных состояний | reactive | например, данные формы, коллекция конфигурации |
Наиболее распространённая практика в реальной разработке:Всегда использовать ref, включая объекты и массивы。
Это обеспечивает единообразие стиля кода, и вам не нужно переключать свою мысленную модель между ref и reactive.
В следующих главах этого руководства повсеместно используется ref для управления всем состоянием. В местах, где требуется
.valuereactive, эти места будут специально отмечены.
computed — Вычисляемые свойства
computed— это "данные, вычисленные из существующих данных."
У него есть две ключевые особенности:Кэширование——когда зависимости не меняются, он не пересчитывается;Автоматическое отслеживание——когда зависимости меняются, он обновляется автоматически.
Пример
import { ref, computed } from 'vue'
const articles = ref([
Начало работы с Vue3
{ id: 2, title: 'Подробное объяснение Promise', category: 'JavaScript' },
Макет Grid
{ id: 4, title: 'Composition API', category: 'Vue' }
])
const activeCategory = ref('All')
// computed фильтрует articles на основе activeCategory
const filteredArticles = computed(() => {
if (activeCategory.value === 'All') {
return articles.value
}
return articles.value.filter(a => a.category === activeCategory.value)
})
// computed также можно использовать для статистики
const totalCount = computed(() => articles.value.length)
const filteredCount = computed(() => filteredArticles.value.length)
</script>
<template>
<p>Всего статей: {{ totalCount }}, сейчас показано: {{ filteredCount }}</p>
<!-- Кнопки категорий -->
<button @click=activeCategory = 'All'>Все</button>
<button @click="activeCategory = 'Vue'">Vue</button>
<button @click="activeCategory = 'JavaScript'">JavaScript</button>
<button @click="activeCategory = 'CSS'">CSS</button>
<!-- Список отфильтрованных статей -->
<div v-for="article in filteredArticles" :key="article.id">
<h3>{{ article.title }}</h3>
<span>{{ article.category }}</span>
</div>
</template>
Всякий раз, когдаactiveCategoryизменения,filteredArticlesон автоматически пересчитается, и страница автоматически обновится.
Примечание: computed возвращает ref, поэтому обращение к нему в JS также требует
.value.value. Однако в приведённом выше примере мы использовали его напрямую в шаблоне, где он автоматически разворачивается.
Практика: добавление фильтрации по категориям в блог
Примените computed на главной странице блога для фильтрации статей при нажатии кнопок категорий.
Пример
import { ref, computed } from 'vue'
const articles = ref([
{ id: 1, title: 'Vue3: полное руководство для начинающих', summary: 'изучение Vue3 с нуля', category: 'Vue', date: '2024-05-10' },
{ id: 2, title: 'JS: асинхронное программирование подробно', summary: Понимание Promise и async/await, category: 'JavaScript', date: '2024-05-08' },
{ id: 3, title: Практическая вёрстка с CSS Grid, summary: 'Реализация адаптивной вёрстки с помощью Grid', category: 'CSS', date: '2024-05-05' },
{ id: 4, title: 'Принципы реактивности Vue3', summary: 'Глубокое понимание ref и reactive', category: 'Vue', date: '2024-05-03' },
{ id: 5, title: 'Полное руководство по Flexbox', summary: 'Изучение flex-вёрстки в одной статье', category: 'CSS', date: '2024-05-01' },
])
// Все категории (без дубликатов)
const categories = computed(() => {
const cats = articles.value.map(a => a.category)
return ['All', ...new Set(cats)] // Добавить 'All' в начало
})
// Текущая выбранная категория
const activeCategory = ref('All')
// Фильтровать статьи по категории
const filteredArticles = computed(() => {
if (activeCategory.value === 'All') {
return articles.value
}
return articles.value.filter(a => a.category === activeCategory.value)
})
</script>
<template>
<div class="home">
<h2>Последние статьи</h2>
<!-- Кнопки фильтра категорий -->
<div class="category-bar">
<button
v-for="cat in categories"
:key="cat"
:class="{ active: activeCategory === cat }"
@click="activeCategory = cat"
>
{{ cat }}
</button>
</div>
<!-- Количество отфильтрованных результатов -->
<p class="result-info">Всего {{ filteredArticles.length }} статей</p>
<!-- Список статей -->
<div class="article-grid">
<div v-for="article in filteredArticles" :key="article.id" class="card">
<span class="tag">{{ article.category }}</span>
<h3>{{ article.title }}</h3>
<p>{{ article.summary }}</p>
<span class="date">{{ article.date }}</span>
</div>
</div>
</div>
</template>
<style scoped>
.category-bar {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.category-bar button {
padding: 6px 16px;
border: 1px solid #ddd;
border-radius: 20px;
background: #fff;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.category-bar button.active {
background: #427DB8;
color: #fff;
border-color: #427DB8;
}
.category-bar button:hover {
border-color: #427DB8;
}
.result-info {
color: #999;
font-size: 14px;
margin-bottom: 16px;
}
</style>
Теперь, когда вы нажимаете разные кнопки категорий, список статей фильтруется в реальном времени. Вам не нужно писать код для манипуляции DOM; достаточно сосредоточиться на логике изменения данных.
Краткое содержание главы
В этой главе вы освоили три основных API системы реактивности Vue 3: ref оборачивает примитивные типы (в JS используется .value), reactive оборачивает объекты, а computed создаёт вычисляемые данные.
С помощью computed + кнопок фильтрации по категориям вы познакомились с шаблоном разработки «представления, управляемые данными» — измените данные, и страница обновится автоматически.
Другие расширения