Оглавление #
Этап 1 #
Тема #
Создание проекта с использованием webpack, luxon, Bootstrap CDN и Docker.
Что было сделано #
В рамках задания был подготовлен отдельный проект labs/webpack2025 внутри репозитория с такими шагами:
- инициализирован npm-проект;
- установлены
luxon,webpack,webpack-cliиserve; - создан исходный файл
src/index.jsдля сборки черезwebpack; - добавлена веб-страница
index.htmlс подключением Bootstrap по CDN; - реализован крупный вывод даты и времени через
luxon; - создан
Dockerfileна базеnode:24-alpine; - выполнены локальная сборка, запуск проекта и запуск в Docker-контейнере.
Локальная среда:
node v24.15.0npm 11.12.1Docker 29.3.1
Структура проекта #
Основные файлы лабораторной:
labs/webpack2025/src/index.jslabs/webpack2025/src/renderClock.jslabs/webpack2025/index.htmllabs/webpack2025/webpack.config.jslabs/webpack2025/Dockerfile
Результат команды npx webpack
#
Команда сборки:
npx webpackРезультат сборки:
asset main.js 69.1 KiB [emitted] [minimized] (name: main)
orphan modules 256 KiB [orphan] 2 modules
./src/index.js + 2 modules 256 KiB [built] [code generated]
webpack 5.106.2 compiled successfully in 404 msСкриншот вывода команды:

Веб-страница с Bootstrap CDN и Luxon #
В index.html подключен Bootstrap через CDN:
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr"
crossorigin="anonymous"
/>Время выводится через luxon крупным шрифтом и обновляется каждую секунду:
import { DateTime } from "luxon";
setInterval(() => {
hh.textContent = DateTime.local()
.setLocale("ru")
.toFormat("dd.LL.y HH:mm:ss");
}, 1000);Скриншот внешнего вида страницы:

Содержимое Dockerfile
#
Использован образ node:24-alpine, так как в задании рекомендовано применять alpine-версию образа.
FROM node:24-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npx webpack
EXPOSE 3000
CMD ["npx", "serve", ".", "-l", "3000"]Запуск приложения через Docker #
Сборка образа:
docker build -t webpack2025-lab .Запуск контейнера:
docker run --rm -p 3001:3000 --name webpack2025-lab-container webpack2025-labПроверка контейнера:
docker ps --filter name=webpack2025-lab-containerРезультат проверки:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7dc727533edf webpack2025-lab "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:3001->3000/tcp, [::]:3001->3000/tcp webpack2025-lab-containerСкриншот запущенного контейнера:

Скриншот запуска контейнера через docker run:

Скриншот страницы, запущенной из Docker-контейнера:

Последовательность действий для запуска #
Если повторять запуск с нуля, достаточно выполнить:
cd labs/webpack2025
npm install
npx webpack
npx serve . -l 3000Для запуска в Docker:
cd labs/webpack2025
docker build -t webpack2025-lab .
docker run --rm -p 3001:3000 --name webpack2025-lab-container webpack2025-labПосле этого приложение будет доступно по адресу:
http://localhost:3000/Для Docker-варианта:
http://localhost:3001/Вывод #
Был создан рабочий проект с базовой сборкой через webpack, отображением времени через luxon, адаптивной страницей на Bootstrap CDN и запуском в Docker-контейнере на базе node:24-alpine. Вся часть, связанная с Vite, вынесена в отдельный третий этап.
Этап 2 #
Тема #
Интеграция Bootstrap 5 в приложение с использованием библиотеки Luxon.
Что было сделано #
На втором этапе приложение с Luxon было встроено в шаблон на Bootstrap 5.
В работе были выполнены следующие шаги:
- подготовлена Bootstrap-разметка страницы с колонками в соотношении
2-8-2; - в центральной колонке размещена большая красная кнопка
Показать время; - реализовано модальное окно Bootstrap;
- в заголовке модального окна выведена строка
Выполнил: Рыбаков Ярослав; - в теле модального окна через
Luxonвыводится текущая дата и время в форматеdd.MM.yyyy HH:mm:ss; - добавлены два способа закрытия окна: крестик в заголовке и кнопка
Закрытьвнизу справа; - подготовлен итоговый экран для скриншота с HTML-кодом и открытым модальным окном.
Структура проекта #
Основные файлы второго этапа:
labs/webpack2025/index.html
Подключение библиотек #
Для выполнения второго этапа были подключены Bootstrap 5 и Luxon.
Скриншот подключения и установки библиотек:

Подключение Luxon
#
Библиотека Luxon используется для получения текущей даты и времени. В коде страницы она подключается отдельно, а значение даты и времени формируется в формате:
luxon.DateTime.local().setLocale("ru").toFormat("dd.LL.yyyy HH:mm:ss");Пример результата:
25.10.2025 18:06:07Подключение Bootstrap 5 #
Для оформления страницы использованы классы Bootstrap 5, а для всплывающего окна задействован стандартный компонент Modal.
На странице реализована сетка с тремя колонками по схеме 2-8-2. Центральная колонка является основной рабочей областью приложения.
В ней размещена большая красная кнопка:
Показать времяКнопка занимает всю ширину центральной колонки благодаря классам Bootstrap:
<button id="showTimeButton" type="button" class="btn btn-danger btn-lg w-100 h-100 py-3">
Показать время
</button>Модальное окно #
После нажатия на кнопку открывается всплывающее окно Bootstrap.
В заголовке указано имя исполнителя:
Выполнил: Рыбаков ЯрославВ теле модального окна крупным шрифтом выводится текущее время, полученное через Luxon.
Окно можно закрыть двумя способами:
- крестиком в правом верхнем углу;
- кнопкой
Закрытьв правом нижнем углу.
Проверка результата #
После подготовки страницы была выполнена проверка результата в браузере.
Скриншот запуска и проверки страницы:

Итоговый скриншот #
Скриншот страницы:

Последовательность выполненных действий #
Последовательность работы над вторым этапом была следующей:
- Создание HTML-страницы с Bootstrap-разметкой.
- Размещение трёх колонок по схеме
2-8-2. - Добавление большой красной кнопки
Показать времяв центральную колонку. - Подключение
Luxonдля вывода текущей даты и времени. - Подключение
Bootstrap 5для сетки, кнопки и модального окна. - Реализация всплывающего окна с ФИО исполнителя.
- Настройка закрытия окна крестиком и кнопкой
Закрыть. - Проверка итогового интерфейса в браузере.
- Подготовка скриншота с HTML-кодом и внешним видом приложения.
Вывод #
Таким образом, была выполнена базовая сборка JavaScript-приложения через Webpack, проверен вывод времени с помощью библиотеки Luxon, добавлено оформление Bootstrap 5 через CDN, подготовлен альтернативный вариант сборки через Vite, а также выполнен запуск приложения в Docker-контейнере на базе alpine-образа. На втором этапе приложение было встроено в Bootstrap-шаблон с сеткой 2-8-2, большой красной кнопкой и модальным окном.
Этап 3 #
Тема #
Сборка Bootstrap-приложения с Luxon через Vite без использования стороннего CDN.
Что было сделано #
На третьем этапе был создан отдельный проект labs/stage3-vite-bootstrap, в котором вся страница собирается локально через Vite.
В работе были выполнены следующие шаги:
- создана новая папка проекта
labs/stage3-vite-bootstrap; - настроен
Viteдля локальной разработки и production-сборки; - установлены зависимости
bootstrap,luxon,viteиsass; - перенесён интерфейс второго этапа с колонками
2-8-2, большой красной кнопкой и модальным окном; - подключен
Luxonчерез npm и встроен вывод текущей даты и времени; - для уменьшения размера бандла подключены только нужные SCSS-модули Bootstrap и только JS-модуль
Modal; - выполнена production-сборка и зафиксирован размер итогового бандла;
- собранная страница опубликована внутри статического сайта.
Структура проекта #
Основные файлы третьего этапа:
labs/stage3-vite-bootstrap/package.jsonlabs/stage3-vite-bootstrap/vite.config.jslabs/stage3-vite-bootstrap/index.htmllabs/stage3-vite-bootstrap/src/main.jslabs/stage3-vite-bootstrap/src/style.scss
Команды проекта #
Основные команды для запуска и сборки:
npm install
npm run dev
npm run buildСкриншот установки зависимостей:

Скрипты в package.json:
"scripts": {
"dev": "vite --host 0.0.0.0 --port 3002 --open /",
"build": "vite build",
"preview": "vite preview --host 0.0.0.0 --port 4173"
}Подключение Luxon
#
В src/main.js используется импорт библиотеки:
import { DateTime } from "luxon";Текущее время формируется так:
DateTime.local().setLocale("ru").toFormat("dd.MM.yyyy HH:mm:ss");Подключение Bootstrap 5 #
Bootstrap в третьем этапе подключается не через CDN, а как зависимость проекта. Для уменьшения размера бандла в src/style.scss импортированы только нужные части Bootstrap:
- сетка;
- контейнеры;
- кнопки;
- модальное окно;
- утилиты и базовые стили.
В JavaScript подключён только модуль:
import Modal from "bootstrap/js/dist/modal";Размер полученного бандла #
Результат команды npm run build:
dist/index.html 2.02 kB │ gzip: 0.79 kB
dist/assets/index-WBhvOPX7.css 116.67 kB │ gzip: 16.86 kB
dist/assets/index-CW1BmQW8.js 94.15 kB │ gzip: 29.47 kBСкриншот запуска сборки:

Скриншот результата сборки с размерами файлов:

Итоговый production-бандл состоит из CSS-файла размером 116.67 kB и JavaScript-файла размером 94.15 kB. Общий размер основных файлов сборки без gzip: 116.67 kB + 94.15 kB = 210.82 kB. Общий размер в gzip: 16.86 kB + 29.47 kB = 46.33 kB.
Скриншот интерфейса #
Скриншот UI-страницы:

Ссылка на репозиторий #
Исходные файлы проекта размещены в репозитории:
Репозиторий с исходными файлами этапов 2 и 3
Ссылка на опубликованную страницу #
Собранное приложение опубликовано на статическом сайте:
Опубликованное приложение этапа 3
Скриншот страницы, открытой из опубликованного портфолио:

Последовательность выполненных действий #
- Создание новой папки проекта для третьего этапа.
- Настройка
Viteиpackage.json. - Установка зависимостей
bootstrap,luxon,viteиsass. - Перенос интерфейса второго этапа в новый Vite-проект.
- Подключение
Luxonчерез npm для обновления времени. - Подключение Bootstrap 5 без CDN, с выборочным импортом модулей.
- Сборка проекта через
npm run build. - Фиксация размера полученного бандла.
- Публикация собранной страницы на статическом сайте.
Вывод #
На третьем этапе Bootstrap-приложение с Luxon было полностью переведено на сборку через Vite. Bootstrap больше не подключается через сторонний CDN: интерфейс собирается локально из зависимостей проекта. Для уменьшения размера бандла были импортированы только необходимые части Bootstrap, после чего собранная страница была опубликована как отдельное статическое приложение.