Skip to content

Установка рабочего проекта

В предыдущих главах мы заложили теоретические основы проектирования веб-приложений на JavaScript с использованием фреймворка Vue 3. Однако до сих пор мы не приступили к реальному проекту. Именно этому и посвящена данная глава. С помощью нового набора инструментов, поставляемых вместе с Vue 3, мы создадим проект с нуля и подготовим шаблон, который будем использовать в других проектах. Как обычно, начальным проектом для веб-приложения является создание списка дел To-Do (эквивалент Hello World). По мере внедрения каждой новой концепции мы будем дорабатывать приложение, превращая его в нечто гораздо более полезное или, по крайней мере, более интересное.

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

  • Настройка рабочей среды и интегрированной среды разработки (IDE)
  • Использование новых инструментов командной строки и нового сборщика Vite для создания каркаса нашего приложения
  • Модификация базового шаблона и структуры папок с учетом лучших практик и продвинутых архитектурных шаблонов
  • Интеграция готовых CSS-фреймворков в наше приложение
  • Конфигурирование сборщика Vite под наши нужды

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

Технические требования

Для выполнения практических шагов, описанных в этой главе, вам потребуется следующее:

  • Компьютер под управлением Windows, Linux или macOS с 64-разрядной архитектурой. Я буду использовать Ubuntu 22.04, но эти инструменты являются кроссплатформенными, и все шаги можно выполнять на разных ОС (если что-то будет отличаться, я укажу на это).
  • Node.js 16.16.0 LTS (рекомендуется ставить последнюю версию - прим. переводчика) с установленным npm (node package manager). Шаги по установке Node.js можно найти в официальной документации по адресу https://nodejs.org/. Инструменты сборки работают поверх Node.js, так что без него вы не сможете продвинуться далеко. Node.js - это версия JavaScript, адаптированная для работы на серверах и в системах "вне" браузера, что делает ее очень и очень удобной и мощной. Большинство современных пакетных программ для веб-разработки так или иначе используют Node.js, хотя бы из-за большого удобства, которое он обеспечивает.
  • Текстовый редактор, работающий с обычным текстом в формате UTF-8, желательно IDE. Для этого инструмента нет недостатка в выборе. Теоретически можно обойтись и без IDE, но я настоятельно рекомендую ее приобрести, хотя бы ради помощи в работе с кодом (известной также как IntelliSense, code completion и т.д.). Вот некоторые из наиболее популярных вариантов:
  • Visual Studio Code (бесплатный): Отличный и очень популярный среди разработчиков вариант от Microsoft, который обеспечивает хорошую поддержку Vue 3 через плагин Volar. Официальный сайт, и в этой книге мы будем использовать этот редактор в качестве рекомендуемой IDE для работы с Vue и Vite.
  • Sublime Text (бесплатная пробная/платная версия): Это еще один популярный вариант, особенно среди пользователей macOS. Официальный сайт
  • Jetbrains WebStorm (бесплатная пробная версия, платная): Официальный сайт
  • Komodo IDE (бесплатно): Официальный сайт
  • NetBeans IDE (бесплатно): Официальный сайт
  • Консоль или эмулятор терминала. Наиболее знакома эта концепция пользователям Linux и macOS. Пользователи Windows могут использовать Command Prompt, встроенный в некоторые IDE терминал, или установить Windows Terminal из Microsoft Store.
  • Современный веб-браузер, основанный на движке Chromium (Google Chrome, Microsoft Edge, Opera, Brave, Vivaldi и др.) или Mozilla Firefox.

Установив все это, мы готовы к рассмотрению примеров и базовых проектов. Однако я рекомендую также установить Git для контроля версий кода. Мы будем использовать его позже в этой книге, в главе 9, Тестирование и контроль исходных текстов.

В современной разработке трудно представить себе работу над проектом без какого-либо инструмента для отслеживания изменений кода и контроля версий. Git стал отраслевым стандартом. Установить его можно, руководствуясь документацией с официального сайта https://git-scm.com/

Файлы кода этой главы можно найти на GitHub здесь

Посмотрите следующее видео, чтобы увидеть код в действии.

Теперь, имея все необходимые инструменты, мы готовы начать наш первый проект на Vue 3.

Настройка проекта и инструменты

