5.01. Справочник по Vue.js
Справочник по Vue.js
1. Основные понятия
Экземпляр приложения
Все приложения Vue начинаются с создания экземпляра приложения через createApp.
const app = Vue.createApp({
// корневой компонент
})
Метод createApp принимает объект конфигурации корневого компонента и возвращает экземпляр приложения.
Корневой компонент
Корневой компонент определяет начальное состояние и поведение приложения. Он содержит:
data— реактивные данныеmethods— методыcomputed— вычисляемые свойстваwatch— наблюдателиsetup()— точка входа для Composition APItemplateилиrender— шаблон или функция рендеринга
Монтирование
Приложение монтируется в DOM-элемент:
app.mount('#app')
Метод mount принимает селектор или DOM-элемент и заменяет его содержимое отрендеренным представлением компонента.
2. Реактивность
ref
Создаёт реактивную ссылку на примитивное значение.
import { ref } from 'vue'
const count = ref(0)
console.log(count.value) // 0
Значение доступно через .value.
reactive
Создаёт реактивный объект (глубокая реактивность).
import { reactive } from 'vue'
const state = reactive({ count: 0 })
Изменения любого вложенного свойства отслеживаются автоматически.
computed
Создаёт вычисляемое свойство, кэшируемое до изменения зависимостей.
import { computed } from 'vue'
const doubled = computed(() => count.value * 2)
Поддерживает геттер и сеттер:
const fullName = computed({
get() { return firstName.value + ' ' + lastName.value },
set(newValue) { /* разбор newValue */ }
})
watch
Наблюдает за изменениями реактивных данных.
import { watch } from 'vue'
watch(count, (newVal, oldVal) => {
console.log(`count изменился с ${oldVal} на ${newVal}`)
})
Типы наблюдаемых значений:
refreactive- геттер-функция
- массив источников
Опции watch:
immediate: true— запуск немедленноdeep: true— глубокое наблюдение за объектамиflush: 'pre' | 'post' | 'sync'— момент выполнения колбэка
watchEffect
Автоматически отслеживает зависимости и выполняет функцию при их изменении.
import { watchEffect } from 'vue'
watchEffect(() => {
console.log(count.value)
})
Не требует явного указания источника.
3. Компоненты
Определение компонента
Через объект:
const MyComponent = {
props: ['title'],
template: '<h1>{{ title }}</h1>'
}
Через defineComponent (рекомендуется для TypeScript):
import { defineComponent } from 'vue'
const MyComponent = defineComponent({ ... })
Регистрация компонентов
Глобальная:
app.component('MyComponent', MyComponent)
Локальная (внутри компонента):
export default {
components: { MyComponent }
}
Props
Передача данных от родителя к дочернему компоненту.
props: {
title: String,
age: { type: Number, required: true },
isActive: { type: Boolean, default: false }
}
Типы type: String, Number, Boolean, Array, Object, Date, Function, Symbol, null (для любого типа).
Опции:
required: truedefault: valueилиdefault() { return ... }validator(value) { return condition }
Emits
Объявление событий, которые может выбросить компонент.
emits: ['update:title', 'submit']
С проверкой типов:
emits: {
submit: (payload) => typeof payload === 'object'
}
Выброс события:
emit('submit', data)
Slots
Передача контента в компонент.
По умолчанию:
<my-component>Контент слота</my-component>
Именованные слоты:
<my-component>
<template #header>Заголовок</template>
<template #footer>Подвал</template>
</my-component>
Scoped slots (слоты с данными):
<my-component v-slot="{ user }">
{{ user.name }}
</my-component>
Внутри компонента:
<slot :user="currentUser"></slot>
provide / inject
Передача данных через дерево компонентов без пропсов.
Провайдер:
import { provide } from 'vue'
provide('theme', 'dark')
Инжектор:
import { inject } from 'vue'
const theme = inject('theme', 'light') // 'light' — значение по умолчанию
Работает с ref и reactive — реактивность сохраняется.
4. Директивы
Встроенные директивы
v-bind
Привязка атрибутов или свойств.
<img v-bind:src="imageSrc" />
<!-- сокращённо -->
<img :src="imageSrc" />
Модификаторы:
.prop— привязка как DOM-свойство.attr— привязка как атрибут.camel— преобразование kebab-case в camelCase
v-on
Обработка событий.
<button v-on:click="handleClick">Клик</button>
<!-- сокращённо -->
<button @click="handleClick">Клик</button>
Модификаторы:
.stop— остановка всплытия.prevent— предотвращение действия по умолчанию.capture— обработка на фазе перехвата.self— только если событие от самого элемента.once— однократное срабатывание.passive— пассивный обработчик- Клавиатурные:
.enter,.esc,.space,.up,.downи т.д. - Мышь:
.left,.right,.middle
v-if / v-else-if / v-else
Условный рендеринг.
<div v-if="isVisible">Видим</div>
<div v-else>Скрыт</div>
v-show
Переключение видимости через CSS display.
<div v-show="isVisible">Всегда в DOM</div>
v-for
Рендеринг списков.
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
С индексом:
<li v-for="(item, index) in items" :key="index">{{ index }}: {{ item }}</li>
Можно использовать с объектами:
<span v-for="(value, key, index) in object" :key="key">
{{ index }}. {{ key }}: {{ value }}
</span>
v-model
Двусторонняя привязка данных.
На <input>:
<input v-model="message" />
Эквивалентно:
<input :value="message" @input="message = $event.target.value" />
Модификаторы:
.lazy— синхронизация поchange, а неinput.number— автоматическое преобразование в число.trim— удаление пробелов по краям
На компонентах:
<custom-input v-model="searchText" />
Внутри компонента:
props: ['modelValue'],
emits: ['update:modelValue'],
template: `<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />`
v-text / v-html
Установка текстового или HTML-содержимого.
<span v-text="msg"></span>
<div v-html="rawHtml"></div>
⚠️ v-html не очищает содержимое — уязвимость XSS при использовании ненадёжных данных.
v-cloak
Скрывает неразобранные шаблоны до инициализации Vue.
<div v-cloak>{{ message }}</div>
Требует CSS:
[v-cloak] { display: none; }
5. Жизненный цикл компонента
Options API хуки
beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeUnmountunmounted
Composition API аналоги
onBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmountedonErrorCapturedonRenderTrackedonRenderTriggered
Пример:
import { onMounted } from 'vue'
onMounted(() => {
console.log('Компонент смонтирован')
})
6. Composition API
setup()
Основная функция компонента в Composition API.
Параметры:
props— реактивные пропсыcontext— объект сattrs,slots,emit,expose
Возврат:
- объект с переменными и методами, доступными в шаблоне
- функция рендеринга
setup(props, { emit }) {
const count = ref(0)
const increment = () => {
count.value++
emit('change', count.value)
}
return { count, increment }
}
expose()
Ограничивает публичный API компонента при обращении через $refs.
setup(props, { expose }) {
const publicMethod = () => { ... }
expose({ publicMethod })
}
7. Teleport
Рендеринг содержимого вне иерархии DOM компонента.
<teleport to="body">
<div>Модальное окно</div>
</teleport>
Полезно для модалок, тултипов, всплывающих окон.
8. Suspense
Обработка асинхронных зависимостей (например, async setup).
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>Загрузка...</div>
</template>
</Suspense>
Компонент с async setup:
async setup() {
const data = await fetchData()
return { data }
}
9. Конфигурация приложения
app.config
errorHandler
Глобальный обработчик ошибок.
app.config.errorHandler = (err, instance, info) => {
console.error('Ошибка:', err, info)
}
warnHandler
Обработчик предупреждений.
globalProperties
Добавление глобальных свойств (аналог $ в Vue 2).
app.config.globalProperties.$http = axios
optionMergeStrategies
Настройка стратегий слияния опций.
performance
Включение метрик производительности (только в dev-режиме).
10. Плагины
Подключение через app.use(plugin, options).
Плагин — функция или объект с методом install.
const MyPlugin = {
install(app, options) {
app.directive('focus', ...)
app.provide('config', options)
}
}
app.use(MyPlugin, { apiKey: '123' })
11. Типы данных и ограничения
Поддерживаемые типы реактивности
- Примитивы:
string,number,boolean,bigint,symbol - Объекты:
Object,Array,Map,Set,WeakMap,WeakSet - Встроенные:
Date,RegExp,Promise
Ограничения
- Свойства, добавленные после создания реактивного объекта, не являются реактивными (используй
Vue.setв Vue 2, в Vue 3 этого нет — всё реактивно благодаря Proxy) - Индексы массива нельзя отслеживать напрямую — используй
splice,push,popи другие мутации - Прямое присваивание по индексу работает в Vue 3
12. Маршрутизация (Vue Router)
Установка
npm install vue-router@4
Базовая настройка
import { createRouter, createWebHistory } from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
const router = createRouter({
history: createWebHistory(),
routes
})
app.use(router)
Динамические сегменты
{ path: '/user/:id', component: User }
Доступ через route.params.id.
Вложенные маршруты
{
path: '/user/:id',
component: User,
children: [
{ path: 'profile', component: UserProfile },
{ path: 'posts', component: UserPosts }
]
}
Требует <router-view /> внутри шаблона User.
Именованные маршруты
{ path: '/user/:id', name: 'user', component: User }
Использование:
<router-link :to="{ name: 'user', params: { id: 123 }}">Профиль</router-link>
Программная навигация
router.push('/about')
router.push({ name: 'user', params: { id: 1 } })
router.replace({ path: '/login' })
router.go(-1) // назад
Глобальные хуки
router.beforeEach(to, from, next)— защита маршрутовrouter.afterEach(to, from)— аналитика, скролл
Meta-поля
{ path: '/admin', component: Admin, meta: { requiresAuth: true } }
Проверка в beforeEach:
if (to.meta.requiresAuth && !isAuthenticated) next('/login')
Lazy-loading компонентов
{ path: '/about', component: () => import('./views/About.vue') }
13. Управление состоянием
Pinia (официальная библиотека для Vue 3)
Установка
npm install pinia
Создание хранилища
import { createPinia } from 'pinia'
app.use(createPinia())
Определение store
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
Использование в компоненте
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
store.increment()
Особенности Pinia:
- Поддержка TypeScript без boilerplate
- Отсутствие мутаций (только действия)
- Модульность без вложенности
- Поддержка SSR
Vuex (устаревший, но поддерживаемый)
Структура
state— данныеgetters— вычисляемые свойстваmutations— синхронные измененияactions— асинхронные операцииmodules— модульность
14. Инструменты сборки
Vite (рекомендуется для Vue 3)
Создание проекта
npm create vue@latest
Преимущества
- Мгновенный запуск dev-сервера
- Нативная поддержка ES-модулей
- Hot Module Replacement (HMR)
- Встроенная поддержка TypeScript, JSX, CSS препроцессоров
Vue CLI (устаревший)
Поддерживается, но не рекомендуется для новых проектов.
15. Однофайловые компоненты (.vue)
Структура:
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { ref } from 'vue'
const message = ref('Привет')
</script>
<style scoped>
div { color: red; }
</style>
Блоки
<template>— один корневой элемент<script>или<script setup>— логика<style>— стили, с атрибутамиscoped,module
Атрибуты script
setup— Composition API безreturnlang="ts"— TypeScriptname— имя компонента для DevTools
Атрибуты style
scoped— изоляция стилейmodule— CSS Moduleslang="scss"— препроцессоры
16. Server-Side Rendering (SSR)
Vue + Vite SSR
Официальный подход через create-vue с флагом SSR.
Nuxt.js
Фреймворк поверх Vue для SSR, генерации статики, мета-тегов.
17. DevTools
Vue DevTools
Браузерное расширение для:
- Инспекции компонентов
- Просмотра состояния (Pinia/Vuex)
- Отслеживания событий
- Профилирования производительности
Поддерживает Vue 3, Pinia, маршруты.
18. Оптимизации
Lazy-loading компонентов
const AsyncComponent = defineAsyncComponent(() =>
import('./components/HeavyComponent.vue')
)
keep-alive
Кэширование компонентов при переключении.
<keep-alive>
<component :is="currentView" />
</keep-alive>
Атрибуты:
include/exclude— список имёнmax— максимальное число кэшируемых экземпляров
v-memo (Vue 3.2+)
Мемоизация поддерева при рендере.
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
{{ item.name }}
</div>
Обновляет DOM только если изменяется условие в массиве.
Компиляция шаблонов
Vue компилирует шаблоны в функции рендеринга. В production-сборке отключается проверка типов и предупреждения.
19. Типизация (TypeScript)
defineComponent
Обеспечивает правильную типизацию опций.
import { defineComponent } from 'vue'
export default defineComponent({
props: { msg: String },
setup(props) {
// props.msg — string | undefined
}
})
defineProps / defineEmits (в <script setup>)
const props = defineProps<{
msg: string
}>()
const emit = defineEmits<{
(e: 'change', id: number): void
}>()
withDefaults
Значения по умолчанию для props в TS:
const props = withDefaults(defineProps<{
size?: 'small' | 'medium' | 'large'
}>(), {
size: 'medium'
})
20. Тестирование
Vitest + Vue Test Utils
Официальный стек для unit-тестов.
Пример:
import { mount } from '@vue/test-utils'
import MyComponent from './MyComponent.vue'
test('renders message', () => {
const wrapper = mount(MyComponent, {
props: { message: 'Hello' }
})
expect(wrapper.text()).toContain('Hello')
})
Cypress / Playwright
Для end-to-end тестирования.
21. Best Practices
- Используйте Composition API для сложной логики
- Разделяйте логику на composable-функции (
useXxx) - Избегайте глубокой вложенности компонентов
- Используйте
keyвv-forдля уникальной идентификации - Не мутируйте props напрямую
- Используйте
v-modelс модификаторами для контроля ввода - Минимизируйте использование глобальных переменных
- Разделяйте представление и бизнес-логику
22. Сравнение с другими фреймворками
| Характеристика | Vue | React | Angular |
|---|---|---|---|
| Парадигма | Декларативная | Декларативная | Декларативная |
| Реактивность | Автоматическая | Через setState / Hooks | Zone.js + ChangeDetection |
| Шаблоны | HTML-based | JSX | HTML + специальные синтаксисы |
| Кривая обучения | Низкая | Средняя | Высокая |
| Размер | ~30 КБ (gzip) | ~40 КБ (с React DOM) | >100 КБ |
| TypeScript | Полная поддержка | Родная поддержка | Встроен |
23. Примеры использования
Простой счётчик
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => count.value++
</script>
<template>
<button @click="increment">{{ count }}</button>
</template>
Форма с валидацией
<script setup>
import { ref } from 'vue'
const email = ref('')
const isValid = ref(true)
const validate = () => {
isValid.value = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)
}
</script>
<template>
<input v-model="email" @blur="validate" :class="{ error: !isValid }" />
<p v-if="!isValid">Неверный email</p>
</template>
Список с фильтрацией
<script setup>
import { ref, computed } from 'vue'
const items = ref(['Apple', 'Banana', 'Orange'])
const query = ref('')
const filtered = computed(() =>
items.value.filter(i => i.toLowerCase().includes(query.value.toLowerCase()))
)
</script>
<template>
<input v-model="query" placeholder="Поиск..." />
<ul>
<li v-for="item in filtered" :key="item">{{ item }}</li>
</ul>
</template>