Олег Марков
Понимание core функционала Vue и его применение
Введение
Vue — это прогрессивный JavaScript-фреймворк, который стал очень популярным благодаря своей простоте и мощности. Если вы только начинаете погружаться в мир Vue или хотите лучше понять, что происходит "под капотом", эта статья даст вам подробный разбор core-функционала: реактивности, компонентов, директив, жизненного цикла и других ключевых аспектов. Здесь вы получите практические советы и узнаете, какие основные возможности предоставляет вам Vue на уровне ядра.
Понимание реактивности во Vue
Как Vue отслеживает и обновляет данные
Главная особенность Vue — его реактивная система. Это значит, что все переменные, которые вы объявляете во Vue, становятся "наблюдаемыми": если их значение изменится, интерфейс автоматически обновится.
Пример:
const { reactive } = Vue
const state = reactive({
message: 'Привет, мир!'
})
// Когда вы обновите message, Vue сразу отобразит это изменение в интерфете:
state.message = 'Изменено'
// Интерфейс отобразит новый текст
Почему это работает?
Vue использует прокси-объекты (Proxy) для того, чтобы "подслушивать" ваши обращения к данным и реагировать на любые изменения. Вам не нужно явно обновлять DOM — Vue сделает это за вас.
В отличие от других фреймворков
В отличие, например, от React, где нужно вызывать setState или использовать хуки, во Vue вы просто меняете свойство объекта, и всё остальное происходит автоматически.
Использование refs и реактивных объектов
В Composition API часто применяют ref для простых типов и reactive для объектов/массивов.
import { ref, reactive } from 'vue'
const count = ref(0) // Для примитивов
const user = reactive({ // Для объектов
name: 'Алексей',
age: 25
})
// Для count обращайтесь как count.value
count.value++
Это важно помнить: когда вы работаете с ref, данные хранятся внутри value, а reactive делает геттеры прямо на объекте. Смотрите разницу на примере выше.
Ограничения реактивности
Некоторые операции Vue отслеживает не полностью:
- Добавление новых свойств к уже реактивному объекту (в Vue 2) — такие свойства не будут реактивны.
- В Vue 3 эти ограничения практически устранены благодаря Proxy, однако, если вы используете массивы и мутируете индексы напрямую, будьте внимательны.
Компоненты: основы, создание и использование
Что такое компонент во Vue
Компоненты — строительные блоки приложений во Vue. Каждый компонент — это самостоятельная часть интерфейса с собственными данными, логикой и шаблоном. Благодаря компонентам вы делите приложение на маленькие управляемые части.
Синтаксис компонента
Давайте создадим простой компонент на Vue 3 с Composition API:
// MyButton.vue
<template>
<button @click="increment">{{ count }}</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
Здесь:
- В шаблоне кнопка отображает счетчик.
- На каждое нажатие кнопки счетчик увеличивается на 1.
Пропсы и события
Компоненты могут получать данные через пропсы и отправлять события обратно "родителю".
Пример передачи пропсов
// ParentComponent.vue
<template>
<ChildComponent :title="parentTitle" @clicked="onChildClick"/>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const parentTitle = ref('Заголовок родителя')
function onChildClick() {
console.log('Клик в дочернем компоненте')
}
</script>
// ChildComponent.vue
<template>
<button @click="$emit('clicked')">{{ title }}</button>
</template>
<script setup>
defineProps(['title'])
</script>
Смотрите, как работает связь:
- Родитель передаёт строку через prop
title
. - Дочерний компонент выводит prop и "сообщает" родителю о клике, генерируя событие.
Slot'ы для расширения компонента
Слоты позволяют компонента сделать универсальным и расширяемым. Вместо забитого содержимого, вы "вставляете" нужный HTML снаружи.
// BaseCard.vue
<template>
<div class="card">
<slot></slot>
</div>
</template>
// Использование
<BaseCard>
<h3>Заголовок карточки</h3>
<p>Текст внутри карточки</p>
</BaseCard>
Директивы в Vue: расширяя синтаксис шаблонов
Директивы — это специальные инструкции, которые вы добавляете к элементам в шаблоне. Они начинаются с v-.
Наиболее используемые директивы
v-bind
Привязывает динамические значения к атрибутам или свойствам DOM:
<img v-bind:src="imgUrl" alt="Картинка">
// Короткая запись:
<img :src="imgUrl" />
Комментарий:
Если значение imgUrl изменится, ссылка на картинку в интерфейсе тоже изменится автоматически.
v-model
Двусторонняя привязка между значением input и переменной:
<input v-model="username" placeholder="Имя пользователя" />
// username будет автоматически обновляться при вводе, и наоборот
v-if, v-else-if, v-else
Используется для условного отображения (рендеринга) элементов:
<p v-if="isAdmin">Вы администратор</p>
<p v-else>Вы не администратор</p>
v-for
Для цикличного вывода массива данных:
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
// users — ваш массив юзеров
Создание пользовательских директив
Иногда стандартных директив может не хватать. Vue позволяет создавать свои:
app.directive('focus', {
mounted(el) {
el.focus()
}
})
// Использование:
<input v-focus />
Комментарий:
Когда элемент "вмонтируется" в DOM, на него автоматически наведётся фокус.
Жизненный цикл компонента
Вам часто нужно выполнять действия тогда, когда компонент только появился или был обновлен, или, наоборот, уничтожен. Для этого у компонентов есть lifecycle hooks.
Основные хуки жизненного цикла:
onMounted
— вызовется после монтирования компонентаonUpdated
— срабатывает при изменении реактивных данных и перерисовкеonUnmounted
— когда компонент удаляется из DOM
Пример использования хуков
<script setup>
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
console.log('Компонент смонтирован')
})
onUnmounted(() => {
console.log('Компонент удален из DOM')
})
</script>
Когда использовать хуки в реальных задачах
- onMounted — чтобы “запустить” подписку на внешние данные, подписаться на ивенты или сделать API-запрос.
- onUnmounted — когда нужно удалить слушателей событий или “почистить за собой” (например, остановить таймеры).
Роутинг и управление состоянием
Эти возможности часто находятся вне core, но кратко затронем основы.
Vue Router
Позволяет делать многостраничные приложения (SPA):
import { createRouter, createWebHistory } from 'vue-router'
import HomePage from './views/HomePage.vue'
import AboutPage from './views/AboutPage.vue'
const routes = [
{ path: '/', component: HomePage },
{ path: '/about', component: AboutPage }
]
const router = createRouter({
history: createWebHistory(),
routes
})
// router передается в createApp на старте приложения
Vuex и Pinia — управление глобальным состоянием
Когда компоненты становятся крупнее, появляется необходимость управлять состоянием в одном месте.
Pinia — современная альтернатива Vuex и официальный state-менеджер для Vue 3.
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: 'Андрей',
isLoggedIn: false
}),
actions: {
login(name) {
this.name = name
this.isLoggedIn = true
}
}
})
// В компоненте:
const userStore = useUserStore()
userStore.login('Сергей')
Взаимодействие с асинхронными данными и API
Обычно приложения обращаются к серверу, подгружают данные. Vue не ограничивает вас — используйте любой способ: fetch, axios, что угодно.
Пример загрузки данных в компоненте
<script setup>
import { ref, onMounted } from 'vue'
const posts = ref([])
onMounted(async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts')
posts.value = await res.json()
})
</script>
<template>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>
Комментарий:
- Данные загружаются при монтировании компонента, список постов становится доступен и автоматически отображается.
Реактивность и производительность: оптимизация
Vue оптимизирует обновление DOM через Virtual DOM. Но есть вещи, о которых нужно помнить:
- Ключи в v-for — всегда задавайте уникальные ключи, чтобы Vue быстро понимал, какие элементы изменились.
- watch и computed — используйте, чтобы реагировать на изменения значений или производить вычисления на основе других реактивных переменных.
Пример использования watch
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log(`Счетчик изменился с ${oldValue} на ${newValue}`)
})
computed-переменные
Для данных, которые можно вычислить из других данных, используйте computed:
import { ref, computed } from 'vue'
const firstName = ref('Иван')
const lastName = ref('Петров')
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
// fullName будет всегда в актуальном состоянии
Инструменты и особенности разработки во Vue
Devtools
Установите Vue Devtools — это расширение для браузера, упрощающее отладку, показ реактивных данных и компонентов "на лету".
SFC — Single File Component
Vue поддерживает компонентный подход, где каждый компонент обычно оформляется в отдельном файле с расширением .vue
. Он содержит <template>
, <script>
и <style>
. Такой подход улучшает читаемость кода и масштабируемость проекта.
Динамические компоненты и ленивый импорт
Если у вас много компонентов, не обязательно загружать их все сразу.
<script setup>
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/BigComponent.vue')
)
</script>
<template>
<Suspense>
<template #default>
<AsyncComp />
</template>
<template #fallback>
<p>Загрузка...</p>
</template>
</Suspense>
</template>
Комментарий: Данный подход позволяет уменьшить объем начальной загрузки, подгружая тяжелый компонент только по мере необходимости.
Заключение
Система реактивности, компонентная архитектура, мощные директивы и удобные инструменты жизненного цикла — вот что делает Vue очень удобным и производительным инструментом для создания современных интерфейсов. Вы видели простоту декларативного синтаксиса, а также гибкость расширения, работы с асинхронными данными и управления состоянием. Понимание core-функционала позволит вам строить масштабируемые и поддерживаемые приложения, используя весь потенциал Vue без лишней сложности.
Частозадаваемые технические вопросы по теме статьи и ответы на них
Как правильно "отслеживать" изменения в реактивных объектах внутри массивов?
Если у вас массив объектов и вы хотите отследить изменение в каком-либо объекте, используйте watch
с опцией deep:
watch(users, (newUsers) => {
// Реакция на любые изменения внутри массива или его объектов
}, { deep: true })
Как эффективно передавать функции из родителя в дочерний компонент?
Передавайте функции как пропсы, как обычные данные. В дочернем компоненте их можно вызывать напрямую, или используйте события (emit
) для оповещения родителя о действиях.
// В родителе
<MyChild :onSave="handleSave" />
// В дочернем
props: ['onSave']
// Вызов: props.onSave()
Как правильно обращаться к DOM-элементу внутри компонента Vue 3?
С помощью template ref:
<input ref="inputEl" />
import { ref, onMounted } from 'vue'
const inputEl = ref(null)
onMounted(() => {
inputEl.value.focus()
})
Почему не срабатывают хуки жизненного цикла в некоторых случаях?
Это возможно, если компонент не был смонтирован (например, из-за условия v-if) или используется неправильный синтаксис (например, попытка прописать хуки вне <script setup>
в компонентах с Composition API).
Как обработать асинхронную ошибку в onMounted?
Используйте try/catch внутри асинхронной функции в хуке:
onMounted(async () => {
try {
await loadData()
} catch (err) {
console.error('Ошибка при загрузке:', err)
}
})