Мы создадим новый проект, используя Vite в качестве нашего сборщика, непосредственно из командной строки. Откройте окно терминала в каталоге, где будет размещен наш проект, и выполните следующие шаги:

  1. Наберите следующую команду:
sh
$ npm create vite@latest
  1. Если появится запрос на установку дополнительных пакетов, введите Y (да).

  2. Следующим шагом вам будет предложено ввести информацию о проекте в следующем порядке:

  3. Имя проекта: Оно будет использоваться для идентификации проекта и создания новой папки для его размещения. Если вы хотите, чтобы проект был установлен в текущую папку, введите точку (.) в качестве.

  4. Имя пакета: Это имя будет использоваться для внутренней конфигурации пакета. В данном примере введите chapter-3 (или любое другое имя по вашему выбору). Эта опция может не отображаться, если вы ввели или приняли имя проекта или приняли предложенное по умолчанию имя. Если вы ввели точку (.) в качестве имени для создания проекта в текущем каталоге, то эта опция будет обязательной.

  5. Выбрать фреймворк: Здесь помощник отобразит меню с вариантами. Выберите vue с помощью клавиш со стрелками и нажмите Enter.

  6. Выберите вариант: Как и раньше, используйте клавиши со стрелками и выберите JavaScript (или TypeScript, но мы будем использовать обычный JavaScript на протяжении всей этой книги).

Далее вы увидите, как помощник загружает дополнительное содержимое на основе выбранных вами параметров и формирует проект. Он создаст структуру каталогов с множеством файлов. Однако если мы собираемся запустить проект, то вскоре обнаруживаем, что он просто не работает. Это связано с тем, что при установке каркаса приложения не устанавливаются зависимости, а только скелет. Поэтому остается сделать еще один шаг - установить зависимости с помощью npm. В терминале введите следующую команду и нажмите Enter (если установка производилась в текущий каталог; если нет, то сначала войдите в только что созданный каталог):

sh
$ npm install

Менеджер пакетов скачает и установит зависимости для нашего проекта и поместит их в новый каталог node_modules. Как вы уже догадались, наша среда разработки для Vue с Vite - это Node.js проект.

После установки зависимостей настало время запустить проект и посмотреть, что у нас получилось. В терминале введите следующую команду:

sh
$ npm run dev

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

image

Рисунок 3.1 - Результат работы сервера разработки с Vite

Самой важной информацией здесь является localhost и порт, на котором обслуживается сайт вашего проекта. Миллисекунды здесь указаны только для того, чтобы вы поняли, насколько быстро Vite может запустить сервер разработки (хвастовство, если хотите знать мое мнение). Далее, чтобы увидеть результаты наших трудов, откройте в браузере локальный адрес, и перед вами должен появиться сайт, выглядящий примерно так:

image

Рисунок 3.2: Базовый проект Vite + Vue в браузере

Этот сайт вполне работоспособен, хотя и не очень производителен. Чтобы проверить, что Vue 3 работает, нажмите на кнопку в центре экрана, и вы увидите, как счетчик увеличивается с каждым нажатием. Это и есть реактивность в действии! Кроме того, Vite предлагает нам сервер разработки с функцией живого обновления и Hot Module Replacement (HMR), то есть как только мы внесем изменения в код и сохраним файлы, сайт обновится автоматически. На практике при разработке пользовательских интерфейсов очень часто приходится держать этот самообновляющийся сайт открытым в браузере для предварительного просмотра нашей работы, а в некоторых случаях и в нескольких браузерах одновременно. Очень удобно!

Мы продвинулись в нашем путешествии, но это еще далеко не конец. Сайт в виде каркаса - это не более чем отправная точка. В оставшейся части главы мы модифицируем его, чтобы он лучше соответствовал нашим целям, и создадим простое приложение To-Do.

В следующем разделе мы более подробно рассмотрим структуру и организацию нашего стартового проекта.

Структура папок и её модификации

В главе 1, Фреймворк Vue 3, мы упоминали, что фреймворки предписывают определенную структуру приложению. Vue 3 не является исключением, но соглашения, используемые в структуре каталогов, минимальны по сравнению с другими фреймворками. Если открыть каталог, в который вы установили проект, в проводнике Files Explorer (либо в ОС, либо в IDE), то вы обнаружите структуру, подобную этой:

image

Рисунок 3.3: Структура проекта в Visual Code

