Эволюция Vue.js: от Options API к Composition API

freeCodeCamp.org 88,6 тыс. 6 ч 56 мин 48 мин 22.05.2025
Главное

Composition API во Vue.js — это не просто смена синтаксиса, а радикальное переосмысление того, как мы управляем реактивностью и состоянием в современных веб-приложениях. Оставляя позади ограничения Options API, разработчики получают инструмент для создания компактных, модульных и легко тестируемых компонентов, которые масштабируются от простых прототипов до полноценных промышленных решений.

🚀 Введение во Vue.js: Эволюция фреймворка и стили API 0:00

Понятие прогрессивного фреймворка и его преимущества перед конкурентами 2:14

Разработчик и инструктор Буган Пател начинает курс с фундаментального вопроса: что такое Vue.js? Это один из самых популярных JavaScript-фреймворков для создания современных веб-приложений. Созданный Эваном Ю и выпущенный в 2014 году, Vue.js официально определяется как прогрессивный фреймворк. Термин «прогрессивный» означает, что технологию можно легко масштабировать вверх или вниз в зависимости от текущих потребностей проекта. Его можно использовать точечно — например, для улучшения отдельной части крупного существующего приложения (подобно старой доброй jQuery) — либо развернуть полноценную одностраничную экосистему (SPA) с маршрутизацией и управлением состоянием.

Пател выделяет несколько ключевых преимуществ Vue.js, обеспечивающих ему идеальный баланс между мощностью и простотой:

При сравнении с главными конкурентами Vue.js выгодно выделяется своей архитектурой. Если React опирается на синтаксис JSX, а Angular требует сложной связки HTML и TypeScript, то Vue использует интуитивно понятные шаблоны на основе чистого HTML. Фреймворк имеет меньший размер бандла и предлагает максимальную гибкость. По сути, Vue заимствует лучшее из обоих миров: гибкость React и мощные возможности Angular, предлагая при этом гораздо более мягкую кривую обучения. Вскользь отметим, что ранее в разговоре автор кратко очертил дорожную карту курса, упомянув грядущее изучение базовой реактивности, структуры Vite, пропсов, слотов, Vue Router и стейт-менеджмента Pinia, который в Vue 3 окончательно заменил устаревший Vuex.

Архитектурные подходы: Options API против Composition API 7:52

Обращаясь к официальной документации vuejs.org, Пател подчеркивает, что в современной версии Vue 3 существуют два основных стиля написания кода: Options API и Composition API. На функциональном уровне между ними нет никакой разницы — это два альтернативных способа организации логики с небольшими синтаксическими различиями. Выбор конкретного подхода часто зависит от требований команды и проекта.

Традиционный Options API считается более простым для освоения новичками. Однако Composition API, ставший стандартом индустрии, генерирует более эффективные и компактные бандлы при сборке приложения. Кроме того, он концептуально ближе к Angular и другим enterprise-решениям, поскольку позволяет структурировать код сложным, но максимально переиспользуемым образом. Именно Composition API выбран в качестве основного инструмента для данного курса, так как он обеспечивает наибольшую гибкость при создании масштабируемых веб-компонентов. Для работы автор рекомендует среду VS Code, но предостерегает новичков от использования AI-ассистентов вроде GitHub Copilot, чтобы освоить базу «с нуля». Требуемая версия Node.js для развертывания локальной среды — 18.3 или выше.

Первая декларативная сборка через CDN и анатомия метода setup 13:01

Чтобы понять глубинную механику Vue.js до перехода к сложным сборкам через npm, Пател предлагает начать с простейшего файла index.html и подключения фреймворка через CDN-ссылку.

Внутри инициализирующего метода createApp объявляется специальная функция setup(), которую Пател метафорически называет главным методом всего приложения. Всё, что объявляется внутри него, должно быть явно возвращено через конструкцию return, чтобы стать доступным в HTML-шаблоне. Забыть добавить переменную в return — одна из самых частых ошибок начинающих разработчиков.

Связывание данных с интерфейсом происходит с помощью синтаксиса двойных фигурных скобок {{ }}. Этот синтаксис имеет особое значение для Vue: он автоматически заменяет плейсхолдеры реальными значениями переменных, включая строки, массивы или сложные объекты. Более того, внутри фигурных скобок можно свободно комбинировать реактивные свойства с классическими выражениями JavaScript. Например, использовать тернарный оператор для динамических вычислений или вызывать стандартные методы вроде Math.random() для генерации случайных значений прямо на лету. Завершающим этапом инициализации является вызов метода .mount(), который жестко привязывает экземпляр приложения к конкретному корневому элементу DOM, традиционно имеющему идентификатор #app.

⚡ Основы реактивности: ref и reactive 30:17

В основе современного Vue 3 лежит концепция реактивности, позволяющая автоматически обновлять DOM при изменении данных в приложении. Разработчики используют два основных инструмента для создания реактивных состояний: ref для простых типов данных и reactive для объектов.

Использование ref для примитивных типов 31:26

Когда мы работаем с базовыми типами, такими как строки, числа или булевы значения, стандартные переменные (даже объявленные через let) не обеспечивают обновление пользовательского интерфейса при их изменении. Для решения этой задачи используется функция ref.

При использовании ref стоит учитывать ключевые особенности:

Если вы попытаетесь обновить ref без обращения к .value, изменения не отразятся в интерфейсе, что является частой ошибкой новичков. При использовании ref для объектов, Vue также обеспечивает реактивность, но доступ к свойствам внутри кода всё равно потребует обращения через .value.

Работа с комплексными объектами через reactive 36:44

Для управления сложными структурами данных (объектами с множеством свойств) Vue предлагает функцию reactive. В отличие от ref, при работе с reactive отпадает необходимость использовать .value для изменения свойств.

Выбор между ref и reactive 38:45

Выбор между этими двумя инструментами сводится к типу данных, с которым вы работаете:

Стоит отметить, что ref под капотом вызывает reactive при передаче объекта. Однако из-за специфики работы с примитивами и необходимости унификации доступа через .value, ref стал более универсальным инструментом, тогда как reactive является предпочтительным выбором для работы с объектами данных, так как он обеспечивает более «бесшовное» присваивание.

Ранее в разговоре упоминалось использование функций внутри setup, а также затрагивались темы директив условного рендеринга, к которым мы вернемся в соответствующих главах.

🛠 Директивы рендеринга и управление DOM 50:31

В процессе разработки на Vue.js управление отображением элементов является фундаментальной задачей. Фреймворк предоставляет мощные инструменты для контроля того, как и когда компоненты появляются в DOM, что напрямую влияет на производительность и пользовательский опыт.

Условный рендеринг: v-if против v-show 50:31

Выбор между директивами v-if и v-show — один из первых важных архитектурных вопросов при построении интерфейса. Несмотря на схожий визуальный результат, механизмы их работы кардинально различаются.

Для сложных сценариев Vue также предлагает привычные конструкции v-else и v-else-if, которые позволяют чисто и читаемо организовать логику переключения между множеством состояний, включая сложные вложенные условия с логическими операторами.

Итерация коллекций и важность атрибута key 55:14

Для отрисовки списков данных во Vue используется директива v-for. Она позволяет итерироваться по массивам, объектам или даже диапазонам чисел. Внутри цикла разработчик получает доступ как к самому элементу коллекции, так и к его индексу.

При работе с массивами объектов v-for легко извлекает данные для отображения, однако здесь кроется важный нюанс производительности и стабильности рендеринга. При динамическом изменении порядка элементов в списке (например, при сортировке или фильтрации) Vue может некорректно сопоставлять состояние DOM-узлов (например, заполненные поля ввода) с данными.

Чтобы избежать этой проблемы, крайне рекомендуется использовать атрибут key.

