Vue 3 представляет мощную систему реактивности, которая является фундаментом для создания современных веб-приложений. В этом руководстве мы глубоко погрузимся в механизмы отслеживания изменений, рассмотрим все аспекты работы watch и watchEffect, а также изучим продвинутые паттерны работы с реактивностью.
Основы реактивности в Vue 3
Vue 3 полностью переработал систему реактивности, используя JavaScript Proxy вместо Object.defineProperty. Это обеспечивает:
- Поддержку работы с массивами и коллекциями
- Более эффективное отслеживание изменений
- Возможность создания «сырых» (raw) объектов без реактивности
- Лучшую интеграцию с TypeScript
Как работает реактивность
import { reactive, effect } from 'vue'
const state = reactive({
count: 0
})
// Аналог watchEffect в системе реактивности
effect(() => {
console.log('Count:', state.count)
})
watch vs watchEffect: полное сравнение
watchEffect
- Автоматическое отслеживание зависимостей
- Немедленный запуск при создании
- Не предоставляет старые значения
- Идеален для побочных эффектов
const count = ref(0)
watchEffect(() => {
console.log(`Count changed: ${count.value}`)
})
watch
- Явное указание источников
- Ленивое выполнение (по умолчанию)
- Предоставляет старые и новые значения
- Подходит для сравнения состояний
watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`)
})
Продвинутые техники работы с реактивностью
1. Контроль зависимостей
const condition = ref(false)
const a = ref(1)
const b = ref(2)
watchEffect(() => {
// Только condition будет зависимостью
if (condition.value) {
console.log(a.value + b.value) // a и b не станут зависимостями
}
})
2. Глубокое наблюдение с кастомным сравнением
watch(
() => ({ ...complexObject }),
(newVal, oldVal) => {
// Логика сравнения
},
{
deep: true,
equals: (a, b) =>
a.id === b.id && a.items.length === b.items.length
}
)
3. Реактивные цепочки и оптимизация
const searchQuery = ref('')
const filters = reactive({
category: '',
sort: 'asc',
// 10K элементов
items: hugeArray
})
// Оптимизированный watch
watch(
() => ({
query: searchQuery.value,
category: filters.category,
sort: filters.sort
}),
({ query, category, sort }) => {
// Фильтрация без отслеживания hugeArray
filterItems(query, category, sort)
}
)
Практические примеры из реальных проектов
1. Интеграция с API
const pagination = reactive({
page: 1,
size: 20,
total: 0
})
const fetchData = async () => {
const res = await api.get('/items', {
params: {
page: pagination.page,
size: pagination.size
}
})
pagination.total = res.total
}
// Автоматический запрос при изменении пагинации
watch([() => pagination.page, () => pagination.size], fetchData, {
immediate: true
})
2. Управление состоянием формы
const form = reactive({
email: '',
password: '',
errors: {}
})
watch(
() => form.email,
(email) => {
form.errors.email = validateEmail(email)
? null
: 'Invalid email format'
},
{ debounce: 300 }
)
Производительность и отладка
1. Измерение времени выполнения
watchEffect((onCleanup) => {
const start = performance.now();
// Тяжелая операция
processLargeData();
const duration = performance.now() - start;
if (duration > 50) {
console.warn(`Slow effect: ${duration.toFixed(2)}ms`);
}
onCleanup(() => {
// Очистка ресурсов
})
})
2. Визуализация зависимостей
function trackDependencies(effect) {
const deps = new Set()
const reactiveEffect = watchEffect(() => {
effect()
console.log('Dependencies:', [...deps])
deps.clear()
}, {
onTrack(e) {
deps.add(e.target[e.key])
}
})
return reactiveEffect
}
Заключение и лучшие практики
- Используйте watch когда нужны старые значения или точный контроль
- Выбирайте watchEffect для автоматического отслеживания зависимостей
- Оптимизируйте глубокое наблюдение с помощью кастомных функций сравнения
- Разделяйте сложные эффекты на несколько простых watchers
- Всегда очищайте ресурсы в onCleanup
- Мониторьте производительность сложных эффектов
Эти техники помогут вам создавать высокопроизводительные приложения с четкой и предсказуемой реактивностью.