Папка .vscode была создана IDE, а node_modules была создана npm для распределения зависимостей. Мы их проигнорируем, так как нам не нужно беспокоиться или работать с ними. Начиная с самого верха, рассмотрим, что представляет собой каждый каталог:

  • public

Эта папка содержит структуру каталогов и файлы, которые не будут обрабатываться сборщиком и будут скопированы непосредственно в конечный сайт. Вы можете свободно размещать здесь свое собственное статическое содержимое. Сюда помещаются изображения, веб-шрифты, сторонние CSS-библиотеки, иконки и т.д. Как правило, здесь размещаются файлы, на которые никогда не будет ссылаться ваш код, например manifest.json, favicon.ico, robots.txt и т.д.

  • src

Здесь мы будем размещать наш JavaScript, динамический CSS, компоненты и т.д. Внутри неё распологаются:

  • Папка assets с SVG-файлом. В эту папку мы можем включить файлы, с которыми будет работать либо код, либо сборщик. Вы можете импортировать их непосредственно в код, а сборщик позаботится об их корректном отображении при передаче на веб-сервер.
  • Папка components, в которую мы поместим наши однофайловые компоненты (SFC) с расширением .vue. Здесь мы можем создать структуру каталогов по своему усмотрению. Здесь размещен компонент HelloWorld.vue.
  • Файл App.vue. Это основной компонент нашего приложения и корневой компонент нашей иерархии. Его принято называть именно так.
  • Файл main.js, который является начальной точкой нашего приложения. Он отвечает за загрузку начальных зависимостей, главного компонента (App.vue), создание приложения Vue 3 со всеми дополнительными функциями (плагины, глобальные директивы и компоненты), а также запуск и установку приложения на веб-страницу.
  • Файл styles.css, представляющий собой глобальную таблицу стилей, которая будет применяться ко всему нашему приложению. В предыдущих версиях инструмента создания структуры проекта его размещали в папке assets, но теперь он переместился в корень src/, заняв более значимое место. Этот файл при импорте в файл main.js будет разобран и объединен с нашим JavaScript.

Настало время исследовать файлы в корне проекта в том же порядке, в котором они появляются:

  • .gitignore - это файл, который управляет тем, что исключается из контроля исходных текстов Git. Мы познакомимся с Git-ом в главе 9, Тестирование и контроль исходных текстов.
  • index.html - это главный файл и отправная точка нашего веб-приложения. Пакет будет обращаться к другим файлам и обрабатывать их в порядке их появления, начиная с index.html. Вы можете изменить его в соответствии со своими потребностями, так как сгенерированный файл является достаточно базовым. Обратите внимание, что в конце тега body инструмент включен тег script для загрузки нашего файла main.js. Именно этот файл создает наше приложение Vue. В отличие от других сборщиков, которые автоматически генерируют этот файл и затем внедряют его в index.html, Vite требует, чтобы он был импортирован явно. Помимо прочих преимуществ, это позволяет контролировать, когда приложение Vue будет загружаться внутри веб-страницы.
  • package-lock.json используется npm для управления зависимостями в node_modules. Игнорируйте его.
  • package.json очень важен. Этот файл определяет проект, отслеживает зависимости для разработки и производства, а также предоставляет некоторые приятные возможности, например, автоматизацию некоторых задач с помощью простых команд. На данный момент интерес представляет секция scripts, в которой задаются простые псевдонимы для команд. Мы можем запускать их из командной строки, набрав npm run <имя скрипта>. Также уже подготовлены для нас три команды Vite:
  • npm run dev: Это запустит сайт в режиме разработчика, с локальным сервером и живой перезагрузкой (hot reload).
  • npm run build: Это позволит собрать наш код в пакет и оптимизировать его для создания готовой продакшен версии.
  • npm run preview: Это промежуточный вариант между двумя предыдущими. Он позволяет локально увидеть собранную версию, готовую к производству. Это может показаться непонятным, если не учитывать, что в процессе разработки адреса и ресурсы, к которым обращается приложение, а также публичный URL, могут отличаться от тех, что используются в производстве. Эта опция позволяет запускать приложение локально, но при этом ссылаться на производственные конечные точки и ресурсы и использовать их. Рекомендуется запускать "предварительный просмотр" перед развертыванием вашего приложения.
  • vite.config.js - это конфигурационный файл, определяющий поведение Vite во время разработки и при сборке для производства. Некоторые из наиболее важных и распространенных опций мы рассмотрим далее в этой главе.