Хотя для простых статических списков использование ключа не всегда строго обязательно, во всех серьезных проектах это считается «золотым стандартом» и лучшей практикой разработки.

🎨 Динамическое управление стилями и обработка событий во Vue.js 1:15:40

Динамическое связывание CSS-классов и встроенных стилей 1:15:40

Vue.js предоставляет разработчикам мощные и интуитивно понятные инструменты для гибкого управления внешним видом интерфейса. Передавая логическое значение, можно динамически контролировать применение CSS-классов к элементам шаблона. Например, если классу присвоено значение true, текст мгновенно увеличивается и окрашивается в зеленый цвет, но стоит изменить флаг на false, как оформление возвращается к исходному состоянию. При работе со сложными именами классов, содержащими дефисы (например, bg-gray), синтаксис требует их обязательного оборачивания в одинарные кавычки во избежание ошибок компиляции. Иногда при динамической смене стилей может возникать визуальное наложение элементов из-за особенностей верстки, что легко исправляется корректировкой размеров. Примечательно, что Vue успешно позволяет комбинировать стандартные статические классы с динамическими привязками на одном и том же теге.

Внутри функции setup можно объявлять целые методы для переключения стилей. Функции вроде isMainTitle или hasGreenBackground возвращают логические маркеры, которые напрямую определяют поведение атрибутов элементов. Вместо разрозненных переменных разработчик может передать полноценный объект, содержащий конфигурацию стилей. Помимо классов, аналогичным образом настраиваются инлайн-стили, где названия свойств пишутся в формате camelCase. Команда фреймворка предусмотрела и лаконичный синтаксический сахар: из-за частого использования директивы v-bind ее можно сокращать до обычного двоеточия (:), что делает итоговый код чистым и читаемым.

Обработка пользовательских событий с помощью директивы v-on 1:26:21

Важнейшим аспектом создания интерактивных веб-приложений является обработка пользовательских действий, таких как клики по кнопкам. В экосистеме Vue за перехват этих сигналов отвечает директива v-on. Для реализации базового сценария достаточно добавить кнопку в основной контейнер шаблона. Обычная функция вроде buttonClick, выводящая модальное окно alert, связывается с интерфейсом с помощью конструкции v-on:click.

Фреймворк позволяет передавать в вызываемые методы произвольные параметры, будь то строковые сообщения или комплексные данные. Наряду с кастомными аргументами, Vue автоматически транслирует в метод нативный объект события через зарезервированную переменную $event. Этот объект инкапсулирует полезные метаданные браузера, включая тип события и координаты курсора по осям X и Y в момент клика. Используя эти координаты, разработчик может на лету обновлять переменные (ранее в разговоре они касались базовой реактивности через функции ref) и выводить точное положение указателя на экран в реальном времени.

Модификаторы событий и перехват ввода с клавиатуры 1:31:36

Для тонкой кастомизации поведения событий Vue использует модификаторы директив — специальные постфиксы, отделяемые точкой. Они предотвращают стандартное поведение DOM и избавляют от необходимости писать громоздкий шаблонный код вручную.

Наиболее востребованными в повседневной практике являются следующие модификаторы:

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

Аналогичный подход применим к клавиатурному вводу с помощью события keyup. Модификатор .enter изолирует выполнение кода исключительно под кнопку Enter. Разработчики могут создавать комбинации вроде .enter.ctrl, требующие одновременного зажатия клавиш Ctrl и Enter, а также перехватывать клики конкретных кнопок мыши.

🛠️ Управление состоянием и автоматизация реактивности 1:40:26

В процессе разработки на Vue создание интерактивных интерфейсов неразрывно связано с работой с формами и оптимизацией того, как приложение реагирует на изменения данных. Фундаментальным инструментом здесь является директива v-model.

Двустороннее связывание данных через v-model 1:41:05

Директива v-model создает двустороннюю привязку данных между элементом формы и переменной в состоянии компонента. Это означает, что любое изменение в поле ввода автоматически обновляет значение переменной в скрипте, и наоборот — обновление значения программным путем тут же отражается в UI. Использование v-model освобождает разработчика от необходимости вручную прописывать обработчики событий для каждого поля ввода.

Для тонкой настройки поведения полей ввода Vue предлагает полезные модификаторы:

Оптимизация с помощью вычисляемых свойств (computed) 1:48:05

Часто бывает необходимо отобразить данные, которые зависят от состояния нескольких переменных. Например, объединение имени и фамилии в «полное имя». Использование обычных функций для таких целей может привести к проблемам с производительностью. При каждом перерендеринге компонента (вызванном, например, изменением счетчика) простая функция будет вызываться заново. Если внутри нее выполняются тяжелые вычисления или API-запросы, это негативно скажется на плавности приложения.

Вычисляемые свойства computed решают эту проблему через механизм кэширования.

Наблюдатели: реакция на изменения (watch) 1:52:44

В то время как вычисляемые свойства идеальны для трансформации данных, наблюдатели watch предназначены для выполнения «побочных эффектов» при изменении конкретной переменной. Это может быть отправка сетевого запроса, работа с DOM или вывод оповещений при достижении переменной определенного значения.

Ключевые особенности работы с watch:

Ранее в разговоре обсуждались основы связывания стилей и обработки событий. Понимание того, как работают computed и watch, позволяет перейти к созданию более сложных структур, например, списков данных с возможностью их динамического пополнения.

🎬 Управление состоянием и разработка форм во Vue.js 2:05:46

В текущем разделе мы переходим к практической реализации управления данными: от работы с массивами в компонентах до создания сложных форм, поддерживающих различные типы полей и их валидацию.

Реализация функционала списков: Push и Pop 2:06:12

Основой интерактивности является работа с массивами данных. Для реализации функционала «добавить» и «удалить» фильм мы используем классические JavaScript-методы push() и pop(). Метод push() добавляет новый объект в конец массива, а pop() — удаляет последний добавленный элемент.

Важным аспектом является валидация: например, ограничение списка пятью элементами. При попытке добавить шестой фильм, срабатывает условие, блокирующее работу кнопки добавления. Это обеспечивает контролируемое состояние интерфейса, где данные не превышают заданные лимиты.

Вычисляемые свойства и Наблюдатели 2:10:32

Ранее в разговоре они касались основ реактивности и работы с атрибутами. Теперь мы углубляемся в способы оптимизации логики. Замена функций на вычисляемые свойства (Computed Properties) позволяет Vue автоматически кэшировать результаты вычислений. Использование вычисляемого свойства вместо обычного вызова функции при рендеринге текста избавляет от необходимости использовать круглые скобки и повышает производительность.

Наблюдатели (Watchers) дополняют эту картину, позволяя реагировать на изменение конкретных данных, например, отслеживать длину массива movies. В отличие от обычных функций, наблюдатели могут выполнять асинхронные операции. Например, с помощью setTimeout можно реализовать отложенное отображение предупреждающего сообщения о достижении лимита элементов, что делает поведение приложения более гибким. Также была затронута краткая нотация событий: вместо v-on:click можно использовать @click, что значительно сокращает запись кода.

Работа с формами и привязка данных 2:18:07

Создание форм — ключевой навык для любого веб-приложения. Мы используем reactive объект для хранения данных формы (email, name, phone, subscribe, gender), что позволяет централизованно управлять состоянием всех полей. Привязка данных осуществляется через v-model, который обеспечивает синхронизацию между DOM-элементами и объектом в setup.

Важный аспект — предотвращение стандартного поведения формы при отправке, которое приводит к перезагрузке страницы. Модификатор .prevent для директивы @submit позволяет перехватить событие и выполнить собственную логику, например, логирование данных через console.log.

Модификаторы модели и выбор элементов управления 2:25:05

Vue предоставляет полезные модификаторы, которые упрощают обработку ввода:

Интерфейс формы легко адаптируется под разные требования. Например, выбор пола можно реализовать как через группу радио-кнопок, так и через выпадающий список select. Оба подхода эффективно работают с одной и той же моделью данных, что позволяет мгновенно менять UI без изменения основной логики управления состоянием.

📦 Переход к промышленной разработке: Инициализация проекта через Vite и структура SFC 2:37:18

Отказ от монолитного файла и создание проекта через npm 2:37:18

Ранее в обучении все базовые концепции, включая основы реактивности и валидацию форм, реализовывались внутри единственного файла index.html с подключением Vue через обычный скрипт. Однако реальные production-приложения никогда не создаются в таком "скученном" формате. Для построения масштабируемой архитектуры необходимо использовать современный инструментарий и экосистему однофайловых компонентов (SFC).

Процесс создания профессионального приложения начинается с перехода на официальный сайт vue.js.org во вкладку установки. Основным методом развертывания является использование пакетного менеджера npm. Для генерации проекта в терминале VS Code запускается встроенная утилита через команду:

npm create vue@latest

Команда подтягивает актуальную версию шаблонизатора. В процессе интерактивной настройки утилита запрашивает имя проекта — в данном случае выбрано view-components-demo. Далее пользователю предлагается богатый выбор дополнительных инструментов: TypeScript, Vue Router, Pinia, Vitest и другие. Автор намеренно отказывается от выбора всех этих опций (оставляя везде "No"), чтобы детально продемонстрировать подключение каждого модуля "с нуля" (from ground zero), помогая понять глубинные связи между файлами.

После завершения инициализации необходимо последовательно выполнить три стандартные команды: перейти в созданную директорию, установить зависимости и запустить локальный сервер разработки:

cd view-components-demo
npm install
npm run dev

Даже при пустом шаблоне package.json содержит минимально необходимые пакеты, которые скачиваются за несколько секунд во время выполнения npm install.

Сборщик Vite и интеграция Vue DevTools 2:42:17

Команда npm run dev активирует современный инструмент сборки под названием Vite. Автор подчеркивает нюанс произношения: несмотря на написание, слово произносится как «вит» (французское слово, означающее «быстрый»). Vite отвечает за компиляцию и преобразование специфического Vue-кода в чистый JavaScript, понятный любому браузеру. Сборщик запускает локальный сервер и предоставляет конкретный URL-адрес для предварительного просмотра. Терминал с работающим процессом Vite необходимо оставлять открытым: система автоматически отслеживает любые изменения в коде и мгновенно обновляет интерфейс в браузере.

Критически важным аспектом разработки является отладка приложения. При сборке проекта на базе Vite автоматически интегрируются инструменты Vue DevTools. Они запускаются по отдельному URL-адресу или встраиваются прямо в панель разработчика браузера Chrome, не перекрывая основной интерфейс. Vue DevTools предоставляют колоссальные возможности для инспекции:

Утилита оснащена функцией выбора элементов (pick icon): при наведении и клике на любой элемент веб-страницы DevTools автоматически определяет, в каком файле и каком компоненте этот элемент рендерится, позволяя мгновенно перейти к его редактированию в IDE.

Анатомия проекта: Разбор структуры папок и очистка бойлерплейта 2:47:07

Сгенерированный проект содержит четко структурированное дерево каталогов. Исключая служебные папки вроде .vscode и каталог установленных пакетов node_modules, ключевыми элементами являются:

В корне проекта также лежат конфигурационные файлы: .gitignore, jsconfig.json и настройки самого сборщика vite.config.js. Важнейший файл — package.json, где зафиксирована зависимость от Vue. Благодаря этому больше нет необходимости вручную прописывать теги <script> с CDN-ссылками в HTML-документе. Входной точкой всего приложения является index.html. Внутри него находится лишь один корневой элемент <div id="app"></div> и подключение скрипта-модуля main.js, расположенного в папке src.

Чтобы детально изучить механику Vue, автор проводит полную очистку стандартного шаблона (boilerplate). Из папки src/components удаляются демонстрационные компоненты (HelloWorld.vue, TheWelcome.vue и др.), полностью удаляются файлы из assets, а также стирается файл корневого компонента App.vue. В файле main.js вычищается весь код создания приложения. После такой тотальной зачистки страница в браузере становится абсолютно пустой, а в консоли разработчика появляется предупреждение о том, что у компонента отсутствует шаблон или функция рендеринга.

Сборка приложения с нуля: Настройка main.js и создание App.vue 2:53:11

После очистки начинается воссоздание приложения с чистого листа. Первым шагом в пустом файле main.js настраивается импорт функции createApp напрямую из пакета vue с использованием ES6-модулей:

import { createApp } from 'vue';

Далее вызывается createApp(), внутрь которой необходимо передать главный родительский компонент, а затем привязать (примонтировать) его к DOM-дереву через метод .mount('#app'). По соглашению, этот главный компонент называют App.

Для этого в каталоге src/ создается новый файл App.vue. Расширение .vue указывает на то, что это однофайловый компонент (Single File Component). Чтобы использовать его в main.js, его необходимо импортировать, указав относительный путь:

import App from './App.vue';

Автор отмечает, что имя при импорте может быть произвольным (например, AppTemp), но использование каноничного App делает код понятным и осмысленным. На данном этапе созданный файл App.vue остается совершенно пустым. Попытка просто написать внутри него HTML-тег, например <div>, без правильной внутренней структуры SFC приводит к ошибке в браузере, так как пустой файл не может корректно обработаться компилятором Vite.

📦 Магия компонентов: от глобальной регистрации к синтаксису <script setup> 2:55:56

Разделение интерфейса на независимые элементы 2:59:59

При проектировании современных веб-приложений ключевым архитектурным принципом становится декомпозиция интерфейса. Во Vue.js этот подход реализуется через создание дерева дочерних элементов вокруг корневого компонента App.vue. Это позволяет изолировать логику, делать код повторно используемым и гибко управлять отображением пользовательского интерфейса.

Для упорядочивания структуры проекта в директории src создается выделенная папка components, где хранятся все кастомные однофайловые компоненты с расширением .vue. Первым практическим примером такой структуры выступает компонент ContactUs.vue, содержащий базовую разметку с заголовком <h1>Contact Us</h1> и статической электронной почтой. Чтобы интегрировать новый элемент в приложение, разработчику доступны два пути: глобальная или локальная регистрация.

При глобальном подходе импорт и конфигурация осуществляются в файле main.js. Сначала объявляется константа приложения через вызов функции createApp, а затем, до этапа монтирования на DOM-элемент, вызывается метод app.component. Данный метод принимает два параметра: строковое имя будущего HTML-тега и сам импортированный объект компонента. По общепринятым правилам именования здесь используется стиль kebab-case (например, contact-us) или PascalCase.

Главное преимущество компонентной модели заключается в ее реактивности и масштабируемости. Добавление тега <contact-us /> в шаблон App.vue несколько раз подряд мгновенно создает на странице соответствующие изолированные блоки. Наглядным примером служит интерактивный счетчик ButtonCounter.vue, инкапсулирующий логику кликов на базе реактивных ссылок. Каждый созданный экземпляр такой кнопки полностью автономен: клик по одному счетчику не изменяет состояние соседнего, так как данные хранятся строго внутри локального контекста конкретного элемента.

Эволюция синтаксиса: переход к <script setup> 3:08:07

Классический подход к организации Composition API внутри компонентов требует написания значительного объема декларативного кода. Разработчику приходится оборачивать всю логику в конструкцию export default, объявлять функцию setup() и вручную возвращать объект через return для связи переменных с шаблоном. При создании множества мелких компонентов это приводит к избыточному коду.

Для решения этой проблемы во Vue.js был внедрен специальный синтаксический сахар — атрибут setup для тега <script>. Данное нововведение коренным образом меняет процесс написания кода:

При переходе на этот синтаксис критически важно следить за корректностью импорта встроенных утилит фреймворка. Например, попытка инициализировать состояние счетчика без явного импорта реактивной ссылки из пакета vue мгновенно генерирует системную ошибку выполнения «Ref is not defined» в консоли браузера. Использование атрибута setup делает кодовую базу лаконичной, чистой и значительно более приятной в долгосрочной поддержке.

Локальная регистрация как стандарт оптимизации производительности 3:09:56

Несмотря на кажущееся удобство глобальной регистрации, когда объявленный компонент можно использовать в любой части приложения без импортов, этот подход таит в себе серьезную архитектурную ловушку. Глобальные компоненты компилируются в единый монолитный пакет. Это означает, что при первом посещении сайта пользователь будет вынужден скачивать весь тяжелый бандл целиком, даже если эти компоненты находятся на скрытых страницах и не нужны прямо сейчас.

Именно поэтому стандартом индустрии является локальная регистрация компонентов. При таком подходе файлы импортируются непосредственно внутри того компонента, где они вызываются (например, внутри App.vue), с помощью стандартных ES-импортов. Если компонент не задействован на главной странице, Vue не станет загружать его ресурсы до момента фактического требования. Процесс миграции на локальные рельсы тривиален: достаточно вырезать строки регистрации из main.js и перенести конструкции import в целевой блок <script setup>.

Для демонстрации стилизации интерфейса в рамках видеокурса в проект через корневой файл index.html интегрируется фреймворк Bootstrap посредством CDN-ссылок на стили и JavaScript-скрипты. Это позволяет оперативно обернуть структуру приложения в класс container и задать адаптивные отступы. В финальной части данного этапа авторы проводят рефакторинг, переименовывая файл в лаконичный Contact.vue, и плавно переходят к изучению механизмов передачи данных сверху вниз через входные параметры Props с валидацией типов данных, детальный разбор которых вынесен в рамки следующей главы.

🔌 Компонентное взаимодействие: Строгая валидация Props и магия Emits 3:21:05

Строгая валидация Props и управление входящими данными 3:21:05

При проектировании архитектуры на фреймворке Vue.js критически важно правильно организовать передачу данных сверху вниз — от родительского компонента к дочернему. По умолчанию любые атрибуты, передаваемые дочернему элементу напрямую, интерпретируются системой как обычные строки. Если разработчику необходимо передать числовой тип или булево значение, обязательным условием становится использование директивы v-bind или её сокращения в виде двоеточия :. Это заставляет Vue вычислять содержимое как JavaScript-выражение, благодаря чему дочерний компонент получает корректный тип данных, а потенциальные предупреждения в консоли исчезают.

Для повышения отказоустойчивости приложения используется механизм строгой валидации входящих параметров через встроенный макрос defineProps. Вместо простого перечисления ожидаемых свойств в виде массива строк, Vue позволяет передавать конфигурационный объект. Внутри него для каждого свойства, например для имени контакта (name), можно задать не только тип данных (type: String), но и флаг обязательности required: true. Если родительский компонент попытается вызвать дочерний без передачи этого параметра, Vue незамедлительно выбросит предупреждение в консоль разработчика. Для необязательных полей, таких как email, хорошей практикой является определение значения по умолчанию с помощью свойства default (например, указав строку "not applicable").

В процессе разработки важно избегать конфликтов имён. Если внутри дочернего компонента объявить локальную переменную, имя которой полностью совпадает с именем входящего пропса (например, email), то при рендеринге локальная переменная получит безусловный приоритет. Так, при тестировании с тестовым адресом broken@test.com свойство просто не отобразится, пока локальный дубликат не будет закомментирован. Приятной особенностью современных версий фреймворка является то, что макрос defineProps доступен в контексте <script setup> глобально — его больше не требуется импортировать из пакета vue вручную.

Разрабатывая демонстрационный интерфейс со списком контактов (куда для проверки добавлялись случайные имена, такие как Ben и Jessica), автор столкнулся с особенностями реактивного связывания. Ранее в разговоре они касались основ реактивности ref и reactive, а также рендеринга списков через v-for. При передаче реактивной переменной ownerName (которая является ref-объектом) важно передавать её целиком, а не через разыменование .value. Только в этом случае Vue сможет отслеживать изменения переменной в инпуте родителя и автоматически проталкивать обновлённые данные вниз в дочерние компоненты.

Ограничение Props и архитектура событий снизу вверх 3:33:33

Следующим этапом построения интерфейса стала реализация интерактивных кнопок для добавления контактов в «избранное» и удаления из него. На уровне стилизации (с использованием сетки Bootstrap, где под кнопку выделялись колонки column 3) текст и классы кнопок меняются динамически в зависимости от булева значения isFavorite. Здесь важно соблюдать синтаксические правила Vue: поскольку для динамического связывания классов уже используются внешние двойные кавычки, любые строковые литералы внутри выражения должны оборачиваться исключительно в одинарные кавычки. Использование двойных кавычек внутри двойных неизбежно приведёт к синтаксической ошибке компиляции.

Однако при попытке реализовать функцию переключения статуса (toggleFavorite) напрямую в дочернем компоненте разработчик сталкивается с фундаментальным правилом однонаправленного потока данных Vue: свойства, переданные через Props, доступны только для чтения. Попытка выполнить операцию записи вида props.isFavorite = !props.isFavorite вызовет в консоли предупреждение: «Set operation on key "isFavorite" failed: target is read only».

Истинным владельцем состояния является родительский компонент App.vue. Дочерний компонент не имеет права мутировать чужие данные напрямую. Единственный архитектурно верный способ изменить состояние родителя изнутри потомка — это сгенерировать специальное кастомное событие (Emit), на которое родитель сможет отреагировать.

Настройка defineEmits и передача аргументов родителям 3:36:14

Для организации обратной связи в дочернем компоненте объявляется макрос defineEmits, принимающий массив регистрируемых событий. Для нашей задачи событие логично назвать updateFavorite. Внутри локальной функции вместо прямой мутации данных вызывается метод emit('updateFavorite'). Родительский компонент подписывается на это событие в своём шаблоне с помощью директивы @update-favorite. Как только дочерний компонент отправляет сигнал, родитель перехватывает его и выполняет безопасное переключение значения на своей стороне.

Vue.js позволяет не просто сигнализировать о событии, но и передавать вместе с ним сопутствующие параметры. Метод emit поддерживает отправку дополнительных аргументов сразу после имени события.

Передавать данные можно несколькими способами:

Передача через массивы заставляет разработчика на стороне родителя играть в «угадайку» с индексами элементов (нулевой индекс, первый индекс). Использование объектов является куда более чистым и масштабируемым подходом. Родитель принимает эти параметры через специальную переменную $event в шаблоне или в качестве аргументов функции-колбэка onUpdateFavorite(dollarEvent).

В качестве альтернативы классическим методам Vue также поддерживает инлайновые (встроенные) эмиты прямо в коде шаблона. Разработчик может написать @click="$emit('updateFavorite', ...)" непосредственно на кнопке, полностью избавившись от необходимости описывать отдельную функцию в секции <script setup>, если логика триггера тривиальна.

🧩 Проблема Prop Drilling и механизм Provide/Inject 3:54:49

При построении сложных иерархий компонентов во Vue неизбежно возникает ситуация, когда данные или методы необходимо передать из корневого компонента (родителя) глубоко в дочерние компоненты (внуков или правнуков). В таких случаях разработчики сталкиваются с концепцией Prop Drilling (сквозная передача свойств).

Что такое Prop Drilling и почему это неудобно 4:05:00

Проблема «прокидывания» свойств возникает, когда промежуточный компонент, находящийся в иерархии между источником данных и их конечным потребителем, вынужден принимать эти данные через props, чтобы просто передать их дальше.

