Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Основы Vue 3
vue_basics

Основы Vue 3

Философия Vue 3, реактивность, шаблонный синтаксис, директивы, v-model, вычисляемые свойства и жизненный цикл компонента

Основы Vue 3

Vue 3 — прогрессивный JavaScript-фреймворк для построения пользовательских интерфейсов. Его философия: минимальное ядро с мощной системой реактивности, компонентной архитектурой и декларативным шаблонным синтаксисом.

#Что такое Vue 3 и его философия

Vue 3 — это реактивный UI-фреймворк, построенный вокруг двух ключевых идей: реактивности и компонентности. Реактивность означает, что когда данные меняются, интерфейс автоматически обновляется — вам не нужно вручную манипулировать DOM. Компонентность означает, что интерфейс строится из изолированных переиспользуемых блоков — компонентов.

Vue 3 следует принципу прогрессивности: вы можете добавить его в существующую страницу через тег <script> или построить полноценное SPA с помощью Vite. Фреймворк не навязывает архитектуру — начните с малого, а когда нужно, масштабируйтесь.

Три главных принципа Vue:

  • Декларативность — описываешь, как должен выглядеть UI при данных X, а Vue сам разбирается как это отрисовать
  • Реактивность — изменение данных автоматически обновляет DOM через виртуальный DOM и систему отслеживания зависимостей
  • Компонентность — каждый компонент инкапсулирует шаблон, логику и стили в одном файле .vue

#Способы подключения: CDN vs Vite-проект

Для быстрого прототипирования или добавления Vue в существующий HTML достаточно подключить его через CDN.

Самый простой способ попробовать Vue — подключить его прямо в HTML-файл через CDN:

<!DOCTYPE html> <html> <head> <title>Мой первый Vue</title> </head> <body> <div id="app">{{ message }}</div> <script type="module"> import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js' createApp({ setup() { const message = ref('Привет, Vue!') return { message } } }).mount('#app') </script> </body> </html>

Этот подход подходит для обучения, но для реальных проектов нужен полноценный инструментарий. Для создания полноценного Vue-проекта используйте официальный скаффолдинг через npm:

npm create vue@latest my-project cd my-project npm install npm run dev

Команда npm create vue@latest запустит интерактивный мастер, где можно выбрать TypeScript, Vue Router, Pinia, ESLint и другие опции. Под капотом используется Vite — молниеносный сборщик на базе ES-модулей.

После установки структура проекта выглядит так:

my-project/
├── src/
│   ├── assets/          # Статические файлы (CSS, изображения)
│   ├── components/      # Переиспользуемые компоненты
│   ├── App.vue          # Корневой компонент
│   └── main.js          # Точка входа
├── public/              # Файлы без обработки
├── index.html           # HTML-шаблон
├── vite.config.js       # Конфигурация Vite
└── package.json

#Single File Components (SFC)

Каждый компонент Vue хранится в файле .vue. Это так называемый Single File Component — концепция, при которой шаблон, скрипт и стили живут вместе.

Файл .vue состоит из трёх блоков:

<template> <!-- HTML-разметка компонента --> <div class="counter"> <p>Счётчик: {{ count }}</p> <button @click="increment">+1</button> </div> </template> <script> // Логика компонента export default { data() { return { count: 0 } }, methods: { increment() { this.count++ } } } </script> <style scoped> /* Стили — scoped означает что они применяются только к этому компоненту */ .counter { padding: 16px; } </style>

Директива scoped в <style> важна: она добавляет уникальный атрибут ко всем элементам компонента и гарантирует, что стили не вытекут в другие компоненты.

#Options API vs Composition API

В Vue 3 есть два способа писать логику компонента. Options API — классический стиль, где логика разделена по «опциям» объекта. Composition API — новый стиль Vue 3, где логика организована по функциям.

Тот же компонент-счётчик на Options API выглядит так:

<script> export default { data() { return { count: 0 } }, computed: { doubled() { return this.count * 2 } }, methods: { increment() { this.count++ } }, mounted() { console.log('Компонент создан, count =', this.count) } } </script>

А на Composition API с <script setup> тот же результат выглядит компактнее и логика группируется по смыслу, а не по типу опции:

<script setup> import { ref, computed, onMounted } from 'vue' const count = ref(0) const doubled = computed(() => count.value * 2) function increment() { count.value++ } onMounted(() => { console.log('Компонент создан, count =', count.value) }) </script>

Options API остаётся полностью поддерживаемым и хорош для небольших компонентов. Composition API — предпочтительный выбор для сложной логики и максимальной переиспользуемости кода. В следующей теме мы подробно разберём Composition API.

#Шаблонный синтаксис

Шаблоны Vue — это расширенный HTML. Vue компилирует шаблоны в оптимизированный JavaScript-код, который обновляет DOM только там, где нужно.

#Интерполяция: двойные фигурные скобки

Самый базовый способ вывести данные — интерполяция через {{ }}. Внутри скобок можно использовать любое JavaScript-выражение:

<template> <div> <p>{{ message }}</p> <p>{{ 1 + 1 }}</p> <p>{{ ok ? 'Да' : 'Нет' }}</p> <p>{{ message.split('').reverse().join('') }}</p> </div> </template>

Важно: {{ }} всегда выводит текст, HTML-теги будут экранированы. Это защита от XSS-атак.

#Атрибуты: v-bind и его сокращение :

Для привязки данных к HTML-атрибутам используется директива v-bind. Она связывает атрибут с реактивным значением:

<template> <!-- Полный синтаксис --> <img v-bind:src="imageUrl" v-bind:alt="imageAlt"> <!-- Сокращение через двоеточие (рекомендуется) --> <img :src="imageUrl" :alt="imageAlt"> <!-- Привязка динамического имени атрибута --> <button :[dynamicAttr]="value">Кнопка</button> <!-- Привязка объекта атрибутов целиком --> <div v-bind="{ id: 'hero', class: 'active' }">Секция</div> </template> <script setup> const imageUrl = 'https://example.com/logo.png' const imageAlt = 'Логотип' const dynamicAttr = 'disabled' const value = true </script>

#Обработка событий: v-on и его сокращение @

Для обработки событий DOM используется v-on. Этот синтаксис позволяет вызывать методы или писать инлайн-выражения:

<template> <!-- Полный синтаксис --> <button v-on:click="handleClick">Нажми</button> <!-- Сокращение @ (рекомендуется) --> <button @click="handleClick">Нажми</button> <!-- Инлайн-выражение --> <button @click="count++">+1</button> <!-- С модификаторами --> <form @submit.prevent="onSubmit"> <input @keydown.enter="onEnter"> </form> </template>

Модификаторы событий очень удобны: .prevent вызывает event.preventDefault(), .stop — event.stopPropagation(), .once — обработчик сработает только раз.

#Директивы

Директивы — это специальные атрибуты с префиксом v-, которые добавляют реактивное поведение DOM-элементам.

#Условный рендеринг: v-if, v-else-if, v-else

Vue рендерит или скрывает элементы в зависимости от условия. При v-if элемент полностью создаётся или уничтожается в DOM:

<template> <div> <p v-if="userType === 'admin'">Панель администратора</p> <p v-else-if="userType === 'moderator'">Панель модератора</p> <p v-else>Обычный пользователь</p> </div> </template> <script setup> const userType = 'admin' </script>

#v-show: скрытие через CSS

v-show в отличие от v-if не удаляет элемент из DOM, а только устанавливает display: none. Это выгоднее когда элемент часто переключается:

<template> <!-- Элемент присутствует в DOM, но скрыт если isVisible = false --> <div v-show="isVisible">Этот блок может скрываться</div> </template>

Правило выбора: используйте v-if если условие редко меняется (дорогой первоначальный рендер оправдан), v-show если состояние часто переключается (например, дропдаун-меню).

#Рендеринг списков: v-for

Директива v-for позволяет рендерить список элементов на основе массива или объекта. Атрибут :key обязателен — он помогает Vue отслеживать изменения списка:

<template> <ul> <!-- Перебор массива --> <li v-for="item in items" :key="item.id"> {{ item.name }} — {{ item.price }} руб. </li> </ul> <!-- С индексом --> <ol> <li v-for="(item, index) in items" :key="item.id"> {{ index + 1 }}. {{ item.name }} </li> </ol> <!-- Перебор объекта --> <div v-for="(value, key) in userProfile" :key="key"> {{ key }}: {{ value }} </div> </template> <script setup> const items = [ { id: 1, name: 'Яблоко', price: 50 }, { id: 2, name: 'Банан', price: 30 }, { id: 3, name: 'Апельсин', price: 70 } ] const userProfile = { name: 'Иван', email: 'ivan@example.com', role: 'developer' } </script>

Почему :key обязателен? Без ключей Vue не понимает, какой элемент соответствует какому DOM-узлу при перестановке. Это приводит к багам — неправильные DOM-узлы обновляются, теряется состояние полей ввода. Всегда используйте уникальный идентификатор в качестве ключа (не индекс массива — при сортировке это сломается).

#Двусторонняя привязка: v-model

v-model — синтаксический сахар для одновременной привязки значения и обработки события ввода. Используется с <input>, <textarea>, <select>:

<template> <div> <input v-model="name" placeholder="Ваше имя"> <p>Привет, {{ name }}!</p> <!-- Числовая привязка — автоматически преобразует в число --> <input v-model.number="age" type="number"> <!-- .trim удаляет пробелы по краям --> <input v-model.trim="email" type="email"> <!-- .lazy обновляет только при blur (потере фокуса) --> <input v-model.lazy="description" type="text"> <!-- Чекбокс --> <input v-model="isAgree" type="checkbox"> <!-- Выпадающий список --> <select v-model="selectedCity"> <option value="">Выберите город</option> <option value="moscow">Москва</option> <option value="spb">Санкт-Петербург</option> </select> </div> </template> <script setup> import { ref } from 'vue' const name = ref('') const age = ref(0) const email = ref('') const description = ref('') const isAgree = ref(false) const selectedCity = ref('') </script>