Теперь, когда мы имеем более четкое представление о том, что нам дал инструмент Vite scaffolding, самое время приступить к созданию нашего примера приложения. Прежде чем углубиться в код, необходимо рассмотреть еще несколько вопросов: как интегрировать сторонние таблицы стилей и CSS-фреймворки, а также некоторые конфигурации Vite, которые облегчат нам жизнь.

Интеграция с CSS-фреймворками

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

В Интернете существует постоянно растущая коллекция CSS-фреймворков и библиотек, которые мы можем легко внедрять в наши приложения. От старого популярного Bootstrap до атомарного дизайна, от утилитарных классов, таких как Tailwind, до графических языков, таких как Material Design и skeuomorphism - спектр возможностей огромен.

В Vue уже имеется достаточное количество библиотек компонентов, реализующих некоторые из них, которые можно найти в репозиториях npm. Используя их, вы будете ограничены в изучении и применении соглашений, применяемых дизайнером, которые в некоторых случаях могут полностью определять, как вы можете построить свой пользовательский интерфейс. Типичными примерами являются использование Vue-material (и других), соответствующих спецификациям Material Design от Google, или применение веб-шрифтов и шрифтов-иконок.

Невозможно рассказать о каждом из них, но здесь приведены рекомендации и некоторые примеры включения этих библиотек в ваш проект:

  1. Поместите статические активы, предоставляемые фреймворком или библиотекой, в папку public, следуя их требуемой структуре и соблюдая любую структуру дерева.
  2. Включите зависимости для CSS-фреймворка или библиотек в файл index.html, следуя их инструкциям. Часто это подразумевает импорт таблиц стилей и файлов JavaScript в раздел head или в тег body. В любом случае убедитесь, что они размещены до загрузки нашего приложения (тег script, ссылающийся на наш файл main.js).
  3. Если фреймворк или библиотеку необходимо инстанцировать, сделайте это до того, как мы смонтируем наше приложение. Это можно сделать непосредственно в index.html в теге script, в main.js или в другом модуле.
  4. Используйте классы (и JavaScript-функции) в секции шаблона вашего компонента обычным образом, как и в обычном HTML, используя эти библиотеки. Некоторые фреймворки создают глобальные объекты JavaScript, привязанные к объекту window, поэтому к ним можно обращаться непосредственно в секции script компонента. Если это не так, рассмотрите возможность инкапсуляции функциональности, чтобы использовать ее в своем приложении, используя паттерны проектирования, такие как синглтон, прокси или декоратор.

А теперь давайте применим эти простые инструкции на примере нашего проекта. Мы включим в него фреймворк, работающий только на CSS (это означает, что он не использует дополнительный JavaScript), а значки шрифтов будут включать базовую иконографию. В промышленной сборке мы должны удалить неиспользуемые правила CSS. Некоторые CSS-фреймворки предоставляют эту возможность из коробки, например, Tailwind. Однако эта тема выходит за рамки данной книги, но ее можно изучить в Интернете.

Фреймворк w3.css

На сайте w3schools.com предлагается бесплатный CSS фреймворк, частично основанный на языке Material Design, популярном в Google и используемом во многих мобильных приложениях. Он предлагает множество полезных классов, которые можно без лицензии применять в своих приложениях. Более подробную информацию можно найти на официальном сайте: https://www.w3schools.com/w3css/.

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

  1. Скачайте файл w3.css с сайта https://www.w3schools.com/w3css/w3css_downloads.asp и поместите его в новую папку с именем css в каталоге public. По окончании работы он должен выглядеть следующим образом:

image

Рисунок 3.4 - Расположение файла w3.css

  1. Модифицируем index.html в корне нашего проекта, добавив ссылку на файл w3.css с помощью тега link следующим образом:
html
<link rel="stylesheet" href="/css/w3.css">

Таким образом, классы, определенные в CSS-файле, теперь готовы к использованию в шаблонах нашего компонента. Также, чтобы избежать нежелательных стилей при начальном развертывании приложения, не забудьте очистить файл styles.css, предоставляемый программой установки. Если теперь запустить сервер разработки с помощью команды npm run dev, то мы увидим, что внешний вид сайта немного изменился, так как новая таблица стилей была успешно применена.