В рассматриваемом примере приложение использовало maxNumber (максимальное число для генерации), которое передавалось из App.vue в Contact.vue только для того, чтобы тот передал его в LuckyNumber.vue. Сам компонент Contact никак не использовал это значение. Ранее в разговоре упоминалось использование defineProps для передачи данных, но этот подход имеет явные границы эффективности.

Использование Provide и Inject как альтернатива 4:06:45

Для решения проблемы Prop Drilling во Vue предусмотрен механизм Provide/Inject. Это позволяет компоненту-родителю «предоставить» данные, которые могут быть «внедрены» (inject) любым дочерним компонентом в дереве, независимо от глубины вложенности, минуя промежуточные звенья.

Важно помнить, что ключи в provide должны быть уникальными, чтобы не конфликтовать с другими данными в приложении. Если при inject указать неверный ключ, данные не будут найдены, что приведет к ошибке в приложении.

Использование этого подхода радикально упрощает структуру компонентов, позволяя передавать параметры только туда, где они действительно необходимы, избавляя промежуточные компоненты от лишней ответственности за управление данными.


Проверка работоспособности 4:09:10

После внедрения provide и inject, функционал приложения остался прежним: при изменении maxNumber в родительском App.vue, новые данные автоматически подхватываются компонентом LuckyNumber.vue благодаря реактивной связке. Это подтверждает, что при передаче полной ссылки (ref) изменения корректно отражаются на всех уровнях вложенности в режиме реального времени.

🧩 Динамические компоненты, магия KeepAlive и гибкие слоты во Vue 4:17:51

Динамический рендеринг через специальный тег <component> 4:17:51

Завершив работу над учебным приложением Contactopedia, в рамках которого ранее в разговоре кратко упоминались механизмы передачи родительских методов через входные данные, генерации кастомных событий дочерними элементами, а также архитектурный паттерн provide/inject, ментор переходит к деконструкции и обновлению кодовой базы. Из проекта полностью вычищаются старые сущности, уступая место двум независимым версиям генератора чисел: базовой LuckyNumber и продвинутой LuckyNumberV2 с настраиваемым максимальным диапазоном. Перед разработчиком встает классическая задача: выводить на экран строго одну версию за раз, предоставив пользователю удобный инструмент для переключения.

Для управления отображением в шаблон внедряются три управляющие кнопки с классами btn btn-primary text-black. Но вместо того чтобы городить громоздкие условные ветвления, Vue предлагает изящное решение — динамические компоненты. В секции скрипта объявляется вычисляемое свойство currentComponent, работающее на основе тернарного оператора. Оно возвращает не текстовое имя и не абстрактный идентификатор, а полноценную конфигурацию импортированного компонента в зависимости от булева флага newVersion.

Для отрисовки используется универсальный зарезервированный тег <component> со специальным реактивным атрибутом :is. Передавая в :is вычисляемое свойство, разработчик делегирует Vue задачу на лету подменять внутреннюю разметку узла.

В процессе привязки событий клика к кнопкам лектор делает важный акцент на специфике работы с реактивными обертками. Внутри шаблона при изменении флага newVersion категорически запрещено дописывать расширение .value. Попытка явного вызова .value в пространстве шаблона приведет к падению приложения с ошибкой компиляции из-за попытки создать свойство на чистом булевом типе. Фреймворк автоматически распаковывает (unwraps) любые refs-объекты, попадающие в блок <template>, поэтому в верстке к ним всегда обращаются напрямую.

Сохранение состояния с помощью встроенного инструмента <keep-alive> 4:23:50

В ходе интерактивного тестирования динамической смены версий обнаруживается критический изъян в пользовательском опыте. Если переключиться на LuckyNumberV2, изменить стандартный лимит со 100 на 1000, сгенерировать число, а затем временно переключить интерфейс на первую версию и вернуться назад — все введенные данные бесследно исчезают, а конфигурация сбрасывается к первоначальным настройкам. По умолчанию тег <component> полностью уничтожает (unmounts) скрываемый элемент, стирая его состояние из оперативной памяти, и монтирует его заново при повторном вызове.

Для преодоления этого ограничения Vue предлагает встроенный компонент <keep-alive>. Обернув динамический узел в этот тег, разработчик заставляет систему сохранять экземпляры неактивных компонентов в памяти. Теперь при переключении состояние полей ввода и сгенерированные числа остаются нетронутыми.

Поскольку кэширование абсолютно всех компонентов в больших приложениях нерационально расходует ресурсы, <keep-alive> поддерживает гибкие директивы фильтрации:

При работе с атрибутом include важно соблюдать строгий синтаксис: если требуется перечислить несколько компонентов (например, lucky number и lucky number v2), их имена передаются в виде массива строк, который обязательно должен быть оформлен внутри одинарных кавычек, вложенных в двойные кавычки атрибута шаблона.

Кастомизация разметки и распределение контента через слоты 4:28:10

После масштабного рефакторинга интерфейса и инкапсуляции управляющей логики в отдельный родительский компонент LuckyNumberComponent, лектор переходит к демонстрации еще одного мощного инструмента декларативной верстки — слотов (slots). В отличие от классических входных параметров, которые доставляют сырые данные исключительно в логическую часть скрипта, слоты позволяют передавать полноценные куски HTML-разметки напрямую из родительского шаблона в структуру дочернего элемента.

Любой контент (например, параграф с текстом-подсказкой), помещенный между открывающим и закрывающим тегами дочернего компонента, автоматически проецируется в то место, где внутри дочернего шаблона прописан одиночный тег <slot></slot>. Ментор акцентирует внимание на фундаментальном правиле: передаваемый контент полностью подчиняется области видимости родителя. В качестве доказательства внутри передаваемой разметки создается кнопка, которая без проблем вызывает родительскую функцию showMessage и обновляет реактивную строку message, объявленную в App.vue. Дочерний компонент выступает лишь как физический контейнер для отображения, не имея доступа к переменным внутри слота.