Под капотом v-model="name" — это v-bind:value="name" @input="name = $event.target.value". Vue разворачивает это в привязку и обработчик события.

#Вычисляемые свойства: computed

Computed-свойства позволяют вычислять значения на основе реактивных данных. В отличие от методов, computed кэшируется — значение пересчитывается только когда зависимые данные изменились:

<template> <div> <p>Полное имя: {{ fullName }}</p> <p>Отфильтрованных товаров: {{ filteredProducts.length }}</p> </div> </template> <script> export default { data() { return { firstName: 'Иван', lastName: 'Петров', products: [ { name: 'Ноутбук', inStock: true }, { name: 'Мышь', inStock: false }, { name: 'Клавиатура', inStock: true } ] } }, computed: { // Геттер — только для чтения fullName() { return `${this.firstName} ${this.lastName}` }, // Вычисление на основе массива filteredProducts() { return this.products.filter(p => p.inStock) } } } </script>

Computed с геттером и сеттером используется когда нужно двусторонняя привязка:

<script> export default { data() { return { firstName: 'Иван', lastName: 'Петров' } }, computed: { fullName: { get() { return `${this.firstName} ${this.lastName}` }, set(value) { const parts = value.split(' ') this.firstName = parts[0] this.lastName = parts[1] || '' } } } } </script>

#Методы: methods

Методы — функции компонента, доступные из шаблона. Они не кэшируются и вызываются заново каждый раз. Используйте методы для обработки событий и выполнения действий:

<template> <div> <button @click="addToCart(product)">В корзину</button> <button @click="removeFromCart(product.id)">Удалить</button> <p>Итого: {{ formatPrice(totalPrice) }}</p> </div> </template> <script> export default { data() { return { cart: [], product: { id: 1, name: 'Книга', price: 500 } } }, computed: { totalPrice() { return this.cart.reduce((sum, item) => sum + item.price, 0) } }, methods: { addToCart(item) { this.cart.push({ ...item }) }, removeFromCart(id) { this.cart = this.cart.filter(item => item.id !== id) }, formatPrice(price) { return `${price.toLocaleString('ru-RU')} руб.` } } } </script>

Вопрос «метод или computed?» решается просто: если нужен результат вычисления без аргументов — computed (кэшируется), если нужна функция с аргументами или действие (изменение данных, запрос к API) — метод.

#Жизненный цикл компонента

Каждый компонент Vue проходит через предсказуемые этапы создания, обновления и уничтожения. Хуки жизненного цикла позволяют запускать код на определённых этапах:

<script> export default { data() { return { posts: [] } }, // Компонент создан (данные инициализированы, но DOM ещё нет) created() { console.log('created: DOM недоступен, но данные уже есть') // Хорошее место для запросов к API this.loadPosts() }, // Компонент смонтирован в DOM mounted() { console.log('mounted: DOM доступен') // Хорошее место для работы с DOM, подключения сторонних библиотек this.$el.querySelector('input')?.focus() }, // Данные обновились, DOM обновляется updated() { console.log('updated: компонент перерисован') }, // Компонент удалён из DOM unmounted() { console.log('unmounted: очищаем ресурсы') // Отписываемся от событий, таймеров, WebSocket clearInterval(this.timer) }, methods: { async loadPosts() { const response = await fetch('/api/posts') this.posts = await response.json() } } } </script>

Также существуют хуки beforeCreate, beforeMount, beforeUpdate, beforeUnmount — они вызываются до соответствующего этапа. На практике чаще всего используются mounted (загрузка данных, DOM-манипуляции) и unmounted (очистка ресурсов).

#Практический пример: компонент карточки товара

Давайте объединим всё изученное в реальный компонент. Это карточка товара с добавлением в корзину:

<template> <div class="product-card" :class="{ 'out-of-stock': !product.inStock }"> <img :src="product.image" :alt="product.name"> <div class="product-info"> <h3>{{ product.name }}</h3> <p class="price">{{ formattedPrice }}</p> <span v-if="product.inStock" class="badge badge-success">В наличии</span> <span v-else class="badge badge-error">Нет в наличии</span> <div class="quantity-control" v-show="product.inStock"> <button @click="quantity--" :disabled="quantity <= 1">-</button> <span>{{ quantity }}</span> <button @click="quantity++">+</button> </div> <button @click="addToCart" :disabled="!product.inStock" class="btn-primary" > {{ product.inStock ? 'В корзину' : 'Недоступно' }} </button> </div> </div> </template> <script> export default { props: { product: { type: Object, required: true } }, emits: ['add-to-cart'], data() { return { quantity: 1 } }, computed: { formattedPrice() { return `${(this.product.price * this.quantity).toLocaleString('ru-RU')} руб.` } }, methods: { addToCart() { this.$emit('add-to-cart', { product: this.product, quantity: this.quantity }) this.quantity = 1 } } } </script>

В этом компоненте мы использовали: привязку классов (:class), атрибутов (:src, :disabled), обработчики событий (@click), условный рендеринг (v-if/v-else, v-show), computed и props с emits (их подробно разберём в теме про компоненты).

Далее: Composition API