Следующим шагом будет добавление шрифта icon.

FontAwesome - это просто замечательно

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

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

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

  1. Скачайте шрифты для web с сайта https://fontawesome.com/download. При этом будет загружен довольно большой ZIP-файл со всеми различными вариантами.

  2. Из ZIP-файла скопируйте директории css/ и webfonts/ как есть, в нашу папку public/. Мы не будем использовать все, что находится в этой папке, в наших проектах, поэтому то, что мы не используем, можно будет удалить позже.

  3. Редактируем файл index.html, чтобы добавить таблицы стилей, которые мы будем использовать. Эти CSS-файлы будут автоматически загружать шрифты иконок из папки /webfonts/:

html
<link rel="stylesheet" href="/css/fontawesome.min.css">
<link rel="stylesheet" href="/css/solid.min.css">
<link rel="stylesheet" href="/css/brands.min.css">

Вот и все, что нам нужно сделать, чтобы включить FontAwesome в наш проект. Существуют и другие альтернативы, которые инкапсулируют шрифты в компоненты Vue, и даже веб-сайт предоставляет реализацию Vue. Однако для наших целей в этой книге мы будем использовать прямой подход. Если мы откроем раздел "Значки" на сайте, то сможем просмотреть и найти все доступные значки. Можно ограничить поиск значками "solid" и "brands", поскольку именно их мы включили в наш проект. Например, если необходимо отобразить значок Vue с помощью FontAwesome, мы можем включить в наш шаблон следующее:

html
<i class="fa-brands fa-vuejs"></i>

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

image

Рисунок 3.5 - Страница иконок FontAwesome.

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

Получив хорошую таблицу стилей и несколько хороших шрифтов иконок, мы почти готовы приступить к написанию кода. Осталось сделать еще одну вещь - включить несколько дополнительных опций в конфигурацию Vite.

Опции конфигурации Vite

Файл vite.config.js экспортирует конфигурацию, которую Vite будет использовать для разработки, а также для производства. Vite был задуман как функциональный инструмент для различных фреймворков, а не только для Vue 3, хотя он и является официальным сборщиком для него. Открыв файл, мы видим, что Vue является плагином для Vite. Внутри Vite использует Rollup.js https://www.rollupjs.org/ и esbuild https://esbuild.github.io/, соответственно, для разработки и сборки.

Это означает, что мы можем передавать опции в Vite, но также иметь еще более тонкий контроль над некоторыми крайними случаями, передавая аргументы этим двум инструментам. Кроме того, для каждого режима обработки (разработка и сборка) можно передавать различные конфигурации, так что без вариантов здесь не обойтись.

Некоторые специфические конфигурации для развертывания мы рассмотрим в главе 10, Развертывание приложения, а пока мы сосредоточимся только на части разработки с несколькими дополнениями, чтобы не набирать слишком много текста и не повторяться в коде.

Откройте файл vite.config.js и добавьте следующий импорт:

js
import path from "path"

Да, импортирование path - это не JavaScript, а Node.js, и мы можем это сделать, потому что этот файл читается и выполняется в контексте Node.js. Он никогда не попадет в браузер или какой-либо JavaScript контекст.

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

js
export default defineConfig({
    plugins: [vue()],
    resolve: {
        alias: {
            "@components":
                 path.resolve(__dirname, "src", "components")
        }
    }
})

В этих строках мы указываем псевдоним @components, сопоставленный с путем проекта /src/components. Таким образом, при импорте компонентов мы можем не писать относительные или полные пути, а просто ссылаться на импорт внутри компонентов следующим образом:

js
import MyComponent from "@components/MyComponent.vue"

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

Полную справку по конфигурационному файлу Vite можно найти на https://vitejs.dev/config.. Vite предлагает короткий список официальных плагинов (например, для Vue) на https://vitejs.dev/plugins/, но сообщество предоставило достаточное количество плагинов для многих сценариев на https://github.com/vitejs/awesome-vite#plugins. Их можно установить и импортировать в наш конфигурационный файл, когда это потребуется.

На данный момент мы достаточно подготовились, чтобы двигаться дальше и наконец-то создать наше простое приложение To-Do.

Приложение To-Do

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

  • Разработать приложение с обновлением в реальном времени
  • Создать компонент с реактивными элементами в синтаксисе script setup
  • Применение стилей и иконографических шрифтов из сторонних библиотек