Для реализации сложных многокомпонентных интерфейсов стандартного слота часто не хватает, поэтому Vue поддерживает концепцию именованных слотов. С помощью специального атрибута v-slot:имя_слота (или лаконичной директивы #имя_слота) разработчик может распределять элементы разметки по разным частям дочернего компонента. Для этого передаваемые блоки оборачиваются в тег <template>, а в дочернем файле вызываются соответствующие теги <slot name="имя_слота">.

При комбинировании подходов важно помнить о строгом правиле: если вы явно используете именованный дефолтный шаблон v-slot:default, внутри родительского тега не должно оставаться никакого «свободного» контента вроде случайных тегов <hr>, иначе компилятор Vue выдаст ошибку из-за двусмысленности распределения неразмеченных элементов.

🔄 Жизненный цикл компонентов и мощь композитных функций 4:38:49

Анатомия жизненного цикла: от инициализации до уничтожения 4:38:49

Понимание жизненного цикла компонентов является одним из основополагающих столпов веб-разработки. Во Vue 3 с применением Composition API разработчики получают гибкий контроль над каждым этапом существования элементов интерфейса — от их первоначальной инициализации до полного удаления из DOM-дерева через специализированные хуки. Чтобы наглядно продемонстрировать порядок их вызова, авторы развернули тестовую конфигурацию, включающую корневой компонент app.vue и дочерний счетчик button counter. Набор отслеживаемых методов включил в себя пары onBeforeMount / onMounted, onBeforeUpdate / onUpdated, а также onBeforeUnmount / onUnmounted.

Проведенный эксперимент выявил важнейшую архитектурную особенность: родительский компонент никогда не завершит свое монтирование до тех пор, пока полностью не смонтируются все его дочерние элементы. Логи консоли наглядно иллюстрируют этот процесс: сначала вызывается onBeforeMount для app.vue, следом запускается onBeforeMount для button counter, после чего дочерний элемент объявляет о готовности через onMounted, и только в самом конце срабатывает финальный onMounted родительского компонента.

Когда дело доходит до обновления данных, система ведет себя изолированно. Клик по кнопке внутри дочернего счетчика активирует исключительно его собственные хуки onBeforeUpdate и onUpdated. Корневой компонент при этом остается в покое, поскольку его собственные данные никак не изменились. Ранее в разговоре уже затрагивались базовые аспекты реактивности, но именно на стыке реактивных переменных и хуков жизненного цикла раскрывается истинная динамика интерфейса.

Особенно ярко это проявляется при использовании механизмов условного рендеринга, ведь ранее в разговоре они касались директив условного отображения элементов. Авторы добавили динамическое условие отображения дочернего компонента с помощью реактивной переменной count, заставив его монтироваться только тогда, когда значение счетчика строго равно единице. В момент, когда условие становится истинным, порядок вызовов хуков усложняется.

Когда условие становится истинным, порядок вызовов хуков резко усложняется:

При разрушении связи ситуация зеркально повторяется: когда счетчик увеличивается до двух, дочерний компонент мгновенно скрывается, что провоцирует запуск onBeforeUnmount и onUnmounted для button counter в промежутке между обновлением родителя. Авторы подчеркивают, что сами по себе эти методы выполняются синхронно в строгой последовательности. Однако внутри них можно без проблем использовать асинхронные операции, например, синтаксис async/await для выполнения HTTP-запросов и предзагрузки необходимых данных из внешних API до того, как интерфейс отобразится перед пользователем.

Композиблы во Vue 3: инкапсуляция и переиспользование логики 4:48:41

Следующим ключевым этапом эволюции Vue 3 стало внедрение композиблов (composables) — переиспользуемых функций, кардинально меняющих подход к организации кода. В реальной практике код внутри компонентов стремительно разрастается, и разработчикам требуется эффективный инструмент для выноса сложной логики в отдельные файлы. В эпоху Options API для этих целей применялись миксины (mixins), часто приводившие к конфликтам имен и непрозрачности кода, но Composition API предлагает куда более изящную альтернативу.

Для соблюдения чистоты архитектуры Vue рекомендует организовывать код, создавая выделенную папку composables внутри директории src. Существует и строгое соглашение по именованию: любая композитная функция должна начинаться с префикса use и записываться в стиле camelCase, как в случае с созданным авторами файлом useCounter.js. Фантастическое преимущество такого подхода заключается в том, что композиблы могут свободно содержать внутри себя реактивные свойства, такие как ref.

Анатомия правильного композибла базируется на строгом принципе возврата значений:

Гибкость настройки и практическое разделение ответственности 4:52:16

Внедрение созданного композибла в компонент выглядит невероятно просто и эстетично. Внутри тега script setup разработчику достаточно импортировать функцию useCounter и деструктурировать ее: const { count, increment, decrement } = useCounter(). В результате код в app.vue становится кристально чистым, избавляясь от нагромождения внутренних функций.

Более того, авторы продемонстрировали выдающуюся гибкость композиблов, добавив в useCounter входные параметры. Функция научилась принимать initialValue (начальное значение) и step (шаг изменения), обеспечивая умные дефолтные значения на случай их отсутствия. Благодаря этому один и тот же композибл можно одновременно переиспользовать в разных компонентах с абсолютно уникальными настройками: в корневом приложении счетчик может инициализироваться со значения 100 и шагом 10, а в изолированном дочернем элементе — стартовать с нуля с шагом в единицу. Их внутренние состояния при этом остаются полностью независимыми.

Подобная модульность позволяет даже вкладывать одни композиблы внутрь других. Вся прелесть изоляции логики сводится к тому, что при изменении бизнес-требований разработчику достаточно скорректировать алгоритм в одном единственном файле композибла, и обновленное поведение мгновенно применится по всему приложению, исключая необходимость ручной правки десятков компонентов. Ранее в обсуждении упоминались концепции передачи данных через props, но композиблы выводят переиспользование кода на совершенно иной уровень автономности.

Очистив проект от тестового кода и заложив прочный фундамент понимания жизненного цикла и композиблов, авторы переходят к следующему масштабному шагу разработки — развертыванию нового чистого проекта для изучения многостраничной маршрутизации с помощью Vue Router, которая подробно рассматривается в последующих главах.

🛠️ Практика настройки маршрутизации: от базовой конфигурации до динамических параметров 5:01:35

Архитектурная подготовка и создание конфигурационного файла routes.js 5:01:35

Начало практического построения полноценной структуры приложения требует создания выделенных компонентов для каждой будущей страницы. Процесс стартует с генерации базовых файлов: Header.vue, Footer.vue, а также страниц Homepage.vue и Contact.vue. Для организации каталога товаров создается отдельная папка product, куда помещаются компоненты ProductList.vue и ProductDetail.vue. На начальном этапе в шаблоны добавляется заглушечный текст, позволяющий визуально контролировать переключения. Несмотря на то, что пакет маршрутизатора уже установлен в проекте, само по себе приложение еще не умеет обрабатывать переходы. Стоит отметить, что структура SFC компонентов и сборщик Vite ранее подробно обсуждались в главе 7.

Для управления всеми путями создается изолированная папка router, внутри которой размещается основной конфигурационный файл routes.js. Чтобы инициализировать маршрутизатор, необходимо импортировать функцию createRouter из библиотеки vue-router. Существует несколько режимов работы с историей переходов, включая createMemoryHistory и createWebHashHistory. Однако наиболее привычным, дружелюбным и стандартным для пользователей подходом является использование традиционных слешей в URL, что реализуется с помощью функции createWebHistory. Создаваемый экземпляр маршрутизатора принимает объект конфигурации, содержащий тип истории и массив объектов самих маршрутов, после чего экспортируется наружу. Наконец, в файле main.js создается константа приложения, на которой вызывается метод app.use(router), подключающий маршрутизацию перед монтированием приложения. При первой проверке в консоли разработчика появляется предупреждение о том, что для базового пути / совпадений не найдено, что парадоксальным образом подтверждает успешную активацию плагина в системе.

Интеграция RouterView и глобального шаблона приложения 5:07:19

Связывание компонентов с конкретными адресами происходит внутри массива маршрутов в файле routes.js. Для каждого пути регистрируются как минимум два ключевых свойства: текстовый path и импортированный компонент. Например, для корневого адреса / назначается Homepage, а для пути /contact — компонент Contact. После внесения этих изменений предупреждения в консоли исчезают, однако ручной ввод адреса в строку браузера по-прежнему не приводит к визуальному обновлению интерфейса.

Причина кроется в отсутствии критически важного элемента — специального тега <router-view>, который указывает фреймворку точное место для рендеринга текущего компонента. Как только этот тег добавляется в файл App.vue, на экране мгновенно отображается контент целевой страницы. Важной архитектурной особенностью является то, что любые элементы, расположенные до или после <router-view>, будут оставаться неизменными на всех страницах приложения. Это идеальное место для размещения сквозных элементов интерфейса — шапки и подвала сайта, между которыми маршрутизатор будет динамически переключать содержимое страниц. При развертывании проекта, созданного через стандартные инструменты Vite, конфигурация createWebHistory обычно дополняется системной переменной import.meta.env.BASE_URL для корректного определения базового пути. Для создания визуальной структуры в компонент шапки внедряется верстка навигационной панели на базе фреймворка Bootstrap, содержащая ссылки на главную страницу, контакты, авторизацию и список продуктов.

Навигация через RouterLink и решение проблемы относительных путей 5:14:03

После выноса верстки панели навигации в отдельный изолированный компонент Header.vue, стандартные HTML-теги ссылок <a> с атрибутами href заменяются на специализированный компонент <router-link>. Использование обычных ссылок привело бы к полной перезагрузке страницы браузера, что нарушает концепцию Single Page Application. В <router-link> традиционный атрибут адреса заменяется на параметр to, принимающий целевой путь, например, / для главной страницы или /contact для формы обратной связи. Это делает переходы мгновенными, плавно обновляя состояние адресной строки без мерцания экрана.

В процессе масштабирования навигационного меню и добавления путей для каталога товаров разработчики часто сталкиваются с коварной ошибкой относительных путей. Если в параметре to опустить ведущий слеш (написать product-list вместо /product-list), маршрутизатор начнет конкатенировать строки. Находясь на динамической странице товара, клик по такой ссылке приведет к тому, что новый адрес прибавится к текущему URL, безвозвратно ломая логику путей. Добавление ведущего косого слеша гарантирует, что переход будет осуществляться строго от абсолютного корня сайта, очищая предыдущий контекст маршрута.

Работа с динамическими и опциональными параметрами маршрутов 5:17:40

В реальных веб-приложениях страницы детального просмотра товаров требуют передачи уникального идентификатора через URL. Для реализации этого сценария в routes.js регистрируется специальный динамический путь product/:productId, где двоеточие указывает на изменяемый параметр. Чтобы извлечь переданное значение внутри самого компонента ProductDetail.vue, используется встроенная композиционная функция useRoute из пакета vue-router. Созданный экземпляр маршрута предоставляет доступ к объекту route.params, содержащему все переменные из строки запроса, имя которых в точности совпадает с объявленным в конфигурации.

Интересной возможностью фреймворка является регистрация одного и того же компонента на разных путях. Например, можно настроить отображение ProductDetail как для адреса с идентификатором, так и для общего пути /product. В таком случае внутри компонента организуется проверка: если route.params.productId существует, выводится информация о конкретном продукте, иначе пользователю сообщается, что ID товара не найден. Здесь применяется директива условного рендеринга v-if, особенности которой подробно рассматривались в главе 3. Маршрутизатор также поддерживает обработку нескольких параметров одновременно, например, связку идентификатора продукта и категории: product/:productId/:categoryId. Если один из параметров должен быть необязательным, после его имени ставится знак вопроса — :categoryId?, что исключает поломку отображения страницы при отсутствии второй переменной в URL. В завершение, для динамического вывода элементов каталога используется рендеринг списков через v-for, изученный в главе 3. В дальнейшем детальный разбор многостраничной маршрутизации с Vue Router будет развернут в главе 14.

🌐 Продвинутая маршрутизация и защита путей во Vue.js 5:26:34

Эффективная работа с навигацией — это фундамент любого современного SPA-приложения. Использование Vue Router позволяет выйти далеко за рамки простой смены URL, предоставляя разработчику мощные инструменты для контроля того, как пользователи перемещаются по приложению, как передаются параметры и как защищены отдельные разделы сайта.

Динамические параметры и именованные маршруты 5:27:28

Для динамического формирования путей, например, когда нам нужно передать идентификатор товара (product ID), используется связывание свойств. Мы можем формировать URL, динамически добавляя параметры через интерполяцию строк $ и фигурные скобки {}, что позволяет создавать гибкие ссылки на детальные страницы товаров.

Однако управление длинными путями вручную неудобно. Vue Router предлагает концепцию именованных маршрутов (named routes). Вместо жестко прописанного пути path: '/contact' мы присваиваем маршруту уникальное имя, например, name: 'contact'. Преимущества такого подхода очевидны:

Перенаправления и обработка ошибок 404 5:30:34

При изменении структуры URL важно сохранять доступность страниц для пользователей, которые могли сохранить старые ссылки. Для этого используются редиректы (redirects). Если пользователь переходит по устаревшему адресу, роутер автоматически перенаправит его на актуальный путь или именованный маршрут.

Для обработки несуществующих страниц используется механизм «catch-all» маршрута. Добавив путь / :catchAll(.*), мы можем перехватывать абсолютно все запросы, которые не соответствуют списку существующих маршрутов, и направлять пользователя на специализированную страницу 404 Not Found.

Программная навигация и управление историей 5:33:15

Иногда навигация должна происходить не по клику на ссылку, а программно, например, внутри функции после успешного завершения какого-либо действия. Использование метода router.push() позволяет переключать страницы прямо из скрипта компонента.

Кроме того, объект $router предоставляет метод .go(), который позволяет управлять историей браузера:

Навигационные гарды: безопасность и контроль доступа 5:36:27

Навигационные гарды (navigation guards) выступают в роли «охранников» вашего приложения. Они позволяют проверять права пользователя до того, как произойдет переход на целевую страницу.

  1. Глобальные гарды: Используя router.beforeEach, мы можем отслеживать все перемещения в приложении. Этот метод принимает параметры to (куда идем), from (откуда пришли) и next. Сейчас предпочтительнее возвращать true для разрешения навигации или false для запрета. Это идеальное место для проверки токенов авторизации: если пользователь не залогинен, мы принудительно перенаправляем его на страницу логина, если только он не пытается перейти на публичную домашнюю страницу.
  2. Гарды отдельных маршрутов (beforeEnter): Если логика проверки уникальна для конкретной страницы (например, проверка прав администратора для списка товаров), можно использовать хук beforeEnter непосредственно в объекте конфигурации маршрута. Это позволяет хранить специфические правила доступа рядом с определением самого роутера.

Стоит помнить, что имена маршрутов чувствительны к регистру, а настройка классов активных ссылок (например, для интеграции с Bootstrap) осуществляется через свойство linkActiveClass в конфигурации роутера, что позволяет использовать стандартные стили фреймворка для визуального выделения текущего раздела.

🌐 Работа с внешними API и JSON-server 55:15

При разработке современных приложений на Vue работа с данными чаще всего сводится к взаимодействию с внешними API. Для этих целей разработчики обычно выбирают между стандартным методом fetch и библиотекой Axios. Хотя fetch является встроенным API браузера, Axios часто предпочтительнее в экосистеме Vue благодаря своей лаконичности, удобству для разработчиков и автоматическому парсингу JSON-данных.

Для интеграции Axios в проект необходимо установить пакет через npm: npm install axios

Для тестирования и прототипирования на локальной машине разработчики используют JSON Server. Этот инструмент позволяет развернуть полноценный REST API из обычного JSON-файла. Для настройки локальной «базы данных» создается файл (например, db.json в папке data), где описываются необходимые ресурсы (эндпоинты).

Запуск сервера осуществляется командой: json-server --watch data/db.json

После запуска сервер предоставляет локальный URL, через который приложение может выполнять GET-запросы к тестовым данным.

🚀 Запросы к API в жизненном цикле компонента 57:48

Для выполнения API-запросов во Vue идеально подходит хук жизненного цикла onMounted. В этот момент компонент уже смонтирован, и можно безопасно инициировать запрос, чтобы наполнить локальное реактивное состояние данными.

Чтобы код оставался чистым и читаемым, рекомендуется выносить логику запроса в отдельную именованную функцию (например, loadDestination), которая вызывается внутри onMounted. Это избавляет основной хук от лишнего кода и улучшает поддержку проекта.

⏳ Отображение состояния загрузки (Loader) 60:47

Поскольку сетевые запросы асинхронны и могут занимать время, важно информировать пользователя о процессе загрузки. Для этого в объекте состояния добавляется булев флаг, например isLoading.

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

  1. Начало: перед вызовом API флаг устанавливается в true.
  2. Завершение: после получения ответа (в блоке .then) флаг меняется на false.
  3. Визуализация: в шаблоне используется директива v-if для отображения компонента-загрузчика только тогда, когда isLoading равен true.

Для улучшения пользовательского опыта часто добавляют искусственную задержку (через setTimeout внутри Promise), чтобы убедиться, что индикатор загрузки корректно отображается даже при быстром ответе сервера.

🧩 Глобальные компоненты 61:43

Когда функционал индикатора загрузки становится востребованным во всем приложении, его логично вынести в отдельный SFC-компонент (Loader.vue). Чтобы не импортировать его в каждый файл отдельно, его можно зарегистрировать как глобальный компонент в файле main.js:

import Loader from './components/Loader.vue';
// ...
app.component('AppLoader', Loader);

Это позволяет использовать тег <AppLoader/> в любом шаблоне проекта без дополнительного импорта.

Ранее в разговоре они касались настройки маршрутизации с помощью Vue Router.

🕹️ Управление состоянием и асинхронные операции в Vue.js 6:22:31

Разработка современных SPA-приложений немыслима без централизованного управления данными. В рамках изучения экосистемы Vue мы рассмотрели интеграцию Pinia — официального хранилища, которое позволяет эффективно организовывать состояние приложения. Важно отметить, что хотя ранее в разговоре затрагивались вопросы настройки роутинга и общей структуры проекта, теперь мы фокусируемся исключительно на работе с данными через Pinia.

Интеграция Pinia в проект 6:22:44

Pinia устанавливается автоматически при выборе соответствующей опции в конфигурации проекта, что отражается в package.json как зависимость. Однако для активации функционала необходимо зарегистрировать плагин в главном файле main.js.

Процесс подключения выглядит следующим образом:

После этой конфигурации приложение готово к работе с хранилищами (stores), которые служат единым источником правды для всех компонентов.

Создание и структура хранилища (Store) 6:24:29

Для поддержания чистоты архитектуры принято создавать отдельную директорию store. Каждое хранилище обычно соответствует логической сущности приложения (например, gameStore.js).

Внутри файла используется метод defineStore, который принимает два аргумента: уникальный идентификатор хранилища и объект с настройками состояния.

Потребление данных и изменение состояния 6:30:18

Чтобы использовать данные из хранилища в компоненте, мы импортируем useGameStore, вызываем его для получения экземпляра и обращаемся к свойствам напрямую через этот экземпляр.

Для изменения состояния внутри компонента мы можем создавать методы, которые напрямую обращаются к свойствам хранилища:

Использование Getters и Actions 6:35:25

Pinia предоставляет дополнительные инструменты для обработки данных — getters и actions.

-

🍍 Глобальное управление состоянием через Pinia 6:41:55

Разделение обязанностей: Getters против Actions 6:41:55

Финальный этап настройки игрового интерфейса наглядно демонстрирует, как легко допустить досадную опечатку при конфигурации централизованного хранилища. Ошибка в написании ключевого свойства — использование единственного числа action вместо обязательного множественного actions — мгновенно парализует выполнение функций, вызывая в консоли сбой «set next attack is not a function». Исправление этого нюанса возвращает приложению полную работоспособность: механики увеличения, уменьшения и рандомизации показателей атаки или защиты начинают функционировать без нареканий.

Этот практический кейс позволяет четко разграничить две фундаментальные концепции Pinia: getters и actions. Геттеры представляют собой аналог вычисляемых свойств (computed properties) внутри компонента, и их единственная цель — возвращать вычисленное значение на основе текущего состояния хранилища. В свою очередь, экшены аналогичны методам или стандартным функциям, но их ключевое предназначение заключается в прямой мутации данных самого хранилища, а не локального состояния отдельного компонента.

Внедрение игровой логики победы и сброса результатов отлично иллюстрирует это разграничение. Когда текущий счет игрока достигает или превышает отметку в 100 очков, на экране отображается уведомление о победе, а панель управления скрывается. Для перезапуска игрового цикла требуется обнулить прогресс или вернуть его к исходному значению в 50 очков. Попытка обновить это состояние напрямую через геттер технически возможна, однако разработчики настоятельно предостерегают от подобного подхода, называя его архитектурным антипаттерном. Геттеры созданы исключительно для извлечения и чтения данных.

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

Совместное использование состояния между компонентами 6:47:04

Настоящая сила Pinia раскрывается в тот момент, когда одно и то же глобальное состояние начинает распределяться между принципиально разными, изолированными элементами интерфейса, такими как вкладки Home и Info. Вместом того чтобы дублировать логику или жестко прописывать максимальный лимит очков, равный 100, приложение может динамически запрашивать этот параметр напрямую из центрального стора. Для этого в хранилище добавляется новый геттер getWinningScore, который возвращает внутреннюю переменную максимального здоровья. Ранее в разговоре авторы уже затрагивали проблему Prop Drilling и синтаксис Provide/Inject, однако централизованная архитектура Pinia полностью избавляет разработчика от необходимости использовать подобные обходные пути для передачи данных по дереву компонентов.

Чтобы задействовать общие данные на информационной странице, достаточно инициализировать хранилище в блоке сценария:

После выполнения этих шагов реактивная связь устанавливается автоматически. Прямая привязка динамического инпута к свойству maxHealth внутри компонента Info мгновенно синхронизирует изменения по всему приложению. Если пользователь обновляет максимальный порог здоровья до 120 очков на одной странице, логика победы на главной странице тут же перестраивается под этот новый лимит, требуя для выигрыша именно 120 очков вместо исходных 100. Никаких кастомных событий или ручного прослушивания изменений не требуется — Pinia берет всю маршрутизацию реактивности на себя.

Эволюция архитектуры: Переход на Composition API 6:50:53

Несмотря на удобство классического стиля Options API, который изначально использовался для построения хранилища, современным стандартом разработки и наиболее организованным подходом по праву признан Composition API стиль. Он избавляет от необходимости жестко разграничивать код по искусственным секциям state, getters и actions, позволяя группировать логику по смысловым блокам. Процесс миграции хранилища на этот синтаксис удивительно прост и прозрачен. Конфигурация стора превращается в привычную стрелочную функцию, внутри которой вместо объектной структуры объявляются стандартные реактивные константы и функции JavaScript.

Для успешной трансформации кода необходимо выполнить несколько последовательных шагов:

Главное архитектурное отличие Composition-стиля в Pinia заключается в том, что все объявленные внутри стрелочной функции переменные, свойства и методы по умолчанию остаются приватными. Чтобы они стали доступными для внешних компонентов Vue, их необходимо в обязательном порядке перечислить в финальном операторе return в самом конце функции хранилища. Такой подход делает архитектуру стора предельно чистой, лаконичной и предсказуемой, при этом полностью сохраняя исходную бизнес-логику и обеспечивая мгновенное, бесшовное обновление интерфейса.

💬 Цитаты

«v-if is better in situation where you want to dynamically add or remove elements from DOM while v-show is better for frequently toggling the visibility»

«Vue always recommends that you should add a key attribute. That way it will always be able to uniquely identify each element»

«Watchers are more appropriate when you have to perform some kind of side effect.»

«Unless there is a specific use case for registering a component globally, you should always opt for registering them locally as compared to the global registration.»

«The props that you receive here are read only. Only the parent who owns those properties can update them.»

«getters are basically computed props in a component and purpose is to return a computed value based on the store's current state. and actions are similar to methods in your component or function, but their main purpose is to mutate the stores data»

👥 Спикеры
📖 Термины
Composition API
Набор API-функций, позволяющий гибко организовывать логику компонентов Vue.
Reactive
Функция Vue 3 для создания реактивных объектов с глубоким отслеживанием изменений.
Pinia
Библиотека для управления глобальным состоянием в приложениях Vue.
Prop Drilling
Проблема передачи данных через промежуточные компоненты, которые их не используют.
Технологии и IT Vue.js Composition API Vite Pinia Фронтенд-разработка