Vue 3 для новичка: реактивность и первый компонент
За полчаса вы соберёте счётчик и список задач на Vue 3, поймёте, почему ref нужно разворачивать через .value, и научитесь передавать данные между компонентами.

К концу этой инструкции у вас будет рабочий проект на Vue 3: счётчик, который меняет число по клику, и список задач с добавлением элементов. По пути вы поймёте, чем ref отличается от reactive и почему число в шаблоне обновляется само, а в JavaScript-коде к нему приходится обращаться через .value. Займёт около 30–40 минут, если Node.js уже стоит.
Пишем на Vue 3 с Composition API и синтаксисом <script setup> — это рекомендованный способ по состоянию на версию 3.4.x. Со старым Options API (data() { return {...} }) код будет выглядеть иначе, здесь его не разбираем.
Что нужно до начала
Проверьте, что на месте, прежде чем создавать проект. Иначе споткнётесь на первой же команде.
- Node.js 18 или новее. Проверка: в терминале выполните
node -v— должна вывестись версия вродеv20.11.0. Если команда не найдена, поставьте Node с официального сайта. - npm. Ставится вместе с Node. Проверка:
npm -v. - Любой редактор кода. Для Vue удобен VS Code с расширением Vue (Official) — оно подсвечивает синтаксис в
.vue-файлах.
Шаг 1. Создаём проект
Vue создаётся официальным инструментом create-vue на базе Vite. Выполните в папке, где хотите держать проект:
npm create vue@latestИнструмент задаст несколько вопросов. Для первого проекта отвечайте так: имя проекта — my-first-vue, на все дополнительные вопросы (TypeScript, JSX, Router, Pinia, тесты) отвечайте No. Лишние опции сейчас только запутают.
Дальше зайдите в папку и установите зависимости:
cd my-first-vue
npm install
npm run devПроверка: в терминале появится строка вроде Local: http://localhost:5173/. Откройте этот адрес в браузере — увидите стартовую страницу Vue с логотипом. Терминал не закрывайте: там работает dev-сервер, который пересобирает страницу при каждом сохранении файла.
Шаг 2. Первый реактивный счётчик
Откройте файл src/App.vue, удалите всё его содержимое и вставьте это:
<script setup>
import { ref } from 'vue'
// ref создаёт реактивную ссылку на значение
const count = ref(0)
function increment() {
// в JS-коде к значению обращаемся через .value
count.value++
}
</script>
<template>
<p>Нажали раз: {{ count }}</p>
<button @click="increment">Нажать</button>
</template>Сохраните файл. Проверка: страница в браузере обновится сама, появятся текст и кнопка. По клику число растёт.
Почему в шаблоне без .value, а в коде с ним
Это первое, обо что спотыкаются все. ref(0) возвращает не число, а объект-обёртку с полем .value. В JavaScript-коде вы работаете с этим объектом, поэтому пишете count.value++. А в шаблоне Vue сам разворачивает ref верхнего уровня — там достаточно {{ count }}. Забыли .value в функции — счётчик не будет меняться, и ошибки при этом не выскочит.
Правило, которое сэкономит вам час отладки: в<script>— всегда через.value, в<template>— никогда. Если число не обновляется, первым делом ищите потерянный.value.
Шаг 3. ref или reactive
Кроме ref есть reactive — он делает реактивным целый объект. Разница на практике:
| Что нужно | Инструмент | Как обращаться в коде |
|---|---|---|
| Одно значение: число, строка, флаг | ref | count.value |
| Массив или объект целиком | ref или reactive | list.value или state.items |
| Хочется единообразия и не путаться | ref везде | всегда через .value |
Совет новичку: используйте ref для всего. Так вы не будете держать в голове, где нужен .value, а где нет. reactive удобен, но у него есть тонкость — если переприсвоить весь объект (state = {...}), реактивность теряется. С ref такой проблемы нет.
Шаг 4. Выносим в отдельный компонент
Приложение из одного файла — это не Vue-приложение, а заготовка. Сделаем отдельный компонент списка задач. Создайте файл src/components/TodoList.vue:
<script setup>
import { ref } from 'vue'
const tasks = ref([]) // массив задач
const newTask = ref('') // текст в поле ввода
function addTask() {
const text = newTask.value.trim()
if (!text) return // пустое не добавляем
tasks.value.push(text)
newTask.value = '' // очищаем поле
}
</script>
<template>
<div>
<input v-model="newTask" @keyup.enter="addTask" placeholder="Что сделать" />
<button @click="addTask">Добавить</button>
<ul>
<li v-for="(task, index) in tasks" :key="index">{{ task }}</li>
</ul>
</div>
</template>Теперь подключите компонент в App.vue — просто импортируйте и поставьте в шаблон:
<script setup>
import TodoList from './components/TodoList.vue'
</script>
<template>
<h1>Мои задачи</h1>
<TodoList />
</template>Проверка: на странице появятся поле ввода, кнопка и заголовок. Введите текст, нажмите Enter или кнопку — задача добавится в список. Здесь работают три вещи сразу: v-model связывает поле ввода с newTask в обе стороны, v-for рисует по элементу списка на каждую задачу, а :key помогает Vue отслеживать элементы при изменениях.
Шаг 5. Передаём данные в компонент через props
Компоненты общаются: родитель передаёт данные ребёнку через props. Сделаем компонент одной задачи src/components/TaskItem.vue:
<script setup>
// объявляем, какие props принимает компонент
defineProps({
text: String
})
</script>
<template>
<li>{{ text }}</li>
</template>И используем его вместо простого <li> в TodoList.vue:
<li is="TaskItem" v-for="(task, index) in tasks" :key="index" />Точнее — замените блок <ul> на такой (и добавьте импорт TaskItem в <script setup>):
<ul>
<TaskItem
v-for="(task, index) in tasks"
:key="index"
:text="task"
/>
</ul>Двоеточие перед :text означает «привязать значение из JavaScript». Без двоеточия text="task" передало бы буквальную строку «task», а не содержимое переменной. Проверка: список работает как раньше, но теперь каждая строка — отдельный компонент.
Если не получилось
command not found: npm— Node.js не установлен или не добавлен в PATH. Переустановите Node с официального сайта и перезапустите терминал.- Счётчик не меняется по клику — в функции забыли
.value. Должно бытьcount.value++, а неcount++. [Vue warn]: Component is missing template or render function— в.vue-файле нет блока<template>или он пустой.- Список пустой, добавление не работает — проверьте, что в кнопке стоит
@click="addTask"без скобок после имени функции. Со скобкамиaddTask()функция вызовется сразу при отрисовке, а не по клику. - Правка в файле не отражается на странице — упал dev-сервер. Посмотрите в терминал: если там ошибка, исправьте её; если сервер остановлен, запустите
npm run devзаново.
Что делать дальше
У вас есть счётчик, список и передача данных через props — база, на которой строится любое Vue-приложение. Следующие шаги в логичном порядке:
- Обратная связь от ребёнка к родителю — событиями через
defineEmits. Например, чтобы кнопка удаления внутриTaskItemсообщала родителю, какую задачу убрать. - Вычисляемые значения —
computed, чтобы, скажем, показывать счётчик выполненных задач без ручного пересчёта. - Маршрутизация — Vue Router, когда появится больше одной страницы.
Всё это описано в официальном руководстве Vue — оно на русском и построено как раз от ref к более сложным темам.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: Vue.js — официальное руководство (русская версия), create-vue — репозиторий инструмента создания проекта
Частые вопросы
У меня Vue 2, код из статьи не работает — почему?
Обязательно ли писать .value каждый раз, это неудобно
Чем create-vue отличается от Vue CLI, что выбрать?
Сервер запустился, но браузер показывает пустую белую страницу
Можно ли не устанавливать проект, а подключить Vue одним тегом script?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.