Эта статья включена в нашу книгу « JavaScript: лучшие практики» . Будьте в курсе быстро меняющихся лучших практик современного JavaScript.
Нет сомнений, что экосистема JavaScript быстро меняется. Мало того, что с появлением ES2015 (он же ES6) претерпел быстрые изменения и не только новые инструменты и структуры, но и сам язык. Понятно, что было написано много статей с жалобами на то, как трудно в наши дни освоить современную разработку JavaScript.
В этой статье я познакомлю вас с современным JavaScript. Мы посмотрим на последние изменения языка и получим обзор инструментов и методов, используемых в настоящее время для написания интерфейсных веб-приложений. Если вы только начинаете изучать язык или не трогали его в течение нескольких лет и задаетесь вопросом, что случилось с JavaScript, который вы привыкли знать, эта статья для вас.
Примечание о Node.js
Node.js — это среда выполнения, которая позволяет программам на стороне сервера быть написанными на JavaScript. Возможно иметь полнофункциональные JavaScript-приложения, в которых как внешний, так и внутренний интерфейс приложения написаны на одном языке. Хотя эта статья посвящена разработке на стороне клиента, Node.js по-прежнему играет важную роль.
Появление Node.js оказало значительное влияние на экосистему JavaScript, представив менеджер пакетов npm и популяризовав формат модуля CommonJS. Разработчики начали создавать более инновационные инструменты и разрабатывать новые подходы, чтобы стирать грань между браузером, сервером и нативными приложениями.
JavaScript ES2015 +
В 2015 году была выпущена шестая версия ECMAScript — спецификация, определяющая язык JavaScript — под названием ES2015 (до сих пор часто упоминаемая как ES6). Эта новая версия включает существенные дополнения к языку, что делает его более простым и выполнимым для создания амбициозных веб-приложений. Но улучшения не прекращаются с ES2015; каждый год выпускается новая версия.
Объявление переменных
В JavaScript теперь есть два дополнительных способа объявления переменных: let и const .
let
var
Хотя var
let
// ES5
for (var i = 1; i < 5; i++) {
console.log(i);
}
// <-- logs the numbers 1 to 4
console.log(i);
// <-- 5 (variable i still exists outside the loop)
// ES2015
for (let j = 1; j < 5; j++) {
console.log(j);
}
console.log(j);
// <-- 'Uncaught ReferenceError: j is not defined'
Использование const
Для примитивных значений, таких как строки и числа, это приводит к чему-то похожему на константу, поскольку вы не можете изменить значение после того, как оно было объявлено:
const name = 'Bill';
name = 'Steve';
// <-- 'Uncaught TypeError: Assignment to constant variable.'
// Gotcha
const person = { name: 'Bill' };
person.name = 'Steve';
// person.name is now Steve.
// As we're not changing the object that person is bound to, JavaScript doesn't complain.
Функции стрелок
Функции со стрелками обеспечивают более чистый синтаксис для объявления анонимных функций (лямбда-выражений), отбрасывания ключевого слова function
return
Это может позволить вам написать код функционального стиля более приятным способом:
// ES5
var add = function(a, b) {
return a + b;
}
// ES2015
const add = (a, b) => a + b;
Другая важная особенность функций со стрелками заключается в том, что они наследуют значение this
function Person(){
this.age = 0;
// ES5
setInterval(function() {
this.age++; // |this| refers to the global object
}, 1000);
// ES2015
setInterval(() => {
this.age++; // |this| properly refers to the person object
}, 1000);
}
var p = new Person();
Улучшенный синтаксис класса
Если вы поклонник объектно-ориентированного программирования, вам может понравиться добавление классов к языку поверх существующего механизма, основанного на прототипах. Хотя это в основном просто синтаксический сахар, он обеспечивает более чистый синтаксис для разработчиков, пытающихся эмулировать классическую объектную ориентацию с прототипами.
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
Обещания / Асинхронные функции
Асинхронная природа JavaScript уже давно представляет проблему; любое нетривиальное приложение рискует попасть в ад обратного вызова при работе с такими вещами, как запросы Ajax.
К счастью, в ES2015 добавлена встроенная поддержка обещаний . Обещания представляют значения, которые не существуют на момент вычисления, но могут быть доступны позже, что делает управление асинхронными вызовами функций более управляемым, не вдаваясь в глубоко вложенные обратные вызовы.
ES2017 представил асинхронные функции (иногда называемые async / await), которые вносят улучшения в эту область, позволяя обрабатывать асинхронный код так, как если бы он был синхронным:
async function doAsyncOp () {
var val = await asynchronousOperation();
console.log(val);
return val;
};
Модули
Еще одна важная функция, добавленная в ES2015, — это собственный формат модулей, делающий определение и использование модулей частью языка. Загрузка модулей ранее была доступна только в виде сторонних библиотек. Мы рассмотрим модули более подробно в следующем разделе.
Есть и другие функции, о которых мы не будем здесь говорить, но мы рассмотрели некоторые основные различия, которые вы, вероятно, заметите, глядя на современный JavaScript. Вы можете проверить полный список с примерами на странице Learn ES2015 на сайте Babel , которая может оказаться полезной для ознакомления с языком. Некоторые из этих функций включают в себя строки шаблонов, переменные и константы в области блока, итераторы, генераторы, новые структуры данных, такие как Map и Set, и многое другое.
Чтобы узнать больше о ES2015, ознакомьтесь с нашим Премиум курсом: погружение в ES2015 .
Кодирование
Линтеры — это инструменты, которые анализируют ваш код и сравнивают его с набором правил, проверяют синтаксические ошибки, форматируют и рекомендуют. Хотя использование линтера рекомендуется всем, это особенно полезно, если вы только начинаете. При правильной настройке для вашего редактора кода / IDE вы можете получить мгновенную обратную связь, чтобы убедиться, что вы не застряли с синтаксическими ошибками при изучении новых языковых функций.
Вы можете проверить ESLint , который является одним из самых популярных и поддерживает ES2015 +.
Модульный код
Современные веб-приложения могут иметь тысячи (даже сотни тысяч) строк кода. Работа с таким размером становится практически невозможной без механизма организации всего в более мелкие компоненты, написания специализированных и изолированных фрагментов кода, которые могут при необходимости использоваться повторно контролируемым образом. Это работа модулей.
CommonJS модули
За прошедшие годы появилось несколько форматов модулей, самым популярным из которых является CommonJS . Это формат модуля по умолчанию в Node.js, и его можно использовать в клиентском коде с помощью компоновщиков модулей, о которых мы вскоре поговорим.
Он использует объект module
require()
// lib/math.js
function sum(x, y) {
return x + y;
}
const pi = 3.141593
module.exports = {
sum: sum,
pi: pi
};
// app.js
const math = require("lib/math");
console.log("2π = " + math.sum(math.pi, math.pi));
Модули ES2015
ES2015 представляет способ определения и использования компонентов прямо на языке, который ранее был возможен только для сторонних библиотек. Вы можете иметь отдельные файлы с нужной вам функциональностью и экспортировать только определенные части, чтобы сделать их доступными для вашего приложения.
Примечание: поддержка встроенного браузера для модулей ES2015 все еще находится в стадии разработки, поэтому в настоящее время вам необходимы некоторые дополнительные инструменты, чтобы использовать их.
Вот пример:
// lib/math.js
export function sum(x, y) {
return x + y;
}
export const pi = 3.141593;
Здесь у нас есть модуль, который экспортирует функцию и переменную. Мы можем включить этот файл в другой и использовать эти экспортированные функции:
// app.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
Или мы также можем быть конкретными и импортировать только то, что нам нужно:
// otherApp.js
import {sum, pi} from "lib/math";
console.log("2π = " + sum(pi, pi));
Эти примеры были взяты с веб-сайта Babel . Для получения более подробной информации, ознакомьтесь с Понимание модулей ES6 .
Управление пакетами
Другие языки уже давно имеют свои собственные репозитории и менеджеры пакетов, чтобы упростить поиск и установку сторонних библиотек и компонентов. Node.js поставляется с собственным менеджером пакетов и репозиторием, npm . Хотя есть и другие доступные менеджеры пакетов, npm стал де-факто менеджером пакетов JavaScript и считается самым большим реестром пакетов в мире.
В репозитории npm вы можете найти сторонние модули, которые вы можете легко загрузить и использовать в своих проектах с помощью одной команды npm install <package>
Пакеты загружаются в локальный каталог node_modules
Загруженные вами пакеты могут быть зарегистрированы как зависимости вашего проекта в файле package.json вместе с информацией о вашем проекте или модуле (который сам по себе может быть опубликован в виде пакета на npm).
Вы можете определить отдельные зависимости как для разработки, так и для производства. В то время как производственные зависимости необходимы для работы пакета, зависимости разработки необходимы только для разработчиков пакета.
Пример файла package.json
{
"name": "demo",
"version": "1.0.0",
"description": "Demo package.json",
"main": "main.js",
"dependencies": {
"mkdirp": "^0.5.1",
"underscore": "^1.8.3"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Sitepoint",
"license": "ISC"
}
Инструменты сборки
Код, который мы пишем при разработке современных веб-приложений на JavaScript, почти никогда не является тем же кодом, который пойдет в производство. Мы пишем код в современной версии JavaScript, которая может не поддерживаться браузером, мы интенсивно используем сторонние пакеты, которые находятся в папке node_modules
Модуль комплектации
При написании чистого многократно используемого кода с модулями ES2015 / CommonJS нам нужен какой-то способ загрузки этих модулей (по крайней мере, пока браузеры не поддерживают загрузку модулей ES2015 изначально). Включение набора тегов сценариев в ваш HTML на самом деле нереальный вариант, поскольку он быстро станет громоздким для любого серьезного приложения, а все эти отдельные HTTP-запросы могут снизить производительность.
Мы можем включить все модули, где они нам нужны, с помощью оператора import
require
Именно этот пакетный файл мы собираемся загрузить на наш сервер и включить в наш HTML. Он будет включать все ваши импортированные модули и их необходимые зависимости.
В настоящее время есть несколько популярных вариантов для этого, наиболее популярными из которых являются Webpack , Browserify и Rollup.js . Вы можете выбрать один или другой в зависимости от ваших потребностей.
Если вы хотите узнать больше о связывании модулей и о том, как они вписываются в общую картину разработки приложений, я рекомендую прочитать « Понимание модулей JavaScript: объединение и перенос» .
Transpilation
Хотя поддержка современного JavaScript довольно хороша в новых браузерах , ваша целевая аудитория может включать устаревшие браузеры и устройства с частичной поддержкой или без нее.
Чтобы наш современный JavaScript работал, нам нужно перевести код, который мы пишем, в его эквивалент в более ранней версии (обычно ES5). Стандартным инструментом для этой задачи является Babel — компилятор, который переводит ваш код в совместимый код для большинства браузеров. Таким образом, вам не нужно ждать, пока поставщики реализуют все; Вы можете просто использовать все современные функции JS.
Есть несколько функций, которые нуждаются не только в переводе синтаксиса. Babel включает в себя Polyfill, который имитирует некоторые механизмы, необходимые для некоторых сложных функций, таких как обещания.
Сборка систем и задач
Объединение и транспортировка модулей — это только два процесса сборки, которые нам могут понадобиться в наших проектах. Другие включают минимизацию кода (для уменьшения размеров файлов), инструменты для анализа и, возможно, задачи, которые не имеют ничего общего с JavaScript, такие как оптимизация изображений или предварительная обработка CSS / HTML.
Управление задачами может стать трудоемким занятием, и нам нужен способ автоматизировать его, чтобы выполнять все с помощью более простых команд. Двумя наиболее популярными инструментами для этого являются Grunt.js и Gulp.js , которые обеспечивают способ упорядочивания ваших задач в группы.
Например, у вас может быть такая команда, как gulp build
Вместо того, чтобы запоминать три команды и связанные с ними аргументы по порядку, мы просто выполняем одну, которая автоматически обрабатывает весь процесс.
Где бы вы ни находились, вручную организовывая этапы обработки для вашего проекта, подумайте, можно ли его автоматизировать с помощью бегунка задач.
Дальнейшее чтение: Введение в Gulp.js.
Архитектура приложений
Веб-приложения имеют различные требования от веб-сайтов. Например, хотя перезагрузка страницы может быть приемлемой для блога, это не относится к приложениям, таким как Google Docs. Ваше приложение должно вести себя как можно ближе к настольному. В противном случае удобство использования будет скомпрометировано.
Веб-приложения старого стиля обычно выполнялись путем отправки нескольких страниц с веб-сервера, а когда требовался большой динамизм, контент загружался через Ajax, заменяя фрагменты HTML в соответствии с действиями пользователя. Хотя это был большой шаг вперед к более динамичной сети, у нее, безусловно, были свои сложности. Отправка фрагментов HTML или даже целых страниц каждого пользовательского действия представляла собой пустую трата ресурсов, особенно времени, с точки зрения пользователя. Юзабилити все еще не соответствовала быстродействию настольных приложений.
Чтобы улучшить ситуацию, мы создали два новых метода для создания веб-приложений — от способа представления их пользователю до способа взаимодействия между клиентом и сервером. Хотя количество JavaScript, необходимое для приложения, также резко возросло, в результате теперь приложения работают очень близко к собственным, без перезагрузки страницы или длительных периодов ожидания при каждом нажатии кнопки.
Одностраничные приложения (SPA)
Наиболее распространенная высокоуровневая архитектура для веб-приложений называется SPA , что означает одностраничное приложение . SPA — это большие фрагменты JavaScript, которые содержат все, что нужно приложению для правильной работы. Пользовательский интерфейс отображается полностью на стороне клиента, поэтому перезагрузка не требуется. Единственное, что изменяется, — это данные внутри приложения, которые обычно обрабатываются с помощью удаленного API через Ajax или другой асинхронный метод связи.
Недостатком этого подхода является то, что приложение загружается дольше в первый раз. Однако после загрузки переходы между представлениями (страницами), как правило, выполняются намного быстрее, поскольку между клиентом и сервером передаются только чистые данные.
Универсальные / Изоморфные Приложения
Несмотря на то, что SPA обеспечивают отличное взаимодействие с пользователем, в зависимости от ваших потребностей, они могут быть не оптимальным решением, особенно если вам требуется более быстрое начальное время отклика или оптимальная индексация поисковыми системами.
Существует довольно недавний подход к решению этих проблем, называемый изоморфными (или универсальными) приложениями JavaScript. В архитектуре этого типа большая часть кода может выполняться как на сервере, так и на клиенте. Вы можете выбрать, что вы хотите визуализировать на сервере, для более быстрой начальной загрузки страницы, и после этого клиент берет на себя рендеринг, пока пользователь взаимодействует с приложением. Поскольку страницы изначально отображаются на сервере, поисковые системы могут правильно их проиндексировать.
развертывание
В современных приложениях JavaScript код, который вы пишете, не совпадает с кодом, развертываемым для производства: вы развертываете только результат процесса сборки. Рабочий процесс для достижения этой цели может варьироваться в зависимости от размера вашего проекта, количества разработчиков, работающих над ним, и иногда используемых вами инструментов / библиотек.
Например, если вы работаете один над простым проектом, каждый раз, когда вы готовы к развертыванию, вы можете просто запустить процесс сборки и загрузить полученные файлы на веб-сервер. Помните, что вам нужно всего лишь загрузить полученные файлы из процесса сборки (перенос, объединение модулей, минификация и т. Д.), Который может быть только одним файлом .js
Вы можете иметь такую структуру каталогов:
├── dist
│ ├── app.js
│ └── index.html
├── node_modules
├── src
│ ├── lib
│ │ ├── login.js
│ │ └── user.js
│ ├── app.js
│ └── index.html
├── gulpfile.js
├── package.json
└── README
Таким образом, у вас есть все файлы приложения в каталоге src
lib
Затем вы можете запустить Gulp, который выполнит инструкции из gulpfile.js
dist
Примечание: если у вас есть файлы, которые не нуждаются в обработке, вы можете просто скопировать их из src
dist
Вы можете настроить задачу для этого в вашей системе сборки.
Теперь вы можете просто загрузить файлы из каталога dist
Развитие команды
Если вы работаете с другими разработчиками, вероятно, вы также используете общий репозиторий кода, такой как GitHub, для хранения проекта. В этом случае вы можете запустить процесс сборки непосредственно перед выполнением коммитов и сохранить результат с другими файлами в репозитории Git, чтобы затем загрузить их на рабочий сервер.
Однако хранение встроенных файлов в хранилище может привести к ошибкам, если несколько разработчиков работают вместе, и вы можете захотеть сохранить все в чистоте от артефактов сборки. К счастью, есть более эффективный способ решения этой проблемы: вы можете поместить сервис, такой как Jenkins , Travis CI , CircleCI и т. Д., В центр процесса, чтобы он мог автоматически строить ваш проект после каждой фиксации в репозитории. Разработчикам нужно только беспокоиться о внесении изменений в код, не создавая проект каждый раз. Хранилище также поддерживается в чистоте от автоматически сгенерированных файлов, и, в конце концов, у вас все еще есть встроенные файлы, доступные для развертывания.
Вывод
Переход от простых веб-страниц к современным приложениям JavaScript может показаться пугающим, если вы в последние годы ушли от веб-разработки, но я надеюсь, что эта статья была полезной в качестве отправной точки. Я связался с более глубокими статьями по каждой теме, где это возможно, чтобы вы могли исследовать дальше.
И помните, что если в какой-то момент, после просмотра всех доступных вариантов, все кажется ошеломляющим и запутанным, просто помните принцип KISS и используйте только то, что вам нужно, а не все, что у вас есть. В конце концов, решение проблем — это главное, а не использование всего нового.
Какой у вас опыт изучения современной разработки JavaScript? Есть ли что-то, чего я не коснулся здесь, что вы хотели бы увидеть в будущем? Я хотел бы услышать от вас!