После завершения работы мы получим простой сайт, который должен выглядеть следующим образом (пункты для примера):

image

Рисунок 3.6 - Конечный результат работы приложения To-Do List с примененными стилями

В рамках данного упражнения мы разработаем все приложение To-Do в одном компоненте, который импортируем в наш главный компонент (App.vue). При этом, конечно, намеренно нарушаются некоторые принципы, которые мы рассматривали в главе 2, Принципы проектирования программного обеспечения и паттерны проектирования. В главе 4, Композиция пользовательского интерфейса с помощью компонентов, мы возьмем этот продукт и "доведем его до ума" с помощью многочисленных компонентов.

В приложении пользователь будет делать следующее:

  1. Вводить краткое описание и нажимать клавишу Enter или щелкать на знаке "плюс", чтобы ввести его в качестве задачи.
  2. Система отобразит ожидающие и выполненные задачи в отдельных списках, показывая, сколько их в каждой группе.
  3. Пользователь может щелкнуть на любой задаче, чтобы отметить, выполнена она или отменена, и приложение переместит ее в соответствующую группу.

Зная, как должно работать приложение, перейдем к коду.

App.vue

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

vue
<script setup>
    import ToDos from "@components/ToDos.vue"
</script>

В секции script нам нужно импортировать только компонент с именем ToDos (этот файл мы создадим далее). Обратите внимание, что мы используем уже псевдоним для пути (@components). Наш основной компонент не будет обрабатывать никакие другие данные или функциональность, и мы используем его только в качестве обертки для управления макетом приложения. Исходя из этого, наш шаблон теперь будет выглядеть следующим образом:

vue
<template>
  <div class="app w3-blue-gray">
    <ToDos />
  </div>
</template>

Мы объявили элемент div с классом (.app), который мы определим в разделе style. Мы также применили один из стилей, импортированных нами из W3.css, чтобы придать нашему приложению цвет фона. Внутри элемента div мы разместим наш компонент ToDos.

Обратите внимание, что мы используем то же имя, которое импортировали в нашу секцию script PascalCase. Мы можем использовать эту нотацию или эквивалент HTML - kebab регистр, <to-dos />, (слова в нижнем регистре, разделенные дефисами). Тем не менее, во избежание конфликтов с компонентами HTML, как нынешними, так и будущими, рекомендуется всегда использовать в наших шаблонах регистр PascalCase при использовании нескольких слов. В окончательном варианте HTML это имя будет преобразовано в регистр kebab.

Следующим шагом мы определим стиль, используя CSS flex лэйаут для центрирования нашего компонента по центру экрана:

vue
<style scoped>
.app {
  display: flex;
  justify-content: center;
  width: 100vw;
  min-height: 100vh;
  padding: 5rem;
}
</style>

После установки основного компонента создадим в каталоге /src/components компонент ToDos с правильным названием [ToDos.vue].

ToDos.vue

В этом компоненте мы разместим всю логику этого простого приложения. Нам понадобятся следующие реактивные переменные:

  • Переменная для захвата текста из ввода и создания нашей задачи
  • Массив, в котором мы будем размещать объекты задач со следующими полями: уникальный ID, описание и булево значение, указывающее на то, выполнена задача или нет
  • Функция фильтрации или вычисляемое свойство (или свойства) для отображения только завершенных задач

В соответствии с вышеизложенными требованиями заполним раздел script следующим кодом:

js
import { ref, computed } from "vue"                         //1
const                                                       //2
    _todo_text = ref(""),
    _todo_list = ref([]),
    _pending = computed(() => {                             //3
           return _todo_list.value.filter(item =>
                                          !item.checked)
    }),
    _done = computed(() => {                                //4
           return _todo_list.value.filter(item =>
                                             item.checked)
    });

function clearToDo() {_todo_text.value = "";}                //5

function addToDo() {                                        //6
    if (_todo_text.value && _todo_text.value !== "") {
        _todo_list.value.push({
            id: new Date().valueOf(),
            text: _todo_text.value,
            checked: false
        });
        clearToDo();
    }
}

Начнем с импорта конструкторов ref и computed из Vue в строке //1, поскольку это все, что нам понадобится в данном приложении от фреймворка. В строке //2 мы начинаем объявлять две константы для указания на реактивные значения: _todo_text, которая будет содержать описание задачи пользователя в элементе ввода, и _todo_list, которая будет представлять собой массив задач (элементов дел).

