В одной из предыдущих статей этого цикла мы узнали о том, что прежде чем приступить к написанию приложения, необходимо получить мнение других людей. После того как мы прояснили все сомнения с заинтересованными сторонами проекта, мы готовы превратить наш прототип в приложение JavaScript.
Над чем мы работаем?
Цель этой серии — показать все аспекты современного фронтенд-приложения на JavaScript на максимально простом примере:
Встроенный JavaScript
Первый шаг — нам нужно сделать так, чтобы какая-то часть нашего HTML-прототипа выполнялась с помощью JavaScript. Самый простой способ сделать это — использовать inline JS. Итак, наш index.html
становится:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hello World!</title>
</head>
<body>
<script type="text/javascript">
const element = document.createElement("h1");
element.innerText = "Hello World!";
document.body.appendChild(element);
</script>
</body>
</html>
Если вам интересно, что здесь произошло и как получается та же страница, что и раньше, вы можете прочитать больше о манипуляции DOM здесь.
Загрузка JS-файла
Размещение всего кода в файле index.html
не приведет к масштабированию — это очень быстро станет неудобным. Вместо этого давайте разобьем наш код на отдельные HTML и JS файлы:
script.js
:
const element = document.createElement("h1");
element.innerText = "Hello World!";
document.body.appendChild(element);
и обновленный index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hello World!</title>
</head>
<body>
<script type="text/javascript" src="./script.js"></script>
</body>
</html>
Это намного лучше!
Модуль JavaScript
В предыдущем шаге мы включили JavaScript традиционным способом — по одному файлу за раз. Современный JS позволяет нам писать код в модулях, которые определяют свои зависимости внутри, а браузеры сами определяют пути и загружают необходимые файлы. На данный момент эта возможность уже доступна почти в 95% браузеров на рынке (источник).
Давайте используем это в нашем приложении!
Сначала мы перенесем сообщение в отдельный файл greeting.js
:
export const greetingMessage = "Hello World!";
Обратите внимание, что мы используем export
перед const greetingMessage...
. Это дает понять JS, что эта константа должна быть доступна для импорта из других файлов.
Теперь мы можем легко импортировать это значение в любое место нашего проекта. Мы сделаем то же самое для обновленного script.js
:
import { greetingMessage } from "./greeting.js";
const element = document.createElement("h1");
element.innerText = greetingMessage;
document.body.appendChild(element);
Наименее необходимым обновлением является изменение атрибута type
в импорте в index.html
:
<title>Hello World!</title>
</head>
<body>
- <script type="text/javascript" src="./script.js"></script>
+ <script type="module" src="./script.js"></script>
</body>
</html>
Вы можете прочитать больше о нативных ES-модулях в этой статье.
Превращение в пакет npm
npm — это менеджер пакетов, который позволяет нам легко загружать пакеты сообщества для использования в нашем приложении. Он был создан для Node, серверной части JavaScript, но несколько лет назад он стал стандартом и для фронтенд-части JavaScript. В нашем случае он позволит просто настроить сценарий сборки и зависимости сборки.
Чтобы инициализировать пакет, вы можете запустить npm init
в папке с пакетом:
$ npm init
This utility will walk you through creating a `package.json` file.
It covers only the most common items, and it tries to guess sensible defaults.
See `npm help init` for definitive documentation on these fields and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (hello-world)
version: (1.0.0)
…
npm предоставляет разумные значения по умолчанию, поэтому в большинстве случаев вы можете выбрать предложенное значение. После успешного выполнения этой команды в вашей папке будет создан package.json
.
Webpack
Использование собственных ES-модулей работает в большинстве браузеров, но в реальных проектах вы все равно увидите, что JS подключается как часть процесса сборки. Почему? Есть много вещей, которые вы обычно хотите сделать в проекте:
- скомпилировать TypeScript или любой другой язык, который компилируется в JavaScript
- уменьшить количество файлов, доставляемых пользователям
- и в то же время иметь тонкий контроль над размером кусков, на которые мы разбиваем наш код
- использовать некоторые методы борьбы с кэшем — например, добавлять кэш файла к его имени.
Более подробно я обсуждаю причины здесь.
Самым популярным JS бандлером для JavaScript является Webpack. Давайте добавим его в наш проект! Сначала нам нужно установить его:
$ npm install webpack --save-dev
added 77 packages, and audited 78 packages in 7s
9 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
В случае успеха эта команда загрузит файлы webpack и добавит их в зависимости разработки в package.json
.
Игнорирование Git
Если вы используете git-as, вам следует установить это значение в .gitingore
:
node_modules
dist
Это сохранит обе папки вне репозитория:
Build
Чтобы начать использовать webpack, в том же файле package.json
добавим build
в раздел scripts
:
{
…
"scripts": {
"build": "webpack --mode production",
…
},
--mode production
явно устанавливает способ, которым Webpack должен собирать код — так мы можем избежать появления следующих предупреждений в консоли:
WARNING in configuration
The 'mode' option has not been set, webpack will fall back to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
Мы запускаем сборку с помощью npm run build
. При первом запуске будут установлены некоторые дополнительные зависимости:
$ npm run build
> hello-world@1.0.0 build
> webpack
CLI for webpack must be installed.
webpack-cli (https://github.com/webpack/webpack-cli)
We will use "npm" to install the CLI via "npm install -D webpack-cli".
Do you want to install 'webpack-cli' (yes/no): yes
Первая сборка завершится неудачей, потому что конфигурация webpack по умолчанию ищет код в папке ./src
. Мы можем исправить это:
- переименование
script.js
вindex.js
, - переместим оба файла
index.js
иgreeting.js
в новую папку./src
.
Чтобы использовать наш собранный код, давайте обновим index.html
со следующим изменением:
<title>Hello World!</title>
</head>
<body>
- <script type="module" src="./script.js"></script>
+ <script src="./dist/main.js"></script>
</body>
</html>
Вы можете найти мой код на этом этапе здесь.
Генерация index.html
Некоторые JS бандлеры используют индексные файлы в качестве конфигурации для определения того, какие файлы должны быть собраны. В Webpack обычно все наоборот: конфигурационный файл отвечает за определение того, как должен быть сгенерирован индексный файл. Поначалу это может быть немного запутанным, но это хорошо работает, когда мы переходим к серверу разработки на следующем шаге. Так что давайте настроим его здесь!
Добавление webpack.config.js
Сначала мы добавим файл конфигурации webpack.config.js
:
module.exports = {
mode: "production",
};
Это изменение позволяет нам упростить сценарий сборки в package.json
:
"scripts": {
- "build": "webpack --mode production",
+ "build": "webpack",
"test": "echo "Error: no test specified" && exit 1"
},
поскольку режим уже задан в конфигурации. На этом этапе сборка должна работать так же, как и раньше.
Код примера.
Добавление html-webpack-plugin
Далее нам нужно добавить еще одну зависимость для разработки:
$ npm install --save-dev html-webpack-plugin
Чтобы использовать его, нам нужно обновить webpack.config.js
до:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "production",
plugins: [new HtmlWebpackPlugin({ title: "Hello World!" })],
};
Теперь удалите старый index.html
.
В результате окончательной сборки будет создано два файла:
$ npm run build
> hello-world@1.0.0 build
> webpack
asset index.html 215 bytes [emitted]
asset main.js 116 bytes [compared for emit] [minimized] (name: main)
orphan modules 47 bytes [orphan] 1 module
./src/index.js + 1 modules 218 bytes [built] [code generated]
webpack 5.74.0 compiled successfully in 157 ms
и его результат можно найти в папке dist
:
$ ls dist
index.html main.js
Посмотрите на код для сравнения.
Сервер разработки
Чтобы помочь в разработке, Webpack предоставляет сервер разработки.
Почему мы должны беспокоиться? Сервер разработки:
- следит за изменениями файлов
- перестраивается каждый раз, когда что-то изменяется
- перезагружает приложение в вашем браузере
Он легко сэкономит вам несколько секунд каждый раз, когда вы вносите изменения в код — а это может быть сотни раз в течение рабочего дня.
Его легко настроить: просто добавьте скрипт start
в package.json
:
"main": "src/index.js",
"scripts": {
"build": "webpack",
+ "start": "webpack serve",
"test": "echo "Error: no test specified" && exit 1"
},
При первом запуске этой команды Webpack предложит установить необходимую зависимость-webpack-dev-server
:
$ npm run start
> hello-world@1.0.0 start
> webpack serve
[webpack-cli] For using the 'serve' command you need to install: 'webpack-dev-server' package.
[webpack-cli] Would you like to install the 'webpack-dev-server' package? (That will run 'npm install -D webpack-dev-server') (Y/n) Y
Давайте посмотрим на это в действии:
$ npm run start
> hello-world@1.0.0 start
> webpack serve
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/
…
Когда вы запустите его на своей машине, вы можете посетить URL и проверить, действительно ли он перезагружается при любых изменениях, которые вы сохраняете в своих файлах.
Ознакомьтесь с эталонным кодом.
Хотите узнать больше о webpack?
У меня есть курс по webpack, доступный на Udemy. Там вы найдете аналогичный пошаговый подход.
Поделитесь своим проектом
Надеюсь, технический поворот не отпугнул вас, и вы продолжаете работать над своим проектом! Поделитесь в комментариях своими успехами или трудностями.