Это перевод статьи Питера Янга Modern JavaScript Explained For Dinosaurs. Она познакомит вас с инфраструктурой современной фронтэнд-разработки.
Наш курс JS: Настройка окружения выполняет схожую задачу, но чуть глубже, последовательно и без привязки только к фронтэнду. Этим темам также посвящена значительная часть первого проекта в JS Backend и JS Frontend. Там мы на деле устанавливаем и настраиваем среду для разработки, проверки, публикации, сборки и запуска своих приложений.
Изучать современный JavaScript — болезненно, если вы не знакомы с ним с самого его рождения. Экосистема разрастается и меняется с такой скоростью, что сложно разобраться с тем, какие проблемы пытаются решить разные инструменты. Я начал программировать в 1998 году, но к серьёзному изучению JavaScript приступил только в 2014. В то время я помню как анализировал Browserify и изумлённо смотрел на его слоган:
Browserify позволяет запрашивать (require) модули в браузере, объединяя все зависимости.
Можно сказать, я не понимал ни слова в этом предложении, и с трудом осознавал, насколько это может быть полезно мне, как разработчику.
Цель этой статьи — показать исторический контекст развития инструментов JavaScript до их уровня в 2017. Начнём с самых первых моментов и построим шаблон веб-сайта, как бы это сделали динозавры — без инструментов, чистый HTML и JavaScript. Затем мы будем пошагово вводить различные инструменты, чтобы на практике видеть, какие задачи они решают — поочерёдно. Благодаря историческому контексту у вас будет больше возможностей изучить и лучше адаптироваться к бесконечно меняющемуся JavaScript. Давайте начнём!
Использование JavaScript старомодным способом
Давайте начнём со старомодного веб-сайта, написанного на HTML и JavaScript, что включает загрузку и помещение ссылок на файлы вручную. Вот простой index.html файл, который ссылается на JavaScript файл:
Строчка <script src="index.js"></script> ссылается на отдельный JavaScript-файл с названием index.js в той же директории:
Это всё, что вам нужно, чтобы сделать веб-сайт!
Продолжайте углублять свои знания
На Хекслете есть блок «Треки», где собраны курсы для опытных разработчиков, которые хотят новых знаний про разработку.
Теперь давайте предположим, что вы хотите добавить библиотеку, которую написал кто-то другой, вроде moment.js (библиотеку, которая помогает превращать даты в понятные для чтения человеком). Например, вы можете использовать функцию moment в JavaScript вот так:
Но только при условии, что вы включите moment.js в код своего веб-сайта! На домашней странице moment.js вы найдёте такие инструкции:
Хмм, как много всего происходит в секции Install справа. Но давайте пока проигнорируем это — мы можем добавить moment.js на веб-сайт, загрузив файл moment.min.js в ту же директорию и включив его в файл index.html.
Заметьте, что moment.min.js загружается перед index.js, а это значит вы можете использовать функцию moment в index.js таким способом:
И вот таким образом мы раньше делали веб-сайты с JavaScript-библиотеками! Положительный момент в том, что всё было достаточно легко понимать. Отрицательный — было муторно искать и загружать новые версии библиотек каждый раз, когда они обновлялись.
Использование пакетного менеджера JavaScript (npm)
Начиная примерно с 2010 появилось несколько конкурирующих пакетных менеджеров JavaScript для автоматизации процесса загрузки и обновления библиотек из центрального репозитория. Bower был, возможно, самым популярным в 2013, но в итоге, примерно в 2015 его настиг npm. (Стоит отметить, что с конца 2016 yarn привлёк много внимания, как альтернатива интерфейсу npm, но он всё ещё использует npm пакеты).
Заметьте, что npm был изначально пакетным менеджером, созданным специально для node.js — среды исполнения JavaScript, предназначенной для запуска на сервере, а не во фронтенде. Что делает его очень странным выбором для фронтенд-пакетного менеджера JavaScript библиотек, предназначенных запускаться в браузере.
Заметьте: Использование пакетного менеджера обычно требует работы с командной строкой, что в прошлом никогда не требовалось от фронтенд-разработки. Если вы никогда ей не пользовались, можете прочитать этот туториал, чтобы составить представление о том, как начать. Так или иначе, умение пользоваться командной строкой — важная часть современного JavaScript (и ещё она открывает двери в другие области разработки).
На Хекслете как раз есть бесплатный курс Bash: Основы командной строки, — прим. ред.
Давайте взглянем как использовать npm, чтобы установить пакет moment.js автоматически, вместо того, чтобы загружать его вручную. Если у вас установлен node.js, у вас уже есть и npm, а это значит, что вы можете через командную строку перейти к папке, в которой находится файл index.html и ввести:
$ npm init
Будет выведено несколько вопросов (можно оставить умолчания: нажимать "Enter" после каждого вопроса) и сгенерируется новый файл с именем package.json. Это конфигурационный файл, который npm использует, чтобы хранить всю информацию о проекте. С настройками по умолчанию содержимое package.json должно выглядеть подобно этому:
{
"name": "your-project-name",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Чтобы установить JavaScript-пакет moment.js, мы теперь можем следовать инструкциям npm на их домашней странице, введя в командную строку:
$ npm install moment --save
Эта команда выполняет две задачи: в начале она загружает весь код из пакета moment.js в папку, называемую node_modules. Потом автоматически модифицирует файл package.json, чтобы мониторить moment.js как зависимость (зависимости — это пакеты и библиотеки, от которых зависит наше приложение).
{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
}
}
Это будет полезно в будущем, когда вместо расшаривания проекта с остальными — а именно папки node_modules (которая может сильно разрастись) — вам понадобится расшарить только файл package.json, и другие разработчики смогут установить необходимые пакеты автоматически с помощью команды npm install.
Читайте также
Как устроен функциональный диалект Лиспа Clojure и почему использующие его программисты восхищаются им
Теперь мы больше не должны загружать moment.js вручную с веб-сайта, потому что можем сделать это автоматически и обновить, используя npm. Заглянув внутрь папки node_modules можно увидеть файл moment.min.js в директории node_modules/moment/min. Это значит, мы можем сослаться на npm загружаемую версию moment.min.js в файле index.html, как видно ниже:
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Example</title>
<script src="node_modules/moment/min/moment.min.js"></script>
<script src="index.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html>
Хорошие новости в том, что теперь мы можем использовать npm, чтобы загружать и обновлять пакеты с помощью командной строки. Плохие в том, что прямо сейчас мы обшариваем папку node_modules в поисках местоположения каждого пакета и вручную включаем их в HTML. Это достаточно неудобно, и следующее, что мы сделаем, это посмотрим как автоматизировать и этот процесс.
Использование модульного упаковщика JavaScript (webpack)
У большинства языков программирования есть способ импорта кода из одного файла в другой. JavaScript изначально не имел этой функции, потому что был создан только для работы в браузере, без доступа к файловой системе компьютера клиента (по соображениям безопасности). Поэтому очень долго организация кода JavaScript в нескольких файлах требовала загрузки каждого файла с глобальными переменными.
Это именно то, что мы делаем в приведенном выше примере с moment.js — весь moment.min.js файл загружается в HTML, что определяет глобальную переменную moment, которая становится доступна любому файлу, загруженному после moment (независимо от того, нужен ли к нему доступ).
В 2009 году был запущен проект CommonJS, определяющий экосистему для JavaScript за пределами браузера. Большая часть CommonJS была спецификацией для модулей и она, наконец, позволила JavaScript делать импорт и экспорт между файлами, как в большинстве языков программирования, без использования глобальных переменных. Самая известная реализация модулей CommonJS — это node.js.
Как говорилось ранее, node.js — это среда выполнения JavaScript, предназначенная для запуска на сервере. Вот как выглядел более ранний пример с использованием модулей node.js. Вместо того, чтобы загружать все moment.min.js с помощью HTML script тега, вы можете загрузить его напрямую в файле JavaScript вот так:
// index.js
var moment = require('moment');
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());
Опять же, так работает загрузка модуля в node.js, и это здорово, поскольку node.js — это серверный язык с доступом к файловой системе компьютера. Node.js также знает расположение каждого пути модуля npm, поэтому вместо необходимости писать require('./node_modules/moment/min/moment.min.js), можно использовать require('moment') — чудно.
Всё это подходит для node.js, но если вы попытаетесь использовать написанный выше код в браузере, вы получите сообщение об ошибке, в котором будет указано require not defined (не определено). Браузер не имеет доступа к файловой системе, это значит, что загрузка модулей таким способом очень проблематична — загрузка файлов должна выполняться динамически, синхронно (что замедляет исполнение), или асинхронно (что может вызвать проблемы с синхронизацией).
Тут вступает в действие упаковщик модулей. Упаковщик модулей JavaScript — это инструмент, который решает задачу с помощью этапа сборки (он имеет доступ к файловой системе) для создания конечно результата, совместимого с браузером (уже не нужен доступ к файловой системе). В этом случае нам нужен упаковщик модулей для поиска всех утверждений require (а это недопустимый синтаксис JavaScript для браузера) и замены их актуальным содержимым каждого требуемого файла. Конечный результат — один бандл (упакованный файл) JavaScript (без инструкций require)!
Самым популярным упаковщиком модулей был Browserify, он вышел в 2011 году и первым использовал стиль node.js для require во фронтенде (что, по сути, помогло npm стать самым используемым менеджером пакетов для фронтенда). Примерно в 2015 году более популярным стал упаковщик модулей webpack (его подпитывала популярность фронтенд-фреймворка React, который максимально пользовался различными функциями webpack).
Давайте посмотрим, как использовать webpack, чтобы заставить показанный выше пример с require('moment') работать в браузере. Сначала нам нужно установить webpack в проект. Сам webpack — это npm-пакет, поэтому мы можем установить его из командной строки:
$ npm install webpack --save-dev
Обратите внимание на аргумент --save-dev, он сохраняется как development-зависимость. Это означает, что это пакет вам нужен в среде разработки, но не на production-сервере. Это можно наглядно увидеть в файле package.json, который был автоматически обновлен:
{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
},
"devDependencies": {
"webpack": "^3.7.1"
}
}
Теперь у нас есть webpack, установленный в папку node_modules, как один из пакетов. Вы можете вызывать webpack из командной строки:
$ ./node_modules/.bin/webpack index.js bundle.js
Эта команда запустит инструмент webpack, который был установлен в папке node_modules, начнет с файла index.js, найдет любые require утверждения и заменит их соответствующим кодом, чтобы создать один выходной файл с именем bundle.js.
Это значит, что мы больше не будем использовать index.js в браузере, так как в нем содержатся недопустимые require утверждения. Вместо этого мы будем использовать вывод bundle.js в браузере, что должно будет отразиться в файле index.html:
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Example</title>
<script src="bundle.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html>
Если вы обновите браузер, увидите, что всё работает, как работало раньше!
Обратите внимание, что нам нужно будет запускать команду webpack каждый раз, когда мы будем менять index.js. Это утомительно и станет ещё утомительней, когда мы станем использовать более сложные фичи webpack (например, генерацию source maps, чтобы помочь отлаживать исходный код из преобразованного кода). Webpack может считывать параметры из файла конфигурации в корневом каталоге проекта с именем webpack.config.js, который в нашем случае будет выглядеть так:
// webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js'
}
};
Теперь каждый раз, когда мы меняем index.js, мы можем запускать webpack с помощью команды:
$ ./node_modules/.bin/webpack
Нам больше не нужно указывать параметры index.js и bundle.js, так как webpack загружает эти параметры из файла webpack.config.js. Уже лучше, но все же утомительно вводить эту команду при каждом изменении кода — позже мы сделаем этот процесс более плавным.
В целом, может казаться, что тут нет ничего особенного, но в этой рабочей последовательности есть несколько огромных плюсов. Мы больше не загружаем внешние скрипты через глобальные переменные. Любые новые библиотеки JavaScript будут добавляться через require в JavaScript, в отличие от добавления новых тегов <script> в HTML. Наличие одного бандла часто лучше для производительности. И теперь, когда мы добавили шаг сборки, мы можем добавить несколько мощных фич в процесс разработки!
Также полезно:
Стоит ли учить JavaScript перспективы, ситуация на рынке труда, мнения экспертов
Transpiling для новых фич языка (babel)
"Transpiling" кода — преобразование исходного кода на одном языке в исходный код на другом схожем языке. Это важный сегмент frontend-разработки: поскольку браузеры медленно добавляют новые фичи, были созданы новые языки с экспериментальными функциями, которые преобразуются (transpile) в совместимый с браузерами код.
Для CSS есть Sass, Less и Stylus. Для JavaScript самым популярным транспайлером какое-то время был CoffeeScript (вышел где-то в 2010 году), а в настоящее время большинство людей используют babel или TypeScript. CoffeeScript — это язык, ориентированный на улучшение JavaScript, через значительное его изменение — опциональные круглые скобки, отступы и т. д.
Babel — это не новый язык, а транспайлер, который преобразует JavaScript нового поколения с фичами, недоступными всем браузерам (ES2015 и выше) в старшую, более совместимую версию JavaScript (ES5). TypeScript — это язык, который практически идентичен JavaScript нового поколения, но в него добавлена опциональная статическая типизация. Многие люди предпочитают использовать babel, потому что он ближе всего к чистому JavaScript.
Давайте рассмотрим пример использования babel с нашим существующим этапом сборки webpack. Сначала мы устанавливаем babel (а это npm-пакет) в проект из командной строки:
$ npm install babel-core babel-preset-env babel-loader --save-dev
Обратите внимание, что мы устанавливаем 3 отдельных пакета как dev зависимости — babel-core — основная часть babel, babel-preset-env — пресет, определяющий, какие новые фичи JavaScript преобразовывать, а babel-loader — это пакет, позволяющий babel работать с webpack. Мы можем настроить webpack для использования babel-loader, отредактировав файл webpack.config.js, как показано ниже:
// webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}
]
}
};
Этот синтаксис может ломать мозг (к счастью, это не то, что мы будем часто редактировать). Мы просим webpack искать любые .js-файлы (исключая те, что в папке node_modules) и применять преобразование babel с помощью babel-loader через пресет babel-preset-env. Здесь вы можете узнать больше о конфигурационном синтаксисе webpack.
У нас есть пример полностью настроенного пакета со всеми описанными штуками — Hexlet Boilerplates: nodejs, — прим. ред.
Теперь, когда все настроено, мы можем начать писать фичи ES2015 в JavaScript! Ниже пример ES2015 template string в файле index.js:
Мы также можем использовать импорты ES2015 вместо require для загрузки модулей. Это то, что вы сегодня увидите во многих базах кода:
// index.js
import moment from 'moment';
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());
var name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`);
В этом примере синтаксис import не сильно отличается от синтаксиса require, но import имеет дополнительную гибкость для более сложных случаев. Поскольку мы изменили index.js, нам нужно снова запустить webpack в командной строке:
$ ./node_modules/.bin/webpack
Теперь вы можете обновить index.html в браузере. На момент написания этой статьи большинство современных браузеров поддерживают все фичи ES2015, поэтому иногда трудно определить, выполнил ли свою работу babel. Вы можете протестировать его в браузере более старой версии, вроде IE9, или покопаться в bundle.js и найти строку транспайлированного кода:
// bundle.js
// ...
console.log('Hello ' + name + ', how are you ' + time + '?');
// ...
Здесь видно, как babel преобразовал интерполированную строку ES2015 в обычную конкатенацию чтобы сохранить совместимость с браузером. Хотя этот пример не слишком захватывающий, способность преобразовать код — очень мощный инструмент. В JavaScript есть некоторые интересные языковые фичи, вроде async/await, которые вы можете начать использовать прямо сейчас, чтобы писать код лучше. И хотя transpilation иногда выглядит утомительным и болезненным занятием, оно привело к резким улучшениям в языке за последние несколько лет, так как сегодня люди тестируют завтрашние фичи.
Мы почти закончили, но в нашем воркфлоу все еще есть неотполированные грани. Если нас беспокоит производительность, мы должны минимизировать бандл-файл, что должно быть уже достаточно лёгкой задачей, поскольку этап сборки уже встроен. Нам также нужно повторно запускать команду webpack каждый раз, когда мы меняем JavaScript. Поэтому следующее, что мы рассмотрим — это несколько удобных инструментов для решения этих проблем.
Также полезно:
С чего начать изучение JavaScript и как это делать эффективно
Использование task runner (скрипты npm)
Теперь, когда мы вложились в использование этапа сборки для работы с модулями JavaScript, имеет смысл использовать task runner, инструмент, который автоматизирует различные части процесса сборки. Для фронтенд разработки в задачи включена минимизация кода, оптимизация изображений, прогон тестов и т.д.
В 2013 году Grunt был самым популярным таск-раннером во фронтенде, чуть позже появился Gulp. Оба полагаются на плагины, которые охватывают другие инструменты командной строки. Сегодня более популярный выбор — использовать возможности скриптов, встроенных в сам менеджер пакетов npm, который не использует плагины, а напрямую работает с другими инструментами командной строки.
Давайте напишем несколько npm-скриптов, чтобы упростить использование webpack. Это простое изменение файла package.json как в примере ниже:
{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --progress -p",
"watch": "webpack --progress --watch"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"webpack": "^3.7.1"
}
}
Здесь мы добавили два новых скрипта, build и watch. Чтобы запустить скрипт сборки, вы можете ввести в командной строке:
$ npm run build
Это запустит webpack (используя конфигурацию из webpack.config.js, которую мы сделали ранее) с опцией --progress, чтобы показать прогресс в процентах и опцию -p, чтобы минимизировать код для production. Чтобы запустить сценарий watch:
$ npm run watch
Используется опция -watch для автоматического перезапуска webpack каждый раз, когда изменяется любой JavaScript-файл, что отлично подходит для разработки.
Обратите внимание, что скрипты в package.json могут запускать webpack без указания полного пути ./node_modules/.bin/webpack, так как node.js знает расположение каждого пути модуля npm. Это круто! Мы можем всё сделать ещё круче, установив webpack-dev-server, отдельный инструмент, который предоставляет простой веб-сервер с живой перезагрузкой. Чтобы установить его как зависимость, введите команду:
$ npm install webpack-dev-server --save-dev
Затем добавьте скрипт npm в package.json:
{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --progress -p",
"watch": "webpack --progress --watch",
"server": "webpack-dev-server --open"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"webpack": "^3.7.1"
}
}
Теперь вы можете запустить свой dev-сервер выполнив команду:
$ npm run server
Это автоматически откроет сайт index.html в вашем браузере с адресом localhost: 8080 (по умолчанию). Каждый раз, когда вы будете менять JavaScript в index.js, webpack-dev-server будет перестраивать собственный упакованный JavaScript и автоматически обновлять браузер. Это удивительно полезная экономия времени, поскольку у вас есть возможность сосредоточиться на коде, а не на постоянном переключении контекстов между кодом и браузером, чтобы увидеть изменения.
И это только верхушка, есть еще много вариантов с webpack и webpack-dev-server (о которых вы можете прочитать здесь). Конечно, вы можете создавать npm-скрипты для выполнения других задач, таких как преобразование Sass в CSS, сжатие изображений, прогон тестов всего, к чему имеет доступ инструмент командной строки — всё имеет смысл. Также есть несколько отличных расширенных опций и манёвров с самими скриптами npm. Это выступление Кейт Хадсон — отличное начало.
Вывод
Вот, что представляет из себя современный JavaScript в двух словах. Мы перешли с простого HTML и JS к менеджеру пакетов, чтобы автоматически загружать сторонние пакеты, упаковщику модулей для создания единого скрипт-файла, транспайлеру для использования будущих фич JavaScript и таск раннеру для автоматизации различных частей процесса сборки. Определенно тут много движимых частей, особенно для новичков. Веб-разработка когда-то была отличным стартом для новичков в программировании, именно потому что было легко начать работать; сейчас это довольно сложно, особенно потому, что различные инструменты склонны к быстрым изменениям.
Тем не менее, всё не так плохо, как кажется. Всё уравновешивается, особенно после внедрениея экосистемы node, как стабильного способа работы с фронтендом. Довольно приятно использовать npm в качестве менеджера пакетов, require или import для модулей, и npm-скрипты для запуска задач. Это значительно упрощает рабочий процесс по сравнению с тем, что было год или два назад!
Дополнительная польза и для начинающих, и для опытных разработчиков в том, что фреймворки сегодня содержат инструменты, которые упрощают процесс и начать теперь намного легче. У Ember есть ember-cli, что очень сильно повлияло на angular-cli от Angular, create-react-app от React, vue-cli от Vue и т. д. Все эти инструменты создают проект с полным набором того, что вам нужно — всё, что вам нужно сделать, это начать писать код. Тем не менее, эти инструменты не волшебные, они просто задают правильную базовую структуру, но вы можете оказаться в ситуации, когда вам потребуется выполнить дополнительную настройку с помощью webpack, babel и т.д. Поэтому очень важно понять, что делает каждый из этих инструментов, что мы и сделали в этой статье.
Работа с современным JavaScript — не всегда самая приятная штука, потому что он постоянно меняется и развивается со скоростью звука. Но, хоть иногда некоторые моменты и кажутся изобретением колеса, быстрая эволюция JavaScript продвинула инновации вроде горячей перезагрузки, инструмента статического анализа и отладки в контексте времени. Сегодня быть разработчиком — захватывающе, и я надеюсь, что эта информация может послужить путеводителем, который поможет вам в вашем путешествии!
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 20:38:35 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="YIIwqGM__EmZ-DaMpsOioyQNM47d-E6BruLyw8kK2gyPU_ufkUFRKS-7EhSqzFLU5AQeJNXPsCMTAmiXmw09Yg";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Описание современного JavaScript для динозавров</title>
<meta name="description" content="Изучать современный JavaScript — болезненно, если вы не знакомы с ним с самого его рождения. Экосистема разрастается и меняется с такой скоростью, что сложно разобраться с тем, какие проблемы пытаются решить разные инструменты. Я начал программировать в 1998 году, но к серьёзному изучению JavaScript">
<link rel="canonical" href="https://ru.hexlet.io/blog/posts/modern-js-explained">
<meta property="og:title" content="Описание современного JavaScript для динозавров">
<meta property="og:description" content="Изучать современный JavaScript — болезненно, если вы не знакомы с ним с самого его рождения. Экосистема разрастается и меняется с такой скоростью, что сложно разобраться с тем, какие проблемы пытаются решить разные инструменты. Я начал программировать в 1998 году, но к серьёзному изучению JavaScript приступил только в 2014.">
<meta property="og:image" content="https://ru.hexlet.io/vite/assets/blog_post-7eTyeLLt.webp">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="wj4BgUPvlU8mQQBDYa_yd8x8tBNXjR4o6dnsWvqXT0st78q2sZE4L5ACJNttoAIADHWZuV-64IpUOXYOqJCoJQ" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="/vite/assets/blog_post-7eTyeLLt.webp"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzcyNywicHVyIjoiYmxvYl9pZCJ9fQ==--2d5cbbf5c3b4a73ae4b2c50632305d78f5872e4d/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programmer-rafiki.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDA0MywicHVyIjoiYmxvYl9pZCJ9fQ==--e2c6c0775e2308e42fbc5dc592ba2db0470632ca/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programmer-rafiki.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDAxOSwicHVyIjoiYmxvYl9pZCJ9fQ==--84efd2b6854b7000046e9ce06e6be85d38af5ab8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/JavaScript%20frameworks-cuate.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzcyNSwicHVyIjoiYmxvYl9pZCJ9fQ==--2e84f5f94140ee4e22019ac479c290ef48c3fac8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzcwOSwicHVyIjoiYmxvYl9pZCJ9fQ==--03e50bbd408fef672ad099f7b2a258d80f54ad96/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-bro.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/blog/posts/show","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T20:38:35.782Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"857IZaFNFE_wYDZWMURRP3z9hqWA9pLXIfTbD3Bq56ocTwNSUzO5L0YjEs49S6FIvPSrD4jBbHWcFEFbIm0AxA","post":{"model_name":"BlogPost","category":{"id":4,"name":"Код","slug":"code","state":"published","created_at":"2016-08-23T13:33:44.258Z"},"creator":{"public_name":"Natalia Bass","id":43585,"is_tutor":false},"tags":[{"id":633,"slug":"javascript","name":"JavaScript"}],"id":165,"title":"Описание современного JavaScript для динозавров","slug":"modern-js-explained","state":"published","summary":"Изучать современный JavaScript — болезненно, если вы не знакомы с ним с самого его рождения. Экосистема разрастается и меняется с такой скоростью, что сложно разобраться с тем, какие проблемы пытаются решить разные инструменты. Я начал программировать в 1998 году, но к серьёзному изучению JavaScript приступил только в 2014. В то время я помню как анализировал [Browserify](http://browserify.org/) и изумлённо смотрел на его слоган:\r\n\r\n> Browserify позволяет запрашивать (require) модули в браузере, объединяя все зависимости.\r\n\r\nМожно сказать, я не понимал ни слова в этом предложении, и с трудом осознавал, насколько это может быть полезно мне, как разработчику.\r\n\r\nЦель этой статьи — показать исторический контекст развития инструментов JavaScript до их уровня в 2017. Начнём с самых первых моментов и построим шаблон веб-сайта, как бы это сделали динозавры — без инструментов, чистый HTML и JavaScript. Затем мы будем пошагово вводить различные инструменты, чтобы на практике видеть, какие задачи они решают — поочерёдно. Благодаря историческому контексту у вас будет больше возможностей изучить и лучше адаптироваться к бесконечно меняющемуся JavaScript. Давайте начнём!","votes_count":12,"created_at":"2017-11-03T13:50:40.417Z","published_at":"2017-11-07T08:12:45.206Z","body":"Это перевод статьи Питера [Янга Modern JavaScript Explained For Dinosaurs](https://medium.com/@peterxjang/modern-javascript-explained-for-dinosaurs-f695e9747b70). Она познакомит вас с инфраструктурой современной фронтэнд-разработки.\r\n\r\nНаш курс [JS: Настройка окружения](https://ru.hexlet.io/courses/js-setup-environment) выполняет схожую задачу, но чуть глубже, последовательно и без привязки только к фронтэнду. Этим темам также посвящена значительная часть [первого проекта в JS Backend и JS Frontend](https://ru.hexlet.io/projects/). Там мы на деле устанавливаем и настраиваем среду для разработки, проверки, публикации, сборки и запуска своих приложений.\r\n\r\n::programs\r\n\r\n---\r\n\r\nИзучать современный JavaScript — болезненно, если вы не знакомы с ним с самого его рождения. Экосистема разрастается и меняется с такой скоростью, что сложно разобраться с тем, какие проблемы пытаются решить разные инструменты. Я начал программировать в 1998 году, но к серьёзному изучению JavaScript приступил только в 2014. В то время я помню как анализировал [Browserify](http://browserify.org/) и изумлённо смотрел на его слоган:\r\n\r\n> Browserify позволяет запрашивать (require) модули в браузере, объединяя все зависимости.\r\n\r\nМожно сказать, я не понимал ни слова в этом предложении, и с трудом осознавал, насколько это может быть полезно мне, как разработчику.\r\n\r\nЦель этой статьи — показать исторический контекст развития инструментов JavaScript до их уровня в 2017. Начнём с самых первых моментов и построим шаблон веб-сайта, как бы это сделали динозавры — без инструментов, чистый HTML и JavaScript. Затем мы будем пошагово вводить различные инструменты, чтобы на практике видеть, какие задачи они решают — поочерёдно. Благодаря историческому контексту у вас будет больше возможностей изучить и лучше адаптироваться к бесконечно меняющемуся JavaScript. Давайте начнём!\r\n\r\n### Использование JavaScript старомодным способом \r\n\r\nДавайте начнём со старомодного веб-сайта, написанного на HTML и JavaScript, что включает загрузку и помещение ссылок на файлы вручную. Вот простой `index.html` файл, который ссылается на JavaScript файл:\r\n\r\n```html\r\n<!-- index.html -->\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <title>JavaScript Example</title>\r\n <script src=\"index.js\"></script>\r\n</head>\r\n<body>\r\n <h1>Hello from HTML!</h1>\r\n</body>\r\n</html>\r\n```\r\n\r\nСтрочка `<script src=\"index.js\"></script>` ссылается на отдельный JavaScript-файл с названием `index.js` в той же директории:\r\n\r\n```javascript\r\n// index.js\r\nconsole.log(\"Hello from JavaScript!\");\r\n```\r\n\r\nЭто всё, что вам нужно, чтобы сделать веб-сайт!\r\n\r\n\r\n> ### Продолжайте углублять свои знания\r\n> На Хекслете есть [блок «Треки»](https://ru.hexlet.io/tracks), где собраны курсы для опытных разработчиков, которые хотят новых знаний про разработку.\r\n\r\nТеперь давайте предположим, что вы хотите добавить библиотеку, которую написал кто-то другой, вроде [moment.js](http://momentjs.com/) (библиотеку, которая помогает превращать даты в понятные для чтения человеком). Например, вы можете использовать функцию `moment` в JavaScript вот так:\r\n\r\n```javascript\r\nmoment().startOf('day').fromNow(); // 20 hours ago\r\n```\r\n\r\nНо только при условии, что вы включите moment.js в код своего веб-сайта! На [домашней странице moment.js](http://momentjs.com/) вы найдёте такие инструкции:\r\n\r\n\r\n\r\nХмм, как много всего происходит в секции **Install** справа. Но давайте пока проигнорируем это — мы можем добавить moment.js на веб-сайт, загрузив файл `moment.min.js` в ту же директорию и включив его в файл `index.html`.\r\n\r\n```html\r\n<!-- index.html -->\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <title>Example</title>\r\n <link rel=\"stylesheet\" href=\"index.css\">\r\n <script src=\"moment.min.js\"></script>\r\n <script src=\"index.js\"></script>\r\n</head>\r\n<body>\r\n <h1>Hello from HTML!</h1>\r\n</body>\r\n</html>\r\n```\r\n\r\nЗаметьте, что `moment.min.js` загружается перед `index.js`, а это значит вы можете использовать функцию `moment` в `index.js` таким способом:\r\n\r\n```javascript\r\n// index.js\r\nconsole.log(\"Hello from JavaScript!\");\r\nconsole.log(moment().startOf('day').fromNow());\r\n```\r\n\r\nИ вот таким образом мы раньше делали веб-сайты с JavaScript-библиотеками! Положительный момент в том, что всё было достаточно легко понимать. Отрицательный — было муторно искать и загружать новые версии библиотек каждый раз, когда они обновлялись.\r\n\r\n### Использование пакетного менеджера JavaScript (npm)\r\n\r\nНачиная примерно с 2010 появилось несколько конкурирующих пакетных менеджеров JavaScript для автоматизации процесса загрузки и обновления библиотек из центрального репозитория. [Bower](https://bower.io/) был, возможно, самым популярным в 2013, но в итоге, примерно в 2015 его настиг [npm](https://www.npmjs.com/). (Стоит отметить, что с конца 2016 [yarn](https://yarnpkg.com/en/) привлёк много внимания, как альтернатива интерфейсу npm, но он всё ещё использует npm пакеты).\r\n\r\nЗаметьте, что npm был изначально пакетным менеджером, созданным специально для [node.js](https://nodejs.org/) — среды исполнения JavaScript, предназначенной для запуска на сервере, а не во фронтенде. Что делает его очень странным выбором для фронтенд-пакетного менеджера JavaScript библиотек, предназначенных запускаться в браузере.\r\n\r\n*Заметьте: Использование пакетного менеджера обычно требует работы с командной строкой, что в прошлом никогда не требовалось от фронтенд-разработки. Если вы никогда ей не пользовались, можете прочитать [этот туториал](https://www.learnenough.com/command-line-tutorial), чтобы составить представление о том, как начать. Так или иначе, умение пользоваться командной строкой — важная часть современного JavaScript (и ещё она открывает двери в другие области разработки).*\r\n\r\n*На Хекслете как раз есть бесплатный курс [Bash: Основы командной строки](https://ru.hexlet.io/courses/bash), — прим. ред.*\r\n\r\nДавайте взглянем как использовать npm, чтобы установить пакет moment.js автоматически, вместо того, чтобы загружать его вручную. Если у вас установлен node.js, у вас уже есть и npm, а это значит, что вы можете через командную строку перейти к папке, в которой находится файл `index.html` и ввести:\r\n\r\n```\r\n$ npm init\r\n```\r\n\r\nБудет выведено несколько вопросов (можно оставить умолчания: нажимать \"Enter\" после каждого вопроса) и сгенерируется новый файл с именем `package.json`. Это конфигурационный файл, который npm использует, чтобы хранить всю информацию о проекте. С настройками по умолчанию содержимое `package.json` должно выглядеть подобно этому:\r\n\r\n```\r\n{\r\n \"name\": \"your-project-name\",\r\n \"version\": \"1.0.0\",\r\n \"description\": \"\",\r\n \"main\": \"index.js\",\r\n \"scripts\": {\r\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n },\r\n \"author\": \"\",\r\n \"license\": \"ISC\"\r\n}\r\n```\r\n\r\nЧтобы установить JavaScript-пакет moment.js, мы теперь можем следовать инструкциям npm на их домашней странице, введя в командную строку:\r\n\r\n```\r\n$ npm install moment --save\r\n```\r\n\r\nЭта команда выполняет две задачи: в начале она загружает весь код из [пакета moment.js](https://unpkg.com/moment/) в папку, называемую `node_modules`. Потом автоматически модифицирует файл `package.json`, чтобы мониторить moment.js как зависимость (зависимости — это пакеты и библиотеки, от которых зависит наше приложение).\r\n\r\n```\r\n{\r\n \"name\": \"modern-javascript-example\",\r\n \"version\": \"1.0.0\",\r\n \"description\": \"\",\r\n \"main\": \"index.js\",\r\n \"scripts\": {\r\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n },\r\n \"author\": \"\",\r\n \"license\": \"ISC\",\r\n \"dependencies\": {\r\n \"moment\": \"^2.19.1\"\r\n }\r\n}\r\n```\r\n\r\nЭто будет полезно в будущем, когда вместо расшаривания проекта с остальными — а именно папки `node_modules` (которая может сильно разрастись) — вам понадобится расшарить только файл `package.json`, и другие разработчики смогут установить необходимые пакеты автоматически с помощью команды `npm install`.\r\n\r\n\r\n> ### Читайте также\r\n> Как устроен функциональный диалект Лиспа Clojure и почему использующие его программисты [восхищаются им](https://ru.hexlet.io/blog/posts/clojure)\r\n\r\nТеперь мы больше не должны загружать moment.js вручную с веб-сайта, потому что можем сделать это автоматически и обновить, используя npm. Заглянув внутрь папки `node_modules` можно увидеть файл `moment.min.js` в директории `node_modules/moment/min`. Это значит, мы можем сослаться на npm загружаемую версию `moment.min.js` в файле `index.html`, как видно ниже:\r\n\r\n```\r\n<!-- index.html -->\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <title>JavaScript Example</title>\r\n <script src=\"node_modules/moment/min/moment.min.js\"></script>\r\n <script src=\"index.js\"></script>\r\n</head>\r\n<body>\r\n <h1>Hello from HTML!</h1>\r\n</body>\r\n</html>\r\n```\r\n\r\nХорошие новости в том, что теперь мы можем использовать npm, чтобы загружать и обновлять пакеты с помощью командной строки. Плохие в том, что прямо сейчас мы обшариваем папку `node_modules` в поисках местоположения каждого пакета и вручную включаем их в HTML. Это достаточно неудобно, и следующее, что мы сделаем, это посмотрим как автоматизировать и этот процесс.\r\n\r\n### Использование модульного упаковщика JavaScript (webpack)\r\n\r\nУ большинства языков программирования есть способ импорта кода из одного файла в другой. JavaScript изначально не имел этой функции, потому что был создан только для работы в браузере, без доступа к файловой системе компьютера клиента (по соображениям безопасности). Поэтому очень долго организация кода JavaScript в нескольких файлах требовала загрузки каждого файла с глобальными переменными.\r\n\r\nЭто именно то, что мы делаем в приведенном выше примере с moment.js — весь `moment.min.js` файл загружается в HTML, что определяет глобальную переменную `moment`, которая становится доступна любому файлу, загруженному после `moment` (независимо от того, нужен ли к нему доступ).\r\n\r\nВ 2009 году был запущен проект CommonJS, определяющий экосистему для JavaScript за пределами браузера. Большая часть CommonJS была спецификацией для модулей и она, наконец, позволила JavaScript делать импорт и экспорт между файлами, как в большинстве языков программирования, без использования глобальных переменных. Самая известная реализация модулей CommonJS — это node.js.\r\n\r\n\r\n\r\nКак говорилось ранее, node.js — это среда выполнения JavaScript, предназначенная для запуска на сервере. Вот как выглядел более ранний пример с использованием модулей node.js. Вместо того, чтобы загружать все `moment.min.js` с помощью HTML script тега, вы можете загрузить его напрямую в файле JavaScript вот так:\r\n\r\n```\r\n// index.js\r\nvar moment = require('moment');\r\nconsole.log(\"Hello from JavaScript!\");\r\nconsole.log(moment().startOf('day').fromNow());\r\n```\r\n\r\nОпять же, так работает загрузка модуля в node.js, и это здорово, поскольку node.js — это серверный язык с доступом к файловой системе компьютера. Node.js также знает расположение каждого пути модуля npm, поэтому вместо необходимости писать `require('./node_modules/moment/min/moment.min.js)`, можно использовать `require('moment')` — чудно.\r\n\r\nВсё это подходит для node.js, но если вы попытаетесь использовать написанный выше код в браузере, вы получите сообщение об ошибке, в котором будет указано `require` not defined (не определено). Браузер не имеет доступа к файловой системе, это значит, что загрузка модулей таким способом очень проблематична — загрузка файлов должна выполняться динамически, синхронно (что замедляет исполнение), или асинхронно (что может вызвать проблемы с синхронизацией).\r\n\r\nТут вступает в действие упаковщик модулей. Упаковщик модулей JavaScript — это инструмент, который решает задачу с помощью этапа сборки (он имеет доступ к файловой системе) для создания конечно результата, совместимого с браузером (уже не нужен доступ к файловой системе). В этом случае нам нужен упаковщик модулей для поиска всех утверждений `require` (а это недопустимый синтаксис JavaScript для браузера) и замены их актуальным содержимым каждого требуемого файла. Конечный результат — один бандл (упакованный файл) JavaScript (без инструкций require)!\r\n\r\nСамым популярным упаковщиком модулей был [Browserify](http://browserify.org/), он вышел в 2011 году и первым использовал стиль node.js для require во фронтенде (что, по сути, помогло npm стать самым используемым менеджером пакетов для фронтенда). Примерно в 2015 году более популярным стал упаковщик модулей [webpack](https://webpack.github.io/) (его подпитывала популярность фронтенд-фреймворка React, который максимально пользовался различными функциями webpack).\r\n\r\nДавайте посмотрим, как использовать webpack, чтобы заставить показанный выше пример с `require('moment')` работать в браузере. Сначала нам нужно установить webpack в проект. Сам webpack — это npm-пакет, поэтому мы можем установить его из командной строки:\r\n\r\n```\r\n$ npm install webpack --save-dev\r\n```\r\n\r\nОбратите внимание на аргумент `--save-dev`, он сохраняется как development-зависимость. Это означает, что это пакет вам нужен в среде разработки, но не на production-сервере. Это можно наглядно увидеть в файле `package.json`, который был автоматически обновлен:\r\n\r\n```\r\n{\r\n \"name\": \"modern-javascript-example\",\r\n \"version\": \"1.0.0\",\r\n \"description\": \"\",\r\n \"main\": \"index.js\",\r\n \"scripts\": {\r\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n },\r\n \"author\": \"\",\r\n \"license\": \"ISC\",\r\n \"dependencies\": {\r\n \"moment\": \"^2.19.1\"\r\n },\r\n \"devDependencies\": {\r\n \"webpack\": \"^3.7.1\"\r\n }\r\n}\r\n```\r\n\r\nТеперь у нас есть webpack, установленный в папку `node_modules`, как один из пакетов. Вы можете вызывать webpack из командной строки:\r\n\r\n```\r\n$ ./node_modules/.bin/webpack index.js bundle.js\r\n```\r\n\r\nЭта команда запустит инструмент webpack, который был установлен в папке `node_modules`, начнет с файла `index.js`, найдет любые `require` утверждения и заменит их соответствующим кодом, чтобы создать один выходной файл с именем `bundle.js`. \r\n\r\nЭто значит, что мы больше не будем использовать `index.js` в браузере, так как в нем содержатся недопустимые `require` утверждения. Вместо этого мы будем использовать вывод `bundle.js` в браузере, что должно будет отразиться в файле `index.html`:\r\n\r\n```\r\n<!-- index.html -->\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <title>JavaScript Example</title>\r\n <script src=\"bundle.js\"></script>\r\n</head>\r\n<body>\r\n <h1>Hello from HTML!</h1>\r\n</body>\r\n</html>\r\n```\r\n\r\nЕсли вы обновите браузер, увидите, что всё работает, как работало раньше!\r\n\r\nОбратите внимание, что нам нужно будет запускать команду webpack каждый раз, когда мы будем менять `index.js`. Это утомительно и станет ещё утомительней, когда мы станем использовать более сложные фичи webpack (например, [генерацию source maps](https://webpack.js.org/guides/development/#using-source-maps), чтобы помочь отлаживать исходный код из преобразованного кода). Webpack может считывать параметры из файла конфигурации в корневом каталоге проекта с именем `webpack.config.js`, который в нашем случае будет выглядеть так:\r\n\r\n```\r\n// webpack.config.js\r\nmodule.exports = {\r\n entry: './index.js',\r\n output: {\r\n filename: 'bundle.js'\r\n }\r\n};\r\n```\r\n\r\nТеперь каждый раз, когда мы меняем `index.js`, мы можем запускать webpack с помощью команды:\r\n\r\n```\r\n$ ./node_modules/.bin/webpack\r\n```\r\n\r\nНам больше не нужно указывать параметры `index.js` и `bundle.js`, так как webpack загружает эти параметры из файла `webpack.config.js`. Уже лучше, но все же утомительно вводить эту команду при каждом изменении кода — позже мы сделаем этот процесс более плавным.\r\n\r\nВ целом, может казаться, что тут нет ничего особенного, но в этой рабочей последовательности есть несколько огромных плюсов. Мы больше не загружаем внешние скрипты через глобальные переменные. Любые новые библиотеки JavaScript будут добавляться через `require` в JavaScript, в отличие от добавления новых тегов `<script>` в HTML. Наличие одного бандла часто лучше для производительности. И теперь, когда мы добавили шаг сборки, мы можем добавить несколько мощных фич в процесс разработки!\r\n\r\n\r\n> ### Также полезно:\r\n> Стоит ли [учить JavaScript](https://ru.hexlet.io/blog/posts/stoit-li-uchit-javascript-perspektivy-situatsiya-na-rynke-truda-mneniya-ekspertov?promo_name=blog&promo_position=article-body&promo_type=link&promo_start=041024) перспективы, ситуация на рынке труда, мнения экспертов\r\n\r\n### Transpiling для новых фич языка (babel)\r\n\r\n\"Transpiling\" кода — преобразование исходного кода на одном языке в исходный код на другом схожем языке. Это важный сегмент frontend-разработки: поскольку браузеры медленно добавляют новые фичи, были созданы новые языки с экспериментальными функциями, которые преобразуются (transpile) в совместимый с браузерами код.\r\n\r\nДля CSS есть [Sass](http://sass-lang.com/), [Less](http://lesscss.org/) и [Stylus](http://stylus-lang.com/). Для JavaScript самым популярным транспайлером какое-то время был [CoffeeScript](http://coffeescript.org/) (вышел где-то в 2010 году), а в настоящее время большинство людей используют [babel](https://babeljs.io/) или [TypeScript](http://www.typescriptlang.org/). CoffeeScript — это язык, ориентированный на улучшение JavaScript, через значительное его изменение — опциональные круглые скобки, отступы и т. д. \r\n\r\nBabel — это не новый язык, а транспайлер, который преобразует JavaScript нового поколения с фичами, недоступными всем браузерам ([ES2015](https://babeljs.io/learn-es2015/) и выше) в старшую, более совместимую версию JavaScript (ES5). TypeScript — это язык, который практически идентичен JavaScript нового поколения, но в него добавлена опциональная статическая типизация. Многие люди предпочитают использовать babel, потому что он ближе всего к чистому JavaScript.\r\n\r\nДавайте рассмотрим пример использования babel с нашим существующим этапом сборки webpack. Сначала мы устанавливаем babel (а это npm-пакет) в проект из командной строки:\r\n\r\n```\r\n$ npm install babel-core babel-preset-env babel-loader --save-dev\r\n```\r\n\r\nОбратите внимание, что мы устанавливаем 3 отдельных пакета как dev зависимости — `babel-core` — основная часть babel, `babel-preset-env` — пресет, определяющий, какие новые фичи JavaScript преобразовывать, а `babel-loader` — это пакет, позволяющий babel работать с webpack. Мы можем настроить webpack для использования `babel-loader`, отредактировав файл `webpack.config.js`, как показано ниже:\r\n\r\n```\r\n// webpack.config.js\r\nmodule.exports = {\r\n entry: './index.js',\r\n output: {\r\n filename: 'bundle.js'\r\n },\r\n module: {\r\n rules: [\r\n {\r\n test: /\\.js$/,\r\n exclude: /node_modules/,\r\n use: {\r\n loader: 'babel-loader',\r\n options: {\r\n presets: ['env']\r\n }\r\n }\r\n }\r\n ]\r\n }\r\n};\r\n```\r\n\r\nЭтот синтаксис может ломать мозг (к счастью, это не то, что мы будем часто редактировать). Мы просим webpack искать любые .js-файлы (исключая те, что в папке `node_modules`) и применять преобразование babel с помощью `babel-loader` через пресет `babel-preset-env`. [Здесь](https://webpack.js.org/concepts/configuration/) вы можете узнать больше о конфигурационном синтаксисе webpack.\r\n\r\n*У нас есть пример полностью настроенного пакета со всеми описанными штуками — [Hexlet Boilerplates: nodejs](https://github.com/hexlet-boilerplates/nodejs-package), — прим. ред.*\r\n\r\nТеперь, когда все настроено, мы можем начать писать фичи ES2015 в JavaScript! Ниже пример [ES2015 template string](https://babeljs.io/learn-es2015/#ecmascript-2015-features-template-strings) в файле `index.js`:\r\n\r\n```javascript\r\n// index.js\r\nvar moment = require('moment');\r\n\r\nconsole.log(\"Hello from JavaScript!\");\r\nconsole.log(moment().startOf('day').fromNow());\r\n\r\nvar name = \"Bob\", time = \"today\";\r\nconsole.log(`Hello ${name}, how are you ${time}?`);\r\n```\r\n\r\nМы также можем использовать [импорты ES2015](https://babeljs.io/learn-es2015/#ecmascript-2015-features-modules) вместо `require` для загрузки модулей. Это то, что вы сегодня увидите во многих базах кода:\r\n\r\n```\r\n// index.js\r\nimport moment from 'moment';\r\n\r\nconsole.log(\"Hello from JavaScript!\");\r\nconsole.log(moment().startOf('day').fromNow());\r\n\r\nvar name = \"Bob\", time = \"today\";\r\nconsole.log(`Hello ${name}, how are you ${time}?`);\r\n```\r\n\r\nВ этом примере синтаксис `import` не сильно отличается от синтаксиса `require`, но `import` имеет дополнительную гибкость для более сложных случаев. Поскольку мы изменили `index.js`, нам нужно снова запустить webpack в командной строке:\r\n\r\n```\r\n$ ./node_modules/.bin/webpack\r\n```\r\n\r\nТеперь вы можете обновить `index.html` в браузере. На момент написания этой статьи большинство современных браузеров поддерживают все фичи ES2015, поэтому иногда трудно определить, выполнил ли свою работу babel. Вы можете протестировать его в браузере более старой версии, вроде IE9, или покопаться в `bundle.js` и найти строку транспайлированного кода:\r\n\r\n```\r\n// bundle.js\r\n// ...\r\nconsole.log('Hello ' + name + ', how are you ' + time + '?');\r\n// ...\r\n```\r\n\r\nЗдесь видно, как babel преобразовал интерполированную строку ES2015 в обычную конкатенацию чтобы сохранить совместимость с браузером. Хотя этот пример не слишком захватывающий, способность преобразовать код — очень мощный инструмент. В JavaScript есть некоторые интересные языковые фичи, вроде [async/await](https://ponyfoo.com/articles/understanding-javascript-async-await), которые вы можете начать использовать прямо сейчас, чтобы писать код лучше. И хотя transpilation иногда выглядит утомительным и болезненным занятием, оно привело к резким улучшениям в языке за последние несколько лет, так как сегодня люди тестируют завтрашние фичи.\r\n\r\nМы почти закончили, но в нашем воркфлоу все еще есть неотполированные грани. Если нас беспокоит производительность, мы должны [минимизировать](https://en.wikipedia.org/wiki/Minification_%28programming%29) бандл-файл, что должно быть уже достаточно лёгкой задачей, поскольку этап сборки уже встроен. Нам также нужно повторно запускать команду webpack каждый раз, когда мы меняем JavaScript. Поэтому следующее, что мы рассмотрим — это несколько удобных инструментов для решения этих проблем.\r\n\r\n\r\n> ### Также полезно:\r\n> С чего [начать изучение JavaScript](https://ru.hexlet.io/blog/posts/js-s-chego-nachat?promo_name=blog&promo_position=article-body&promo_type=link&promo_start=041024) и как это делать эффективно\r\n\r\n\r\n### Использование task runner (скрипты npm)\r\n\r\nТеперь, когда мы вложились в использование этапа сборки для работы с модулями JavaScript, имеет смысл использовать task runner, инструмент, который автоматизирует различные части процесса сборки. Для фронтенд разработки в задачи включена минимизация кода, оптимизация изображений, прогон тестов и т.д.\r\n\r\nВ 2013 году Grunt был самым популярным таск-раннером во фронтенде, чуть позже появился Gulp. Оба полагаются на плагины, которые охватывают другие инструменты командной строки. Сегодня более популярный выбор — использовать возможности скриптов, встроенных в сам менеджер пакетов npm, который не использует плагины, а напрямую работает с другими инструментами командной строки.\r\n\r\nДавайте напишем несколько npm-скриптов, чтобы упростить использование webpack. Это простое изменение файла `package.json` как в примере ниже:\r\n\r\n```\r\n{\r\n \"name\": \"modern-javascript-example\",\r\n \"version\": \"1.0.0\",\r\n \"description\": \"\",\r\n \"main\": \"index.js\",\r\n \"scripts\": {\r\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\r\n \"build\": \"webpack --progress -p\",\r\n \"watch\": \"webpack --progress --watch\"\r\n },\r\n \"author\": \"\",\r\n \"license\": \"ISC\",\r\n \"dependencies\": {\r\n \"moment\": \"^2.19.1\"\r\n },\r\n \"devDependencies\": {\r\n \"babel-core\": \"^6.26.0\",\r\n \"babel-loader\": \"^7.1.2\",\r\n \"babel-preset-env\": \"^1.6.1\",\r\n \"webpack\": \"^3.7.1\"\r\n }\r\n}\r\n```\r\n\r\nЗдесь мы добавили два новых скрипта, `build` и `watch`. Чтобы запустить скрипт сборки, вы можете ввести в командной строке:\r\n\r\n```\r\n$ npm run build\r\n```\r\n\r\nЭто запустит webpack (используя конфигурацию из `webpack.config.js`, которую мы сделали ранее) с опцией `--progress`, чтобы показать прогресс в процентах и опцию `-p`, чтобы минимизировать код для production. Чтобы запустить сценарий `watch`:\r\n\r\n```\r\n$ npm run watch\r\n```\r\n\r\nИспользуется опция `-watch` для автоматического перезапуска webpack каждый раз, когда изменяется любой JavaScript-файл, что отлично подходит для разработки.\r\n\r\nОбратите внимание, что скрипты в `package.json` могут запускать webpack без указания полного пути `./node_modules/.bin/webpack`, так как node.js знает расположение каждого пути модуля npm. Это круто! Мы можем всё сделать ещё круче, установив webpack-dev-server, отдельный инструмент, который предоставляет простой веб-сервер с живой перезагрузкой. Чтобы установить его как зависимость, введите команду:\r\n\r\n```\r\n$ npm install webpack-dev-server --save-dev\r\n```\r\n\r\nЗатем добавьте скрипт npm в `package.json`:\r\n\r\n```\r\n{\r\n \"name\": \"modern-javascript-example\",\r\n \"version\": \"1.0.0\",\r\n \"description\": \"\",\r\n \"main\": \"index.js\",\r\n \"scripts\": {\r\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\r\n \"build\": \"webpack --progress -p\",\r\n \"watch\": \"webpack --progress --watch\",\r\n \"server\": \"webpack-dev-server --open\"\r\n },\r\n \"author\": \"\",\r\n \"license\": \"ISC\",\r\n \"dependencies\": {\r\n \"moment\": \"^2.19.1\"\r\n },\r\n \"devDependencies\": {\r\n \"babel-core\": \"^6.26.0\",\r\n \"babel-loader\": \"^7.1.2\",\r\n \"babel-preset-env\": \"^1.6.1\",\r\n \"webpack\": \"^3.7.1\"\r\n }\r\n}\r\n```\r\n\r\nТеперь вы можете запустить свой dev-сервер выполнив команду:\r\n\r\n```\r\n$ npm run server\r\n```\r\n\r\nЭто автоматически откроет сайт `index.html` в вашем браузере с адресом `localhost: 8080` (по умолчанию). Каждый раз, когда вы будете менять JavaScript в `index.js`, webpack-dev-server будет перестраивать собственный упакованный JavaScript и автоматически обновлять браузер. Это удивительно полезная экономия времени, поскольку у вас есть возможность сосредоточиться на коде, а не на постоянном переключении контекстов между кодом и браузером, чтобы увидеть изменения.\r\n\r\nИ это только верхушка, есть еще много вариантов с webpack и webpack-dev-server (о которых вы можете прочитать [здесь](https://webpack.js.org/guides/development/)). Конечно, вы можете создавать npm-скрипты для выполнения других задач, таких как преобразование Sass в CSS, сжатие изображений, прогон тестов всего, к чему имеет доступ инструмент командной строки — всё имеет смысл. Также есть несколько отличных расширенных опций и манёвров с самими скриптами npm. Это [выступление](https://www.youtube.com/watch?v=0RYETb9YVrk) Кейт Хадсон — отличное начало.\r\n\r\n### Вывод\r\n\r\nВот, что представляет из себя современный JavaScript в двух словах. Мы перешли с простого HTML и JS к **менеджеру пакетов**, чтобы автоматически загружать сторонние пакеты, **упаковщику модулей** для создания единого скрипт-файла, **транспайлеру** для использования будущих фич JavaScript и **таск раннеру** для автоматизации различных частей процесса сборки. Определенно тут много движимых частей, особенно для новичков. Веб-разработка когда-то была отличным стартом для новичков в программировании, именно потому что было легко начать работать; сейчас это довольно сложно, особенно потому, что различные инструменты склонны к быстрым изменениям.\r\n\r\nТем не менее, всё не так плохо, как кажется. Всё уравновешивается, особенно после внедрениея экосистемы node, как стабильного способа работы с фронтендом. Довольно приятно использовать npm в качестве менеджера пакетов, `require` или `import` для модулей, и npm-скрипты для запуска задач. Это значительно упрощает рабочий процесс по сравнению с тем, что было год или два назад!\r\n\r\n::posts\r\n\r\nДополнительная польза и для начинающих, и для опытных разработчиков в том, что фреймворки сегодня содержат инструменты, которые упрощают процесс и начать теперь намного легче. У Ember есть [ember-cli](https://ember-cli.com/), что очень сильно повлияло на [angular-cli](https://cli.angular.io/) от Angular, [create-react-app](https://github.com/facebookincubator/create-react-app) от React, [vue-cli](https://github.com/vuejs/vue-cli) от Vue и т. д. Все эти инструменты создают проект с полным набором того, что вам нужно — всё, что вам нужно сделать, это начать писать код. Тем не менее, эти инструменты не волшебные, они просто задают правильную базовую структуру, но вы можете оказаться в ситуации, когда вам потребуется выполнить дополнительную настройку с помощью webpack, babel и т.д. Поэтому очень важно понять, что делает каждый из этих инструментов, что мы и сделали в этой статье.\r\n\r\nРабота с современным JavaScript — не всегда самая приятная штука, потому что он постоянно меняется и развивается со скоростью звука. Но, хоть иногда некоторые моменты и кажутся изобретением колеса, быстрая эволюция JavaScript продвинула инновации вроде горячей перезагрузки, инструмента статического анализа и отладки в контексте времени. Сегодня быть разработчиком — захватывающе, и я надеюсь, что эта информация может послужить путеводителем, который поможет вам в вашем путешествии!\r\n\r\n\r\n\r\n","reading_time":13,"url":"https://ru.hexlet.io/blog/posts/modern-js-explained","cover_thumb_variant":null,"cover_list_variant":"/vite/assets/blog_post-7eTyeLLt.webp","cover_main_variant":"/vite/assets/blog_post-7eTyeLLt.webp","related_stacks_count":5},"relatedPosts":[{"model_name":"BlogPost","id":12,"title":"Среды разработки: какие они бывают и чем отличаются друг от друга","slug":"environment","summary":"Сооснователь Хекслета Кирилл Мокевнин рассказывает, какие бывают среды разработки, как проводится контроль и испытание фичи и что такое интеграция.","created_at":"2016-08-23T14:10:57.533Z","published_at":"2023-03-21T13:40:16.878Z","cover_list_variant":"/vite/assets/blog_post-7eTyeLLt.webp"},{"model_name":"BlogPost","id":2,"title":"10 советов, как более эффективно учиться в Хекслете","slug":"10-sovetov-o-tom-kak-bolee-effectivno-uchitsya-na-hexlet","summary":"Делимся десятью лайфхаками, которые повысят эффективность обучения на Хекслете.","created_at":"2016-07-29T16:52:44.532Z","published_at":"2022-11-07T14:23:05.569Z","cover_list_variant":"/vite/assets/blog_post-7eTyeLLt.webp"},{"model_name":"BlogPost","id":129,"title":"Как работать с командной строкой эффективно: 6 советов от разработчика Дейва Керра","slug":"effective_shell_navigation","summary":"Чем больше программист работает в командной строке, тем сложнее ему ориентироваться в тексте и командах. Мы перевели статью разработчика Дейва Керра и узнали, как с помощью сочетаний клавиш (шорткатов) упростить навигацию и поиск в командной строке.","created_at":"2017-07-03T09:54:55.442Z","published_at":"2022-10-28T10:49:59.501Z","cover_list_variant":"/vite/assets/blog_post-7eTyeLLt.webp"}],"category":{"id":4,"name":"Код","slug":"code","state":"published","created_at":"2016-08-23T13:33:44.258Z"},"mainStackCategory":{"id":2,"name":"Курсы по веб-разработке","slug":"web_development","short_name":"Веб-разработка","order":190,"state":"published","category_slug":"courses_web_development"},"categories":[{"id":6,"name":"Мотивация","slug":"motivation","state":"published","created_at":"2016-10-06T18:31:38.903Z"},{"id":3,"name":"Истории успеха","slug":"success","state":"published","created_at":"2016-07-30T12:57:18.308Z"},{"id":14,"name":"Дневник студента","slug":"student-diary","state":"published","created_at":"2019-02-25T13:27:09.471Z"},{"id":4,"name":"Код","slug":"code","state":"published","created_at":"2016-08-23T13:33:44.258Z"},{"id":12,"name":"Карьера","slug":"career","state":"published","created_at":"2017-07-21T15:42:21.481Z"}],"relatedLandings":[{"stack":{"id":12,"slug":"frontend","title":"Фронтенд-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":20,"duration_in_months":10},"id":17,"slug":"frontend","title":"Фронтенд-разработчик","subtitle":"Изучите HTML, CSS, JavaScript и React","subtitle_for_lists":"Изучите HTML, CSS, JavaScript и React","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"frontend","price_text":"от 6 792 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzcyNywicHVyIjoiYmxvYl9pZCJ9fQ==--2d5cbbf5c3b4a73ae4b2c50632305d78f5872e4d/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programmer-rafiki.png"},{"stack":{"id":43,"slug":"fullstack-javascript","title":"Fullstack-разработчик на Node.js","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":140,"duration_in_months":12},"id":74,"slug":"fullstack-javascript","title":"Fullstack-разработчик на Node.js","subtitle":"Освоите JavaScript, Node.js, Fastify и React для фронтенда и бэкенда.","subtitle_for_lists":"Освоите JavaScript, Node.js, Fastify и React для фронтенда и бэкенда.","locale":"ru","current":true,"duration_in_months_text":"12 месяцев","stack_slug":"fullstack-javascript","price_text":"от 7 934 ₽","duration_text":"12 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDA0MywicHVyIjoiYmxvYl9pZCJ9fQ==--e2c6c0775e2308e42fbc5dc592ba2db0470632ca/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programmer-rafiki.png"},{"stack":{"id":29,"slug":"js-oop","title":"ООП на Javascript","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4250,"duration_in_months":2},"id":46,"slug":"js-oop","title":"ООП на Javascript","subtitle":"Навык глубокого понимания архитектуры и написания чистого кода, позволяющий решать сложные задачи","subtitle_for_lists":"Изучите архитектуру и принципы чистого кода на JS","locale":"ru","current":true,"duration_in_months_text":"2 месяца","stack_slug":"js-oop","price_text":"от 3 900 ₽","duration_text":"2 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDAxOSwicHVyIjoiYmxvYl9pZCJ9fQ==--84efd2b6854b7000046e9ce06e6be85d38af5ab8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/JavaScript%20frameworks-cuate.png"},{"stack":{"id":13,"slug":"backend","title":"Node.js-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":130,"duration_in_months":10},"id":19,"slug":"backend","title":"Node.js-разработчик","subtitle":"Изучите JavaScript, Node.js, Fastify и REST API","subtitle_for_lists":"Изучите JavaScript, Node.js, Fastify и REST API","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"backend","price_text":"от 4 755 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzcyNSwicHVyIjoiYmxvYl9pZCJ9fQ==--2e84f5f94140ee4e22019ac479c290ef48c3fac8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png"},{"stack":{"id":52,"slug":"typescript","title":"Typescript","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":200,"duration_in_months":2},"id":92,"slug":"typescript","title":"Typescript","subtitle":"Навык снижать ошибки, упрощать отладку, повышать качество кода и ускорять разработку с автодополнением и типизацией","subtitle_for_lists":"Изучите Typescript и получите навык снижать ошибки, упрощать отладку","locale":"ru","current":true,"duration_in_months_text":"2 месяца","stack_slug":"typescript","price_text":"от 3 900 ₽","duration_text":"2 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzcwOSwicHVyIjoiYmxvYl9pZCJ9fQ==--03e50bbd408fef672ad099f7b2a258d80f54ad96/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-bro.png"}]},"url":"/blog/posts/modern-js-explained","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","author":"Natalia Bass","name":"Описание современного JavaScript для динозавров","datePublished":"2017-11-07T08:12:45.206Z","headline":"Изучать современный JavaScript — болезненно, если вы не знакомы с ним с самого его рождения. Экосистема разрастается и меняется с такой скоростью, что сложно разобраться с тем, какие проблемы пытаются решить разные инструменты. Я начал программировать в 1998 году, но к серьёзному изучению JavaScript приступил только в 2014. В то время я помню как анализировал [Browserify](http://browserify.org/) и изумлённо смотрел на его слоган:\r\n\r\n> Browserify позволяет запрашивать (require) модули в браузере, объединяя все зависимости.\r\n\r\nМожно сказать, я не понимал ни слова в этом предложении, и с трудом осознавал, насколько это может быть полезно мне, как разработчику.\r\n\r\nЦель этой статьи — показать исторический контекст развития инструментов JavaScript до их уровня в 2017. Начнём с самых первых моментов и построим шаблон веб-сайта, как бы это сделали динозавры — без инструментов, чистый HTML и JavaScript. Затем мы будем пошагово вводить различные инструменты, чтобы на практике видеть, какие задачи они решают — поочерёдно. Благодаря историческому контексту у вас будет больше возможностей изучить и лучше адаптироваться к бесконечно меняющемуся JavaScript. Давайте начнём!","image":"/vite/assets/blog_post-7eTyeLLt.webp","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":12}]}</script><div style="--container-size:var(--container-size-lg);margin-top:var(--mantine-spacing-xl);height:100%" class="m_7485cace mantine-Container-root" data-size="lg" data-strategy="block"><script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"position":1,"@type":"ListItem","item":{"@id":"/blog","name":"Блог Хекслета"}},{"position":2,"@type":"ListItem","item":{"@id":"/blog/categories/code","name":"Код"}},{"position":3,"@type":"ListItem","item":{"@id":"/blog/posts/modern-js-explained","name":"Описание современного JavaScript для динозавров"}}]}</script><div style="margin-bottom:var(--mantine-spacing-xs)" class="m_8b3717df mantine-Breadcrumbs-root"><a style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:inherit" class="mantine-focus-auto m_849cf0da m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-size="sm" data-underline="hover" href="/"><div style="color:inherit" class="m_4451eb3a mantine-Center-root"><svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-home-link "><path d="M20.085 11.085l-8.085 -8.085l-9 9h2v7a2 2 0 0 0 2 2h4.5"></path><path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 1.807 1.143"></path><path d="M20 21a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M20 16a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M15 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M21 16l-5 3l5 2"></path></svg></div></a><div class="m_3b8f2208 mantine-Breadcrumbs-separator">/</div><a style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:inherit" class="mantine-focus-auto m_849cf0da m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-size="sm" data-underline="hover" href="/blog">Блог Хекслета</a><div class="m_3b8f2208 mantine-Breadcrumbs-separator">/</div><a style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:inherit" class="mantine-focus-auto m_849cf0da m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-size="sm" data-underline="hover" href="/blog/categories/code">Код</a><div class="m_3b8f2208 mantine-Breadcrumbs-separator">/</div><p style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:var(--mantine-color-dimmed)" class="mantine-focus-auto m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root" data-size="sm">Описание современного JavaScript для динозавров</p></div><style data-mantine-styles="inline">.__m__-_R_eub_{margin-bottom:var(--mantine-spacing-xs);}@media(min-width: 36em){.__m__-_R_eub_{margin-bottom:var(--mantine-spacing-xs);}}</style><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root __m__-_R_eub_"><style data-mantine-styles="inline">.__m__-_R_deub_{width:100%;}@media(min-width: 36em){.__m__-_R_deub_{width:70%;}}@media(min-width: 75em){.__m__-_R_deub_{width:75%;}}</style><div class="__m__-_R_deub_"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size)" class="m_8a5d1357 mantine-Title-root" data-order="1">Описание современного JavaScript для динозавров</h1></div></div></div><div style="position:absolute;top:calc(18.75rem * var(--mantine-scale))" class=""></div><style data-mantine-styles="inline">.__m__-_R_2iub_{--grid-gutter:var(--mantine-spacing-xl);}</style><div class="m_410352e9 mantine-Grid-root __m__-_R_2iub_"><div class="m_dee7bd2f mantine-Grid-inner"><style data-mantine-styles="inline">.__m__-_R_dmiub_{--col-flex-grow:auto;--col-flex-basis:100%;--col-max-width:100%;}@media(min-width: 48em){.__m__-_R_dmiub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}@media(min-width: 62em){.__m__-_R_dmiub_{--col-flex-grow:auto;--col-flex-basis:66.66666666666667%;--col-max-width:66.66666666666667%;}}</style><div class="m_96bdd299 mantine-Grid-col __m__-_R_dmiub_"><div style="--stack-gap:var(--mantine-spacing-md);--stack-align:stretch;--stack-justify:flex-start;margin-bottom:var(--mantine-spacing-xl)" class="m_6d731127 mantine-Stack-root"><div class=""><div style="--group-gap:var(--mantine-spacing-xs);--group-align:center;--group-justify:flex-start;--group-wrap:wrap;margin-bottom:var(--mantine-spacing-xl)" class="m_4081bf90 mantine-Group-root"><button style="--badge-height:var(--badge-height-sm);--badge-padding-x:var(--badge-padding-x-sm);--badge-fz:var(--badge-fz-sm);--badge-bg:var(--mantine-color-default);--badge-color:var(--mantine-color-default-color);--badge-bd:calc(0.0625rem * var(--mantine-scale)) solid var(--mantine-color-default-border);cursor:pointer;color:inherit" class="m_347db0ec mantine-Badge-root" data-variant="default" data-size="sm" type="button" aria-label="JavaScript"><span class="m_5add502a mantine-Badge-label">JavaScript</span></button></div><div style="--group-gap:calc(0.625rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap;margin-bottom:var(--mantine-spacing-sm);color:var(--mantine-color-gray-text)" class="m_4081bf90 mantine-Group-root"><div style="--group-gap:calc(0.1875rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap;margin-inline-end:var(--mantine-spacing-lg)" class="m_4081bf90 mantine-Group-root">7 ноября 2017 г.</div><div style="--group-gap:calc(0.1875rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-clock "><path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0"></path><path d="M12 7v5l3 3"></path></svg></div>13 минут</div><div style="--group-gap:calc(0.1875rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-thumb-up "><path d="M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3"></path></svg></div>12</div></div><div style="--ar-ratio:2" class="m_71ac47fc mantine-AspectRatio-root"><img style="--image-radius:var(--mantine-radius-md);--image-object-fit:cover;width:100%;height:100%" class="m_9e117634 mantine-Image-root" src="/vite/assets/blog_post-7eTyeLLt.webp" alt="Описание современного JavaScript для динозавров"/></div></div><div role="link" tabindex="0" style="cursor:pointer"><button style="display:block;width:100%" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Присоединяйтесь к нашему Telegram-сообществу"><div style="background-color:light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6))" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:flex-start;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><div style="--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:auto;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-brand-telegram "><path d="M15 10l-4 4l6 6l4 -16l-18 7l4 2l2 6l3 -4"></path></svg></div>Присоединяйтесь к нашему Telegram-сообществу</div></div></button></div><div style="margin-bottom:var(--mantine-spacing-xl)" class="m_d08caa0 mantine-Typography-root"><p>Это перевод статьи Питера <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://medium.com/@peterxjang/modern-javascript-explained-for-dinosaurs-f695e9747b70" rel="noopener noreferrer" target="_blank">Янга Modern JavaScript Explained For Dinosaurs</a>. Она познакомит вас с инфраструктурой современной фронтэнд-разработки.</p>
<p>Наш курс <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/courses/js-setup-environment" rel="noopener noreferrer" target="_blank">JS: Настройка окружения</a> выполняет схожую задачу, но чуть глубже, последовательно и без привязки только к фронтэнду. Этим темам также посвящена значительная часть <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/projects/" rel="noopener noreferrer" target="_blank">первого проекта в JS Backend и JS Frontend</a>. Там мы на деле устанавливаем и настраиваем среду для разработки, проверки, публикации, сборки и запуска своих приложений.</p>
<style data-mantine-styles="inline">.__m__-_R_5derddmiub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_5derddmiub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_5derddmiub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/frontend?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Фронтенд-разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите HTML, CSS, JavaScript и React</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzcyNywicHVyIjoiYmxvYl9pZCJ9fQ==--2d5cbbf5c3b4a73ae4b2c50632305d78f5872e4d/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programmer-rafiki.png" alt="Фронтенд-разработчик" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 6 792 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/fullstack-javascript?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">12 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Fullstack-разработчик на Node.js</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Освоите JavaScript, Node.js, Fastify и React для фронтенда и бэкенда.</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDA0MywicHVyIjoiYmxvYl9pZCJ9fQ==--e2c6c0775e2308e42fbc5dc592ba2db0470632ca/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programmer-rafiki.png" alt="Fullstack-разработчик на Node.js" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 7 934 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/js-oop?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">2 месяца</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">ООП на Javascript</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите архитектуру и принципы чистого кода на JS</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDAxOSwicHVyIjoiYmxvYl9pZCJ9fQ==--84efd2b6854b7000046e9ce06e6be85d38af5ab8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/JavaScript%20frameworks-cuate.png" alt="ООП на Javascript" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/backend?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Node.js-разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите JavaScript, Node.js, Fastify и REST API</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzcyNSwicHVyIjoiYmxvYl9pZCJ9fQ==--2e84f5f94140ee4e22019ac479c290ef48c3fac8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png" alt="Node.js-разработчик" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 4 755 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/typescript?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">2 месяца</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Typescript</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Typescript и получите навык снижать ошибки, упрощать отладку</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzcwOSwicHVyIjoiYmxvYl9pZCJ9fQ==--03e50bbd408fef672ad099f7b2a258d80f54ad96/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-bro.png" alt="Typescript" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div>
<hr/>
<p>Изучать современный JavaScript — болезненно, если вы не знакомы с ним с самого его рождения. Экосистема разрастается и меняется с такой скоростью, что сложно разобраться с тем, какие проблемы пытаются решить разные инструменты. Я начал программировать в 1998 году, но к серьёзному изучению JavaScript приступил только в 2014. В то время я помню как анализировал <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://browserify.org/" rel="noopener noreferrer" target="_blank">Browserify</a> и изумлённо смотрел на его слоган:</p>
<blockquote>
<p>Browserify позволяет запрашивать (require) модули в браузере, объединяя все зависимости.</p>
</blockquote>
<p>Можно сказать, я не понимал ни слова в этом предложении, и с трудом осознавал, насколько это может быть полезно мне, как разработчику.</p>
<p>Цель этой статьи — показать исторический контекст развития инструментов JavaScript до их уровня в 2017. Начнём с самых первых моментов и построим шаблон веб-сайта, как бы это сделали динозавры — без инструментов, чистый HTML и JavaScript. Затем мы будем пошагово вводить различные инструменты, чтобы на практике видеть, какие задачи они решают — поочерёдно. Благодаря историческому контексту у вас будет больше возможностей изучить и лучше адаптироваться к бесконечно меняющемуся JavaScript. Давайте начнём!</p>
<h3 id="heading-3-1">Использование JavaScript старомодным способом</h3>
<p>Давайте начнём со старомодного веб-сайта, написанного на HTML и JavaScript, что включает загрузку и помещение ссылок на файлы вручную. Вот простой <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.html</code> файл, который ссылается на JavaScript файл:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Example</title>
<script src="index.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html></code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Строчка <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><script src="index.js"></script></code> ссылается на отдельный JavaScript-файл с названием <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code> в той же директории:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">// index.js
console.log("Hello from JavaScript!");</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Это всё, что вам нужно, чтобы сделать веб-сайт!</p>
<blockquote>
<h3 id="heading-3-2">Продолжайте углублять свои знания</h3>
<p>На Хекслете есть <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/tracks" rel="noopener noreferrer" target="_blank">блок «Треки»</a>, где собраны курсы для опытных разработчиков, которые хотят новых знаний про разработку.</p>
</blockquote>
<p>Теперь давайте предположим, что вы хотите добавить библиотеку, которую написал кто-то другой, вроде <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://momentjs.com/" rel="noopener noreferrer" target="_blank">moment.js</a> (библиотеку, которая помогает превращать даты в понятные для чтения человеком). Например, вы можете использовать функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment</code> в JavaScript вот так:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">moment().startOf('day').fromNow(); // 20 hours ago</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Но только при условии, что вы включите moment.js в код своего веб-сайта! На <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://momentjs.com/" rel="noopener noreferrer" target="_blank">домашней странице moment.js</a> вы найдёте такие инструкции:</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://i.imgur.com/y1teBkW.png" alt="img" loading="lazy"/></p>
<p>Хмм, как много всего происходит в секции <strong>Install</strong> справа. Но давайте пока проигнорируем это — мы можем добавить moment.js на веб-сайт, загрузив файл <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment.min.js</code> в ту же директорию и включив его в файл <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.html</code>.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example</title>
<link rel="stylesheet" href="index.css">
<script src="moment.min.js"></script>
<script src="index.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html></code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Заметьте, что <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment.min.js</code> загружается перед <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code>, а это значит вы можете использовать функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment</code> в <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code> таким способом:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">// index.js
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>И вот таким образом мы раньше делали веб-сайты с JavaScript-библиотеками! Положительный момент в том, что всё было достаточно легко понимать. Отрицательный — было муторно искать и загружать новые версии библиотек каждый раз, когда они обновлялись.</p>
<h3 id="heading-3-3">Использование пакетного менеджера JavaScript (npm)</h3>
<p>Начиная примерно с 2010 появилось несколько конкурирующих пакетных менеджеров JavaScript для автоматизации процесса загрузки и обновления библиотек из центрального репозитория. <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://bower.io/" rel="noopener noreferrer" target="_blank">Bower</a> был, возможно, самым популярным в 2013, но в итоге, примерно в 2015 его настиг <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://www.npmjs.com/" rel="noopener noreferrer" target="_blank">npm</a>. (Стоит отметить, что с конца 2016 <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://yarnpkg.com/en/" rel="noopener noreferrer" target="_blank">yarn</a> привлёк много внимания, как альтернатива интерфейсу npm, но он всё ещё использует npm пакеты).</p>
<p>Заметьте, что npm был изначально пакетным менеджером, созданным специально для <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://nodejs.org/" rel="noopener noreferrer" target="_blank">node.js</a> — среды исполнения JavaScript, предназначенной для запуска на сервере, а не во фронтенде. Что делает его очень странным выбором для фронтенд-пакетного менеджера JavaScript библиотек, предназначенных запускаться в браузере.</p>
<p><em>Заметьте: Использование пакетного менеджера обычно требует работы с командной строкой, что в прошлом никогда не требовалось от фронтенд-разработки. Если вы никогда ей не пользовались, можете прочитать <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://www.learnenough.com/command-line-tutorial" rel="noopener noreferrer" target="_blank">этот туториал</a>, чтобы составить представление о том, как начать. Так или иначе, умение пользоваться командной строкой — важная часть современного JavaScript (и ещё она открывает двери в другие области разработки).</em></p>
<p><em>На Хекслете как раз есть бесплатный курс <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/courses/bash" rel="noopener noreferrer" target="_blank">Bash: Основы командной строки</a>, — прим. ред.</em></p>
<p>Давайте взглянем как использовать npm, чтобы установить пакет moment.js автоматически, вместо того, чтобы загружать его вручную. Если у вас установлен node.js, у вас уже есть и npm, а это значит, что вы можете через командную строку перейти к папке, в которой находится файл <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.html</code> и ввести:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ npm init</code>
<p>Будет выведено несколько вопросов (можно оставить умолчания: нажимать "Enter" после каждого вопроса) и сгенерируется новый файл с именем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">package.json</code>. Это конфигурационный файл, который npm использует, чтобы хранить всю информацию о проекте. С настройками по умолчанию содержимое <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">package.json</code> должно выглядеть подобно этому:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">{
"name": "your-project-name",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}</code>
<p>Чтобы установить JavaScript-пакет moment.js, мы теперь можем следовать инструкциям npm на их домашней странице, введя в командную строку:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ npm install moment --save</code>
<p>Эта команда выполняет две задачи: в начале она загружает весь код из <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://unpkg.com/moment/" rel="noopener noreferrer" target="_blank">пакета moment.js</a> в папку, называемую <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node_modules</code>. Потом автоматически модифицирует файл <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">package.json</code>, чтобы мониторить moment.js как зависимость (зависимости — это пакеты и библиотеки, от которых зависит наше приложение).</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
}
}</code>
<p>Это будет полезно в будущем, когда вместо расшаривания проекта с остальными — а именно папки <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node_modules</code> (которая может сильно разрастись) — вам понадобится расшарить только файл <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">package.json</code>, и другие разработчики смогут установить необходимые пакеты автоматически с помощью команды <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">npm install</code>.</p>
<blockquote>
<h3 id="heading-3-4">Читайте также</h3>
<p>Как устроен функциональный диалект Лиспа Clojure и почему использующие его программисты <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/blog/posts/clojure" rel="noopener noreferrer" target="_blank">восхищаются им</a></p>
</blockquote>
<p>Теперь мы больше не должны загружать moment.js вручную с веб-сайта, потому что можем сделать это автоматически и обновить, используя npm. Заглянув внутрь папки <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node_modules</code> можно увидеть файл <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment.min.js</code> в директории <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node_modules/moment/min</code>. Это значит, мы можем сослаться на npm загружаемую версию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment.min.js</code> в файле <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.html</code>, как видно ниже:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Example</title>
<script src="node_modules/moment/min/moment.min.js"></script>
<script src="index.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html></code>
<p>Хорошие новости в том, что теперь мы можем использовать npm, чтобы загружать и обновлять пакеты с помощью командной строки. Плохие в том, что прямо сейчас мы обшариваем папку <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node_modules</code> в поисках местоположения каждого пакета и вручную включаем их в HTML. Это достаточно неудобно, и следующее, что мы сделаем, это посмотрим как автоматизировать и этот процесс.</p>
<h3 id="heading-3-5">Использование модульного упаковщика JavaScript (webpack)</h3>
<p>У большинства языков программирования есть способ импорта кода из одного файла в другой. JavaScript изначально не имел этой функции, потому что был создан только для работы в браузере, без доступа к файловой системе компьютера клиента (по соображениям безопасности). Поэтому очень долго организация кода JavaScript в нескольких файлах требовала загрузки каждого файла с глобальными переменными.</p>
<p>Это именно то, что мы делаем в приведенном выше примере с moment.js — весь <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment.min.js</code> файл загружается в HTML, что определяет глобальную переменную <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment</code>, которая становится доступна любому файлу, загруженному после <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment</code> (независимо от того, нужен ли к нему доступ).</p>
<p>В 2009 году был запущен проект CommonJS, определяющий экосистему для JavaScript за пределами браузера. Большая часть CommonJS была спецификацией для модулей и она, наконец, позволила JavaScript делать импорт и экспорт между файлами, как в большинстве языков программирования, без использования глобальных переменных. Самая известная реализация модулей CommonJS — это node.js.</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://i.imgur.com/xU3eOHk.png" alt="node js" loading="lazy"/></p>
<p>Как говорилось ранее, node.js — это среда выполнения JavaScript, предназначенная для запуска на сервере. Вот как выглядел более ранний пример с использованием модулей node.js. Вместо того, чтобы загружать все <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">moment.min.js</code> с помощью HTML script тега, вы можете загрузить его напрямую в файле JavaScript вот так:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">// index.js
var moment = require('moment');
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());</code>
<p>Опять же, так работает загрузка модуля в node.js, и это здорово, поскольку node.js — это серверный язык с доступом к файловой системе компьютера. Node.js также знает расположение каждого пути модуля npm, поэтому вместо необходимости писать <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require('./node_modules/moment/min/moment.min.js)</code>, можно использовать <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require('moment')</code> — чудно.</p>
<p>Всё это подходит для node.js, но если вы попытаетесь использовать написанный выше код в браузере, вы получите сообщение об ошибке, в котором будет указано <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require</code> not defined (не определено). Браузер не имеет доступа к файловой системе, это значит, что загрузка модулей таким способом очень проблематична — загрузка файлов должна выполняться динамически, синхронно (что замедляет исполнение), или асинхронно (что может вызвать проблемы с синхронизацией).</p>
<p>Тут вступает в действие упаковщик модулей. Упаковщик модулей JavaScript — это инструмент, который решает задачу с помощью этапа сборки (он имеет доступ к файловой системе) для создания конечно результата, совместимого с браузером (уже не нужен доступ к файловой системе). В этом случае нам нужен упаковщик модулей для поиска всех утверждений <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require</code> (а это недопустимый синтаксис JavaScript для браузера) и замены их актуальным содержимым каждого требуемого файла. Конечный результат — один бандл (упакованный файл) JavaScript (без инструкций require)!</p>
<p>Самым популярным упаковщиком модулей был <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://browserify.org/" rel="noopener noreferrer" target="_blank">Browserify</a>, он вышел в 2011 году и первым использовал стиль node.js для require во фронтенде (что, по сути, помогло npm стать самым используемым менеджером пакетов для фронтенда). Примерно в 2015 году более популярным стал упаковщик модулей <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://webpack.github.io/" rel="noopener noreferrer" target="_blank">webpack</a> (его подпитывала популярность фронтенд-фреймворка React, который максимально пользовался различными функциями webpack).</p>
<p>Давайте посмотрим, как использовать webpack, чтобы заставить показанный выше пример с <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require('moment')</code> работать в браузере. Сначала нам нужно установить webpack в проект. Сам webpack — это npm-пакет, поэтому мы можем установить его из командной строки:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ npm install webpack --save-dev</code>
<p>Обратите внимание на аргумент <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">--save-dev</code>, он сохраняется как development-зависимость. Это означает, что это пакет вам нужен в среде разработки, но не на production-сервере. Это можно наглядно увидеть в файле <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">package.json</code>, который был автоматически обновлен:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
},
"devDependencies": {
"webpack": "^3.7.1"
}
}</code>
<p>Теперь у нас есть webpack, установленный в папку <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node_modules</code>, как один из пакетов. Вы можете вызывать webpack из командной строки:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ ./node_modules/.bin/webpack index.js bundle.js</code>
<p>Эта команда запустит инструмент webpack, который был установлен в папке <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node_modules</code>, начнет с файла <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code>, найдет любые <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require</code> утверждения и заменит их соответствующим кодом, чтобы создать один выходной файл с именем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">bundle.js</code>.</p>
<p>Это значит, что мы больше не будем использовать <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code> в браузере, так как в нем содержатся недопустимые <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require</code> утверждения. Вместо этого мы будем использовать вывод <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">bundle.js</code> в браузере, что должно будет отразиться в файле <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.html</code>:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Example</title>
<script src="bundle.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html></code>
<p>Если вы обновите браузер, увидите, что всё работает, как работало раньше!</p>
<p>Обратите внимание, что нам нужно будет запускать команду webpack каждый раз, когда мы будем менять <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code>. Это утомительно и станет ещё утомительней, когда мы станем использовать более сложные фичи webpack (например, <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://webpack.js.org/guides/development/#using-source-maps" rel="noopener noreferrer" target="_blank">генерацию source maps</a>, чтобы помочь отлаживать исходный код из преобразованного кода). Webpack может считывать параметры из файла конфигурации в корневом каталоге проекта с именем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">webpack.config.js</code>, который в нашем случае будет выглядеть так:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">// webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js'
}
};</code>
<p>Теперь каждый раз, когда мы меняем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code>, мы можем запускать webpack с помощью команды:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ ./node_modules/.bin/webpack</code>
<p>Нам больше не нужно указывать параметры <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code> и <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">bundle.js</code>, так как webpack загружает эти параметры из файла <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">webpack.config.js</code>. Уже лучше, но все же утомительно вводить эту команду при каждом изменении кода — позже мы сделаем этот процесс более плавным.</p>
<p>В целом, может казаться, что тут нет ничего особенного, но в этой рабочей последовательности есть несколько огромных плюсов. Мы больше не загружаем внешние скрипты через глобальные переменные. Любые новые библиотеки JavaScript будут добавляться через <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require</code> в JavaScript, в отличие от добавления новых тегов <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"><script></code> в HTML. Наличие одного бандла часто лучше для производительности. И теперь, когда мы добавили шаг сборки, мы можем добавить несколько мощных фич в процесс разработки!</p>
<blockquote>
<h3 id="heading-3-6">Также полезно:</h3>
<p>Стоит ли <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/blog/posts/stoit-li-uchit-javascript-perspektivy-situatsiya-na-rynke-truda-mneniya-ekspertov?promo_name=blog&promo_position=article-body&promo_type=link&promo_start=041024" rel="noopener noreferrer" target="_blank">учить JavaScript</a> перспективы, ситуация на рынке труда, мнения экспертов</p>
</blockquote>
<h3 id="heading-3-7">Transpiling для новых фич языка (babel)</h3>
<p>"Transpiling" кода — преобразование исходного кода на одном языке в исходный код на другом схожем языке. Это важный сегмент frontend-разработки: поскольку браузеры медленно добавляют новые фичи, были созданы новые языки с экспериментальными функциями, которые преобразуются (transpile) в совместимый с браузерами код.</p>
<p>Для CSS есть <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://sass-lang.com/" rel="noopener noreferrer" target="_blank">Sass</a>, <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://lesscss.org/" rel="noopener noreferrer" target="_blank">Less</a> и <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://stylus-lang.com/" rel="noopener noreferrer" target="_blank">Stylus</a>. Для JavaScript самым популярным транспайлером какое-то время был <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://coffeescript.org/" rel="noopener noreferrer" target="_blank">CoffeeScript</a> (вышел где-то в 2010 году), а в настоящее время большинство людей используют <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://babeljs.io/" rel="noopener noreferrer" target="_blank">babel</a> или <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://www.typescriptlang.org/" rel="noopener noreferrer" target="_blank">TypeScript</a>. CoffeeScript — это язык, ориентированный на улучшение JavaScript, через значительное его изменение — опциональные круглые скобки, отступы и т. д.</p>
<p>Babel — это не новый язык, а транспайлер, который преобразует JavaScript нового поколения с фичами, недоступными всем браузерам (<a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://babeljs.io/learn-es2015/" rel="noopener noreferrer" target="_blank">ES2015</a> и выше) в старшую, более совместимую версию JavaScript (ES5). TypeScript — это язык, который практически идентичен JavaScript нового поколения, но в него добавлена опциональная статическая типизация. Многие люди предпочитают использовать babel, потому что он ближе всего к чистому JavaScript.</p>
<p>Давайте рассмотрим пример использования babel с нашим существующим этапом сборки webpack. Сначала мы устанавливаем babel (а это npm-пакет) в проект из командной строки:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ npm install babel-core babel-preset-env babel-loader --save-dev</code>
<p>Обратите внимание, что мы устанавливаем 3 отдельных пакета как dev зависимости — <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">babel-core</code> — основная часть babel, <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">babel-preset-env</code> — пресет, определяющий, какие новые фичи JavaScript преобразовывать, а <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">babel-loader</code> — это пакет, позволяющий babel работать с webpack. Мы можем настроить webpack для использования <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">babel-loader</code>, отредактировав файл <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">webpack.config.js</code>, как показано ниже:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">// webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}
]
}
};</code>
<p>Этот синтаксис может ломать мозг (к счастью, это не то, что мы будем часто редактировать). Мы просим webpack искать любые .js-файлы (исключая те, что в папке <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node_modules</code>) и применять преобразование babel с помощью <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">babel-loader</code> через пресет <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">babel-preset-env</code>. <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://webpack.js.org/concepts/configuration/" rel="noopener noreferrer" target="_blank">Здесь</a> вы можете узнать больше о конфигурационном синтаксисе webpack.</p>
<p><em>У нас есть пример полностью настроенного пакета со всеми описанными штуками — <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://github.com/hexlet-boilerplates/nodejs-package" rel="noopener noreferrer" target="_blank">Hexlet Boilerplates: nodejs</a>, — прим. ред.</em></p>
<p>Теперь, когда все настроено, мы можем начать писать фичи ES2015 в JavaScript! Ниже пример <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://babeljs.io/learn-es2015/#ecmascript-2015-features-template-strings" rel="noopener noreferrer" target="_blank">ES2015 template string</a> в файле <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">// index.js
var moment = require('moment');
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());
var name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`);</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Мы также можем использовать <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://babeljs.io/learn-es2015/#ecmascript-2015-features-modules" rel="noopener noreferrer" target="_blank">импорты ES2015</a> вместо <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require</code> для загрузки модулей. Это то, что вы сегодня увидите во многих базах кода:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">// index.js
import moment from 'moment';
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());
var name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`);</code>
<p>В этом примере синтаксис <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">import</code> не сильно отличается от синтаксиса <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require</code>, но <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">import</code> имеет дополнительную гибкость для более сложных случаев. Поскольку мы изменили <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code>, нам нужно снова запустить webpack в командной строке:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ ./node_modules/.bin/webpack</code>
<p>Теперь вы можете обновить <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.html</code> в браузере. На момент написания этой статьи большинство современных браузеров поддерживают все фичи ES2015, поэтому иногда трудно определить, выполнил ли свою работу babel. Вы можете протестировать его в браузере более старой версии, вроде IE9, или покопаться в <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">bundle.js</code> и найти строку транспайлированного кода:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">// bundle.js
// ...
console.log('Hello ' + name + ', how are you ' + time + '?');
// ...</code>
<p>Здесь видно, как babel преобразовал интерполированную строку ES2015 в обычную конкатенацию чтобы сохранить совместимость с браузером. Хотя этот пример не слишком захватывающий, способность преобразовать код — очень мощный инструмент. В JavaScript есть некоторые интересные языковые фичи, вроде <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ponyfoo.com/articles/understanding-javascript-async-await" rel="noopener noreferrer" target="_blank">async/await</a>, которые вы можете начать использовать прямо сейчас, чтобы писать код лучше. И хотя transpilation иногда выглядит утомительным и болезненным занятием, оно привело к резким улучшениям в языке за последние несколько лет, так как сегодня люди тестируют завтрашние фичи.</p>
<p>Мы почти закончили, но в нашем воркфлоу все еще есть неотполированные грани. Если нас беспокоит производительность, мы должны <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://en.wikipedia.org/wiki/Minification_%28programming%29" rel="noopener noreferrer" target="_blank">минимизировать</a> бандл-файл, что должно быть уже достаточно лёгкой задачей, поскольку этап сборки уже встроен. Нам также нужно повторно запускать команду webpack каждый раз, когда мы меняем JavaScript. Поэтому следующее, что мы рассмотрим — это несколько удобных инструментов для решения этих проблем.</p>
<blockquote>
<h3 id="heading-3-8">Также полезно:</h3>
<p>С чего <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/blog/posts/js-s-chego-nachat?promo_name=blog&promo_position=article-body&promo_type=link&promo_start=041024" rel="noopener noreferrer" target="_blank">начать изучение JavaScript</a> и как это делать эффективно</p>
</blockquote>
<h3 id="heading-3-9">Использование task runner (скрипты npm)</h3>
<p>Теперь, когда мы вложились в использование этапа сборки для работы с модулями JavaScript, имеет смысл использовать task runner, инструмент, который автоматизирует различные части процесса сборки. Для фронтенд разработки в задачи включена минимизация кода, оптимизация изображений, прогон тестов и т.д.</p>
<p>В 2013 году Grunt был самым популярным таск-раннером во фронтенде, чуть позже появился Gulp. Оба полагаются на плагины, которые охватывают другие инструменты командной строки. Сегодня более популярный выбор — использовать возможности скриптов, встроенных в сам менеджер пакетов npm, который не использует плагины, а напрямую работает с другими инструментами командной строки.</p>
<p>Давайте напишем несколько npm-скриптов, чтобы упростить использование webpack. Это простое изменение файла <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">package.json</code> как в примере ниже:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --progress -p",
"watch": "webpack --progress --watch"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"webpack": "^3.7.1"
}
}</code>
<p>Здесь мы добавили два новых скрипта, <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">build</code> и <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">watch</code>. Чтобы запустить скрипт сборки, вы можете ввести в командной строке:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ npm run build</code>
<p>Это запустит webpack (используя конфигурацию из <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">webpack.config.js</code>, которую мы сделали ранее) с опцией <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">--progress</code>, чтобы показать прогресс в процентах и опцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-p</code>, чтобы минимизировать код для production. Чтобы запустить сценарий <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">watch</code>:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ npm run watch</code>
<p>Используется опция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-watch</code> для автоматического перезапуска webpack каждый раз, когда изменяется любой JavaScript-файл, что отлично подходит для разработки.</p>
<p>Обратите внимание, что скрипты в <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">package.json</code> могут запускать webpack без указания полного пути <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">./node_modules/.bin/webpack</code>, так как node.js знает расположение каждого пути модуля npm. Это круто! Мы можем всё сделать ещё круче, установив webpack-dev-server, отдельный инструмент, который предоставляет простой веб-сервер с живой перезагрузкой. Чтобы установить его как зависимость, введите команду:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ npm install webpack-dev-server --save-dev</code>
<p>Затем добавьте скрипт npm в <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">package.json</code>:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --progress -p",
"watch": "webpack --progress --watch",
"server": "webpack-dev-server --open"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"webpack": "^3.7.1"
}
}</code>
<p>Теперь вы можете запустить свой dev-сервер выполнив команду:</p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$ npm run server</code>
<p>Это автоматически откроет сайт <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.html</code> в вашем браузере с адресом <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">localhost: 8080</code> (по умолчанию). Каждый раз, когда вы будете менять JavaScript в <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">index.js</code>, webpack-dev-server будет перестраивать собственный упакованный JavaScript и автоматически обновлять браузер. Это удивительно полезная экономия времени, поскольку у вас есть возможность сосредоточиться на коде, а не на постоянном переключении контекстов между кодом и браузером, чтобы увидеть изменения.</p>
<p>И это только верхушка, есть еще много вариантов с webpack и webpack-dev-server (о которых вы можете прочитать <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://webpack.js.org/guides/development/" rel="noopener noreferrer" target="_blank">здесь</a>). Конечно, вы можете создавать npm-скрипты для выполнения других задач, таких как преобразование Sass в CSS, сжатие изображений, прогон тестов всего, к чему имеет доступ инструмент командной строки — всё имеет смысл. Также есть несколько отличных расширенных опций и манёвров с самими скриптами npm. Это <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://www.youtube.com/watch?v=0RYETb9YVrk" rel="noopener noreferrer" target="_blank">выступление</a> Кейт Хадсон — отличное начало.</p>
<h3 id="heading-3-10">Вывод</h3>
<p>Вот, что представляет из себя современный JavaScript в двух словах. Мы перешли с простого HTML и JS к <strong>менеджеру пакетов</strong>, чтобы автоматически загружать сторонние пакеты, <strong>упаковщику модулей</strong> для создания единого скрипт-файла, <strong>транспайлеру</strong> для использования будущих фич JavaScript и <strong>таск раннеру</strong> для автоматизации различных частей процесса сборки. Определенно тут много движимых частей, особенно для новичков. Веб-разработка когда-то была отличным стартом для новичков в программировании, именно потому что было легко начать работать; сейчас это довольно сложно, особенно потому, что различные инструменты склонны к быстрым изменениям.</p>
<p>Тем не менее, всё не так плохо, как кажется. Всё уравновешивается, особенно после внедрениея экосистемы node, как стабильного способа работы с фронтендом. Довольно приятно использовать npm в качестве менеджера пакетов, <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">require</code> или <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">import</code> для модулей, и npm-скрипты для запуска задач. Это значительно упрощает рабочий процесс по сравнению с тем, что было год или два назад!</p>
<style data-mantine-styles="inline">.__m__-_R_71derddmiub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:80%;}@media(min-width: 36em){.__m__-_R_71derddmiub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_71derddmiub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/environment"><div style="padding-top:0rem;height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="margin-bottom:var(--mantine-spacing-sm)" class="m_599a2148 mantine-Card-section" data-first-section="true"><div style="--ar-ratio:2" class="m_71ac47fc mantine-AspectRatio-root"><img class="m_9e117634 mantine-Image-root" src="/vite/assets/blog_post-7eTyeLLt.webp" loading="lazy" alt="Среды разработки: какие они бывают и чем отличаются друг от друга"/></div></div><p style="margin-bottom:var(--mantine-spacing-xs);font-size:var(--mantine-font-size-lg);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Среды разработки: какие они бывают и чем отличаются друг от друга</p><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Сооснователь Хекслета Кирилл Мокевнин рассказывает, какие бывают среды разработки, как проводится...</p><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-sm)" class="m_4081bf90 mantine-Group-root">21 марта 2023 г.<p style="font-size:inherit" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/10-sovetov-o-tom-kak-bolee-effectivno-uchitsya-na-hexlet"><div style="padding-top:0rem;height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="margin-bottom:var(--mantine-spacing-sm)" class="m_599a2148 mantine-Card-section" data-first-section="true"><div style="--ar-ratio:2" class="m_71ac47fc mantine-AspectRatio-root"><img class="m_9e117634 mantine-Image-root" src="/vite/assets/blog_post-7eTyeLLt.webp" loading="lazy" alt="10 советов, как более эффективно учиться в Хекслете"/></div></div><p style="margin-bottom:var(--mantine-spacing-xs);font-size:var(--mantine-font-size-lg);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 советов, как более эффективно учиться в Хекслете</p><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Делимся десятью лайфхаками, которые повысят эффективность обучения на Хекслете.</p><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-sm)" class="m_4081bf90 mantine-Group-root">7 ноября 2022 г.<p style="font-size:inherit" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/effective_shell_navigation"><div style="padding-top:0rem;height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="margin-bottom:var(--mantine-spacing-sm)" class="m_599a2148 mantine-Card-section" data-first-section="true"><div style="--ar-ratio:2" class="m_71ac47fc mantine-AspectRatio-root"><img class="m_9e117634 mantine-Image-root" src="/vite/assets/blog_post-7eTyeLLt.webp" loading="lazy" alt="Как работать с командной строкой эффективно: 6 советов от разработчика Дейва Керра"/></div></div><p style="margin-bottom:var(--mantine-spacing-xs);font-size:var(--mantine-font-size-lg);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Как работать с командной строкой эффективно: 6 советов от разработчика Дейва Керра</p><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Чем больше программист работает в командной строке, тем сложнее ему ориентироваться в тексте и ко...</p><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-sm)" class="m_4081bf90 mantine-Group-root">28 октября 2022 г.<p style="font-size:inherit" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></a></div></div></div></div></div>
<p>Дополнительная польза и для начинающих, и для опытных разработчиков в том, что фреймворки сегодня содержат инструменты, которые упрощают процесс и начать теперь намного легче. У Ember есть <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ember-cli.com/" rel="noopener noreferrer" target="_blank">ember-cli</a>, что очень сильно повлияло на <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://cli.angular.io/" rel="noopener noreferrer" target="_blank">angular-cli</a> от Angular, <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://github.com/facebookincubator/create-react-app" rel="noopener noreferrer" target="_blank">create-react-app</a> от React, <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://github.com/vuejs/vue-cli" rel="noopener noreferrer" target="_blank">vue-cli</a> от Vue и т. д. Все эти инструменты создают проект с полным набором того, что вам нужно — всё, что вам нужно сделать, это начать писать код. Тем не менее, эти инструменты не волшебные, они просто задают правильную базовую структуру, но вы можете оказаться в ситуации, когда вам потребуется выполнить дополнительную настройку с помощью webpack, babel и т.д. Поэтому очень важно понять, что делает каждый из этих инструментов, что мы и сделали в этой статье.</p>
<p>Работа с современным JavaScript — не всегда самая приятная штука, потому что он постоянно меняется и развивается со скоростью звука. Но, хоть иногда некоторые моменты и кажутся изобретением колеса, быстрая эволюция JavaScript продвинула инновации вроде горячей перезагрузки, инструмента статического анализа и отладки в контексте времени. Сегодня быть разработчиком — захватывающе, и я надеюсь, что эта информация может послужить путеводителем, который поможет вам в вашем путешествии!</p></div><div class=""><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap;margin-bottom:var(--mantine-spacing-lg)" class="m_4081bf90 mantine-Group-root"><div class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:var(--mantine-spacing-xs);color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-user "><path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0"></path><path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"></path></svg></div><p style="margin-inline-end:var(--mantine-spacing-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Natalia Bass</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">8 лет назад</p></div><div style="align-items:center" class="m_8bffd616 mantine-Flex-root __m__-_R_5dirddmiub_"><a style="display:inline-flex" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/modern-js-explained/votes"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:var(--mantine-spacing-xs);color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-thumb-up "><path d="M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3"></path></svg></div></a><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">12</p></div></div></div><div style="background-color:var(--mantine-color-indigo-light);border:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding:var(--mantine-spacing-xl)" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h4)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Читайте также:</p><ul style="margin-inline-start:var(--mantine-spacing-lg)" class="m_abbac491 mantine-List-root"><li style="margin-bottom:var(--mantine-spacing-sm)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><a style="color:inherit" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/environment">Среды разработки: какие они бывают и чем отличаются друг от друга</a></span></div></li><li style="margin-bottom:var(--mantine-spacing-sm)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><a style="color:inherit" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/10-sovetov-o-tom-kak-bolee-effectivno-uchitsya-na-hexlet">10 советов, как более эффективно учиться в Хекслете</a></span></div></li><li style="margin-bottom:var(--mantine-spacing-sm)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><a style="color:inherit" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/effective_shell_navigation">Как работать с командной строкой эффективно: 6 советов от разработчика Дейва Керра</a></span></div></li></ul></div><div style="margin-block:var(--mantine-spacing-xl)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div></div><div></div></div><style data-mantine-styles="inline">.__m__-_R_lmiub_{--col-flex-grow:auto;--col-flex-basis:100%;--col-max-width:100%;}@media(min-width: 48em){.__m__-_R_lmiub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}@media(min-width: 62em){.__m__-_R_lmiub_{--col-flex-grow:auto;--col-flex-basis:33.333333333333336%;--col-max-width:33.333333333333336%;}}</style><div class="m_96bdd299 mantine-Grid-col __m__-_R_lmiub_ mantine-visible-from-md"><div style="background-color:var(--mantine-color-indigo-light);border:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-bottom:var(--mantine-spacing-xl);padding:var(--mantine-spacing-xl);width:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><div style="margin-bottom:var(--mantine-spacing-md)" class="m_4451eb3a mantine-Center-root" data-inline="true"><p style="font-size:var(--mantine-font-size-h4)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Категории</p></div><ul class="m_abbac491 mantine-List-root"><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Мотивация">Мотивация</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Истории успеха">Истории успеха</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Дневник студента">Дневник студента</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Код">Код</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Карьера">Карьера</button></span></div></li></ul></div><div style="justify-content:end;margin-top:0rem;position:sticky;top:calc(5rem * var(--mantine-scale))" class="m_8bffd616 mantine-Flex-root __m__-_R_5dlmiub_"><div tabindex="0" style="cursor:pointer"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses_web_development?promo_name=program_category&promo_position=blog_post&promo_creative=card&promo_type=card"><div style="background-color:var(--mantine-color-default);border:calc(0.0625rem * var(--mantine-scale)) solid var(--mantine-color-default-border);padding-inline:var(--mantine-spacing-xl);padding-top:var(--mantine-spacing-xl);padding-bottom:var(--mantine-spacing-xs);width:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><div class="m_4451eb3a mantine-Center-root" data-inline="true"><p style="font-size:var(--mantine-font-size-h4)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Курсы по веб-разработке</p></div><img class="m_9e117634 mantine-Image-root" src="/vite/assets/development-BVihs_d5.png"/><p style="margin-bottom:var(--mantine-spacing-xs);text-align:right" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></a></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>