В строках //3 и //4 мы объявляем два вычисляемых свойства с именами _pending и _done. Первое будет содержать реактивный массив всех незавершенных дел, а второе - всех помеченных как завершенные. Обратите внимание, что благодаря использованию свойства computed нам нужно хранить только один массив со всеми элементами. Вычисляемые свойства используются для получения сегмента представления списка в соответствии с нашими потребностями.

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

И наконец, в строке //5 у нас есть вспомогательная функция для сброса значения текста элемента, а в строке //6 - простая функция, которая проверяет значение описания и создает задачу (элемент дел) для добавления в наш список. Важно отметить, что при изменении _task_list все зависящие от него свойства и переменные будут автоматически перевычеслены. Так происходит со свойствами computed.

Вот и все, что нам понадобится в логике нашего компонента для достижения желаемых результатов. Теперь пришло время создать шаблон на HTML. Для удобства мы разобьем код на участки. Выделенные сегменты обозначают наличие связи или взаимодействие с фреймворком и нашим кодом в секции script:

html
<div class="todo-container w3-white w3-card-4">                     //1
     <!-- Простой заголовок -->                                     //2
     <div class="w3-container w3-black w3-margin-0
         w3-bottombar w3-border-blue">
         <h1>
             <i class="fa-solid fa-clipboard-list"></i>
              To-Do List
         </h1>
     </div>
</div>

Шаблон нашего компонента начинается в строке //1 с определения элемента-обертки с некоторыми стилями. Затем, в строке //2, мы размещаем простой заголовок со стилями и шрифтом-иконкой. Обратите внимание, что мы используем классы CSS из фреймворка W3 CSS, а также собственные стили.

Следующие строки кода будут посвящены перехвату пользовательского ввода:

html
<!-- Пользовательский ввод -->                                               //3
<div class="w3-container flex-container w3-light-gray w3-padding">
    <input class="w3-input w3-border-0" type="text"
            autofocus
            v-model="_todo_text"
            @keyup.enter="addToDo()"
            placeholder="Type here your to-do item...">
    <button class="w3-button w3-gray" @click="clearToDo()">
         <i class="fa-solid fa-times"></i>
    </button>
    <button class="w3-button w3-blue" @click="addToDo()">
         <i class="fa-solid fa-plus"></i>
    </button>
</div>

Взаимодействие с пользователем начинается в секции в строке //3, где мы определяем элемент ввода и присоединяем нашу реактивную переменную _todo_text с помощью директивы v-model. С этого момента все, что пользователь набирает в нашем поле ввода, будет значением нашей переменной в коде. Для удобства мы также фиксируем клавишу Enter с помощью следующего атрибута:

js
@keyup.enter="addToDo()"

Это вызовет функцию addToDo из нашего скрипта. То же самое мы добавим в кнопку с плюсом рядом с полем ввода, также по событию клик:

js
@click="addToDo()"

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

html
<!-- Список отложенных дел -->                                          //4
<div class="w3-padding w3-blue">Pending ({{ _pending.length }})
</div>
<div class="w3-padding" v-for="todo in _pending" :key="todo.id">
    <label>
        <input type="checkbox" v-model="todo.checked">
        <span class="w3-margin-left">
             {{ todo.text }}
        </span>
    </label>
</div>
<div class="w3-padding" v-show="_pending.length == 0">No tasks
</div>
<!-- Список выполненных заданий -->                                     //5
<div class="w3-padding w3-blue">Completed ({{ _done.length }})
</div>
<div class="w3-padding" v-for="todo in _done" :key="todo.id">
    <label>
        <input type="checkbox" v-model="todo.checked">
        <span class="w3-margin-left">
             {{ todo.text }}
        </span>
    </label>
</div>
<div class="w3-padding" v-show="_done.length == 0">                     //6
  No tasks
</div>
</div>

Для отображения списка задач у нас есть два практически одинаковых блока кода, начинающихся на строках //4 и //5 - один для отложенных задач, другой - для выполненных. Мы остановимся только на первом блоке (начинающемся на строке //4), поскольку поведение этих блоков практически одинаково.

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

js
Pending ({{ _pending.length }})

Обратите внимание, что мы можем обращаться к атрибутам массива непосредственно внутри двойных фигурных скобок, без использования атрибута .value. Хотя в JavaScript-коде мы должны написать это как _pending.value.length, при использовании интерполяции в HTML Vue достаточно умён, чтобы определить реактивную переменную в нашей секции шаблона и получить доступ к ее значению напрямую. Это верно как для вычисляемых свойств, так и для реактивных переменных, созданных с помощью ref().

В следующем элементе div мы создаем список с помощью директивы v-for/:key, которая будет перебирать наш массив _pending и создавать копию элемента для каждого элемента. Внутри каждого из них мы теперь можем ссылаться на каждый элемент с именем todo, которое мы объявили в директиве v-for.

Далее мы обернем input и span внутри элемента label и привяжем свойство todo.checked (Boolean) к input с помощью v-model. Vue позаботится о присвоении значения true или false в зависимости от состояния флажка. Когда это произойдет, он также вызовет пересчет свойств computed, и мы увидим, как при установке/снятии флажка элемент перемещается между группами (ожидающие и завершенные), а также обновляет общее количество каждого блока. У нас также есть элемент span для отображения текста задачи.

И наконец, для случаев, когда группа списка пуста, у нас также есть элемент div, который будет виден только в том случае, если этот список пуст, в строке //6 (\pending.length==0)

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

В данном случае объем наших стилей будет невелик, так как нам потребуется всего пара дополнительных настроек, поскольку основная часть работы была выполнена с помощью библиотеки w3.css. Внутри нашей секции style добавьте следующее:

css
.todo-container {max-width: 100%; min-width: 30rem;}
label {cursor: pointer; display: flex;}

Класс todo-container ограничивает максимальную и минимальную ширину нашего компонента, а также мы модифицируем элемент label для отображения его дочерних элементов с помощью макета flex

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

sh
$ npm run dev

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

Краткая критика нашего приложения To-Do

Приложение, которое мы только что сделали, работает и является немного более продвинутым, чем простое Hello World или кнопка счетчика. Однако мы не применили все лучшие практики и паттерны, которые должны или могли бы применить. Это сделано специально, в качестве обучающего упражнения.

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

Как только мы создали первый прототип, самое время сделать шаг назад и подвергнуть его искренней критике, подумав о том, как мы можем его улучшить и сделать лучше. В данном случае, вот наша критика:

  • В нашем шаблоне есть дублирование кода, поскольку вычисляемые свойства _pending и _done в принципе одинаковы, с небольшим отличием, основанным на значении переменной.
  • Мы не используем возможности компонентов, поскольку все построено в одном компоненте.
  • Наш компонент также создает наши модели (элементы To-Do), поэтому наша бизнес-логика привязана к нашему компоненту.
  • Мы сделали очень мало в плане санитизации и контроля ввода. Можно предвидеть, что некоторый код, даже с одинаковыми входными данными, сломает наше приложение.
  • Наш список дел непостоянен. Обновление страницы может очистить наш список.
  • В нашей задаче есть только два состояния (выполнено и отложено). А что, если мы хотим иметь третье или более состояний? Например, "в процессе", "в ожидании" или "следующий в очереди"
  • Текущий дизайн не предусматривает возможности редактирования или удаления задачи после ее создания.
  • Мы можем одновременно управлять только одним списком элементов.

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

Подведение итогов

В этой главе мы начали создавать приложения, используя реальные инструменты, от IDE до инструментов командной строки, для создания каркаса приложения, предварительного просмотра и сборки приложения. Мы также создали простое приложение To-Do и узнали, как можно интегрировать сторонние CSS-библиотеки и шрифты иконок в наше приложение, а также определили некоторые общие рекомендации по использованию других библиотек.

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

Вопросы для проверки

  • Каковы требования к разработке приложения Vue 3 с помощью Vite?
  • Возможно ли интегрировать сторонние библиотеки и фреймворки с Vue 3?
  • Каковы шаги по интеграции библиотеки, работающей только с CSS, в приложение Vue?
  • Является ли хорошей идеей создание приложения внутри одного компонента? Почему да или нет? Можете ли вы назвать сценарии, когда однокомпонентное приложение является подходящим вариантом? А как насчет сценария, когда это не так?
  • Почему разработка программного обеспечения - это итеративный процесс совершенствования?