Перед вами третья часть перевода руководства по Node.js. Сегодня мы поговорим о выборе
Часть 2: JavaScript, V8, некоторые приёмы разработки
Часть 3: Хостинг, REPL, работа с консолью, модули
Хостинг для Node.js-приложений
Выбор
▍Самый простой вариант хостинга: локальный туннель
Даже если вашему компьютеру назначен динамический IP-адрес или вы находитесь за NAT, вы можете развернуть на нём своё приложение и обслуживать запросы пользователей к нему, используя локальный туннель.
Этот вариант подходит для быстрой организации тестирования, для того, чтобы устроить демонстрацию продукта, или для того, чтобы дать доступ к приложению очень маленькой группе людей.
Для организации локальных туннелей есть очень хороший сервис, ngrok, доступный для множества платформ.
Используя ngrok, достаточно выполнить команду вида ngrok PORT
и указанный вами порт будет доступен из интернета. У вас при этом, если вы пользуетесь бесплатной версией сервиса, будет адрес в домене ngrok.io
. Если же вы решите оформить платную подписку, вы сможете использовать собственные доменные имена, и, кроме того, сможете повысить безопасность решения (пользуясь ngrok, вы открываете доступ к своему компьютеру всему интернету).
Ещё один инструмент, который можно использовать для организации локальных туннелей, называется localtunnel.
▍Среды для развёртывания Node.js-проектов, не требующие настройки
Glitch
Glitch — это интерактивная среда и платформа для быстрой разработки приложений, которая позволяет разворачивать их в поддоменах glitch.com. Собственные домены пользователей эта платформа пока не поддерживает, при работе с ней существуют некоторые ограничения, но она отлично подходит для работы над прототипами приложений. Дизайн Glitch выглядит довольно забавно (пожалуй, это можно записать в плюсы данной платформы), но это не некая «игрушечная», ограниченная донельзя среда. Здесь к вашим услугам возможность работы с Node.js, CDN, защищённое хранилище для конфиденциальной информации, возможности обмена данными с GitHub и многое другое.
Проектом Glitch занимается та же компания, которая стоит за FogBugz и Trello (она же является одним из создателей StackOverflow). Я часто использую эту платформу для демонстрации приложений.
Codepen
Codepen — это замечательная платформа, вокруг которой сформировалось интересное сообщество. Здесь можно создавать проекты, включающие в себя множество файлов, и разворачивать их с использованием собственного домена.
▍Бессерверные среды
Бессерверные платформы позволяют публиковать приложения и при этом совершенно не думать о серверах, об их настройке или об управлении ими. Парадигма бессерверных вычислений заключается в том, что приложения публикуют в виде функций, которые реагируют на обращения к сетевой конечной точке. Подобный подход к развёртыванию приложений ещё называют FAAS (Functions As A Service, функция как услуга).
Вот пара популярных решений в этой области:
- Фреймворк Serverless.
- Библиотека Standard.
Оба эти проекта предоставляют разработчику некий уровень абстракции, позволяющий публиковать приложения на различных FAAS-платформах, например, на Amazon AWS Lambda, на Microsoft Azure и на Google Cloud.
▍PAAS-решения
PAAS (Platform As A Service, платформа как услуга) — это платформы, которые берут на себя заботу обо многих вещах, о которых, в обычных условиях, должен заботиться разработчик, развёртывающий приложение.
Zeit Now
Zeit — это интересный вариант для развёртывания приложений. Развёртывание, при использовании этой платформы, сводится к вводу в терминале команды now
. Существует бесплатная версия Zeit, при работе с ней действуют некоторые ограничения. Есть и платная, более мощная версия этой платформы. Пользуясь Zeit, вы можете попросту не думать о том, что для работы вашего приложения нужен сервер. Вы просто разворачиваете приложение, а всё остальное находится в ведении этой платформы.
Nanobox
Создатели платформы Nanobox, в возможности которой входит развёртывание Node.js-приложений, называют её PAAS V2.
Heroku
Heroku — это ещё одна замечательная платформа для размещения Node.js-приложений. Вот хорошая статья о том, как с ней работать.
Microsoft Azure
Azure — это облачная платформа от Microsoft. В её документации есть раздел, посвящённый Node.js-приложениям.
Платформа Google Cloud
Google Cloud представляет собой замечательную среду для развёртывания Node.js-приложений. Вот соответствующий раздел её документации.
▍VPS-хостинг
Существует множество платформ, предоставляющих услуги VPS-хостинга. Общей чертой таких платформ является тот факт, что разработчик получает в своё распоряжение виртуальный сервер, самостоятельно устанавливает на него операционную систему (Linux или Windows), самостоятельно развёртывает приложения.
Среди платформ, предоставляющих VPS-услуги, которых существует великое множество, можно отметить следующие, которыми я пользовался и которые мог бы порекомендовать другим:
- Digital Ocean
- Linode
- Amazon Web Services (в частности, хотелось бы отметить сервис AWS Elastic Beanstalk, облегчающий развёртывание приложений и управление ресурсами AWS).
От себя добавим, что компания RUVDS тоже оказывает услуги VPS-хостинга. Мы лицензированы ФСТЭК, наши клиенты застрахованы AIG, у нас есть четыре дата-центра в разных странах. Есть собственный дата-центр RUCLOUD уровня TIER 3 в г. Королеве, Московская область, а также гермозоны в дата-центрах Deltalis (Швейцария), Лондоне Equinix LD8 (Великобритания), и ММТС-9 (Москва, Россия). Все гермозоны отвечают уровню надежности не ниже TIER 3.
Партнерами компании являются АО «ФИНАМ», финансовая группа «БКС», Национальный расчетный депозитарий (Московская биржа), АО «ВЦИОМ», компания «Гарс-Телеком», оператор такси Gett, оператор доставки Delivery Club и многие другие.
▍Обычный сервер
Ещё одно решение в области
Использование Node.js в режиме REPL
Аббревиатура REPL расшифровывается как Read-Evaluate-Print-Loop (цикл «чтение — вычисление — вывод»). Использование REPL — это отличный способ быстрого исследования возможностей Node.js.
Как вы уже знаете, для запуска скриптов в Node.js используется команда node
, выглядит это так:
node script.js
Если ввести такую же команду, но не указывать имя файла, Node.js будет запущен в режиме REPL:
node
Если вы попробуете сейчас ввести такую команду в своём терминале, то в результате увидите примерно следующее:
> node
>
Node.js теперь находится в режиме ожидания. Система ждёт, что мы введём в командной строке какой-нибудь JavaScript-код, который она будет выполнять.
Для начала попробуем что-нибудь очень простое:
> console.log('test')
test
undefined
>
Тут мы предложили Node.js выполнить команду, используемую для вывода данных в консоль. Первое значение, test
, представляет собой то, что вывела команда console.log('test')
. Второе значение, undefined
, это то, что возвратила функция console.log()
.
После завершения выполнения команды появляется приглашение REPL, это означает, что мы можем ввести здесь новую команду.
▍Автозавершение команд с помощью клавиши Tab
REPL — это интерактивная среда. Если в процессе написания кода нажать клавишу Tab
на клавиатуре, REPL попытается автоматически завершить ввод, подобрав, например, подходящее имя уже объявленной вами переменной или имя некоего стандартного объекта.
▍Исследование объектов JavaScript
Введите в командную строку имя какого-нибудь стандартного объекта JavaScript, например — Number
, добавьте после него точку и нажмите клавишу Tab
.
REPL выведет список свойств и методов объекта, с которыми может взаимодействовать разработчик:
Исследование объекта Number
▍Исследование глобальных объектов
Для того чтобы узнать, с какими глобальными объектам Node.js вы можете работать, введите в терминале команду global.
и нажмите Tab
.
Исследование глобальных объектов
▍Специальная переменная _
Переменная _
(знак подчёркивания) хранит результат последней выполненной операции. Эту переменную можно использовать в составе команд, вводимых в консоль.
▍Команды, начинающиеся с точки
В режиме REPL можно пользоваться некоторыми специальными командами, которые начинаются с точки. Вот они:
- Команда
.help
выводит справочные сведения по командам, начинающимся с точки. - Команда
.editor
переводит систему в режим редактора, что упрощает ввод многострочного JavaScript-кода. После того, как находясь в этом режиме, вы введёте всё, что хотели, для запуска кода воспользуйтесь командойCtrl+D
. - Команда
.break
позволяет прервать ввод многострочного выражения. Её использование аналогично применению сочетания клавишCtrl+C
. - Команда
.clear
очищает контекст REPL, а так же прерывает ввод многострочного выражения. - Команда
.load
загружает в текущий сеанс код из JavaScript-файла. - Команда
.save
сохраняет в файл всё, что было введено во время REPL-сеанса. - Команда
.exit
позволяет выйти из сеанса REPL, она действует так же, как два последовательных нажатия сочетания клавишCtrl+C
.
Надо отметить, что REPL распознаёт ввод многострочных выражений и без использования команды .editor
.
Например, мы начали вводить код итератора:
[1, 2, 3].forEach(num => {
Если, после ввода фигурной скобки, нажать на клавишу Enter
, REPL перейдёт на новую строку, приглашение в которой будет выглядеть как три точки. Это указывает на то, что мы можем вводить код соответствующего блока. Выглядит это так:
... console.log(num)
... })
Нажатие на Enter
после ввода последней скобки приведёт к выполнению выражения. Если ввести в этом режиме .break
, ввод будет прерван и выражение выполнено не будет.
Режим REPL — полезная возможность Node.js, но область её применения ограничена небольшими экспериментами. Нас же интересует нечто большее, чем возможность выполнить пару команд. Поэтому переходим к работе с Node.js в обычном режиме. А именно, поговорим о том, как Node.js-скрипты могут принимать аргументы командной строки.
Работа с аргументами командной строки в Node.js-скриптах
При запуске Node.js-скриптов им можно передавать аргументы. Вот обычный вызов скрипта:
node app.js
Передаваемые скрипту аргументы могут представлять собой как самостоятельные значения, так и конструкции вида ключ-значение. В первом случае запуск скрипта выглядит так:
node app.js flavio
Во втором — так:
node app.js name=flavio
От того, какой именно способ передачи аргументов используется, зависит то, как с ними можно будет работать в коде скрипта.
Так, для того, чтобы получить доступ к аргументам командной строки, используется стандартный объект Node.js process
. У него есть свойство argv
, которое представляет собой массив, содержащий, кроме прочего, аргументы, переданные скрипту при запуске.
Первый элемент массива argv
содержит полный путь к файлу, который выполняется при вводе команды node
в командной строке.
Второй элемент — это путь к выполняемому файлу скрипта.
Все остальные элементы массива, начиная с третьего, содержат то, что было передано скрипту при его запуске.
Перебор аргументов, имеющихся в argv
(сюда входят и путь к node
, и путь к выполняемому файлу скрипта), можно организовать с использованием цикла forEach
:
process.argv.forEach((val, index) => {
console.log(`${index}: ${val}`)
})
Если два первых аргумента вас не интересуют, на основе argv
можно сформировать новый массив, в который войдёт всё из argv
кроме первых двух элементов:
const args = process.argv.slice(2)
Предположим, при запуске скрипта, ему передали лишь один аргумент, в виде самостоятельного значения:
node app.js flavio
Обратиться к этому аргументу можно так:
const args = process.argv.slice(2)
args[0]
Теперь попробуем воспользоваться конструкцией вида ключ-значение:
node app.js name=flavio
При таком подходе, после формирования массива args
, в args[0]
окажется строка name=flavio
. Прежде чем пользоваться аргументом, эту строку надо разобрать. Самый удобный способ это сделать заключается в использовании библиотеки minimist, которая предназначена для облегчения работы с аргументами командной строки:
const args = require('minimist')(process.argv.slice(2))
args['name'] //flavio
Теперь рассмотрим вывод данных в консоль.
Вывод данных в консоль с использованием модуля console
Стандартный модуль Node.js console даёт разработчику массу возможностей по взаимодействию с командной строкой во время выполнения программы. В целом, это — то же самое, что объект console
, используемый в браузерном JavaScript. Пожалуй, самый простой и самый широко используемый метод модуля console
— это console.log()
, который применяется для вывода передаваемых ему строковых данных в консоль. При этом, если передать ему объект, то он, перед выводом, будет преобразован к своему строковому представлению.
Методу console.log()
можно передавать несколько значений:
const x = 'x'
const y = 'y'
console.log(x, y)
После выполнения этой последовательности инструкций в консоль попадёт и значение x
, и значение y
.
Для формирования сложных строк команда console.log()
поддерживает использование подстановочных символов, которые, при выводе данных, заменяются на соответствующие им значения в порядке очерёдности.
Например, вот команда, которая выводит текст My cat has 2 years
:
console.log('My %s has %d years', 'cat', 2)
Рассмотрим особенности подстановочных символов:
%s
форматирует значение в виде строки.%d
или%i
форматируют значение в виде целого числа.%f
форматирует значение в виде числа с плавающей точкой.%O
используется для вывода строковых представлений объектов.
Вот ещё один пример использования подстановочных символов:
console.log('%O', Number)
▍Очистка консоли
Для очистки консоли используется команда console.clear()
(её поведение в разных терминалах может различаться).
▍Подсчёт элементов
Сейчас мы рассмотрим полезный метод console.count()
. Взгляните на этот код:
const x = 1
const y = 2
const z = 3
console.count(
'The value of x is ' + x + ' and has been checked .. how many times?'
)
console.count(
'The value of x is ' + x + ' and has been checked .. how many times?'
)
console.count(
'The value of y is ' + y + ' and has been checked .. how many times?'
)
Метод count()
подсчитывает количество выводов строк и выводит результат рядом с ними.
Используя этот метод можно, в следующем примере, посчитать яблоки и апельсины:
const oranges = ['orange', 'orange']
const apples = ['just one apple']
oranges.forEach(fruit => {
console.count(fruit)
})
apples.forEach(fruit => {
console.count(fruit)
})
▍Вывод в консоль результатов трассировки стека
Иногда бывает полезно вывести в консоль трассировку стека функции. Например, для того, чтобы ответить на вопрос о том, как мы попали в некое место программы. Сделать это можно с помощью такой команды:
console.trace()
Вот пример её использования:
const function2 = () => console.trace()
const function1 = () => function2()
function1()
Вот что произошло, когда я запустил этот код в режиме REPL:
Trace
at function2 (repl:1:33)
at function1 (repl:1:25)
at repl:1:1
at ContextifyScript.Script.runInThisContext (vm.js:44:33)
at REPLServer.defaultEval (repl.js:239:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval] (domain.js:314:12)
at REPLServer.onLine (repl.js:440:10)
at emitOne (events.js:120:20)
at REPLServer.emit (events.js:210:7)
▍Измерение времени, затраченного на выполнение некоего действия
Измерить время, которое занимает, например, выполнение некоей функции, можно с использованием методов console.time()
и console.timeEnd()
. Выглядит это так:
const doSomething = () => console.log('test')
const measureDoingSomething = () => {
console.time('doSomething()')
//вызываем функцию и замеряем время, необходимое на её выполнение
doSomething()
console.timeEnd('doSomething()')
}
measureDoingSomething()
▍Работа с stdout и stderr
Как мы уже видели, команда console.log()
отлично подходит для вывода сообщений в консоль. При её применении используется так называемый стандартный поток вывода, или stdout
.
Команда console.error()
выводит данные в стандартный поток ошибок, stderr
. Данные, отправляемые в stderr
, попадают в консоль, хотя то, что выводится в этот поток, можно, например, перенаправить в файл журнала ошибок.
▍Использование цвета при выводе данных в консоль
Для того чтобы раскрасить выводимые в консоль тексты, можно воспользоваться escape-последовательностями, идентифицирующими цвета:
console.log('x1b[33m%sx1b[0m', 'hi!')
Если выполнить эту команду, например, в режиме REPL, текст hi
будет выведен жёлтым цветом.
Такой подход, однако, не особенно удобен. Для вывода в консоль цветных надписей удобно будет воспользоваться специализированной библиотекой, например — chalk. Эта библиотека, помимо цветового форматирования текстов, поддерживает и другие способы их стилизации. Например, с её помощью можно оформить текст полужирным, курсивным или подчёркнутым шрифтом.
Для её установки из npm воспользуйтесь такой командой:
npm install chalk
Пользоваться ей можно так:
const chalk = require('chalk')
console.log(chalk.yellow('hi!'))
Пользоваться командой chalk.yellow()
гораздо удобнее, чем escape-последовательностями, да и текст программы при таком подходе читать гораздо легче.
Для того чтобы узнать подробности о chalk, посмотрите страницу этой библиотеки на GitHub.
▍Создание индикатора выполнения операции
Индикатор выполнения операции (progress bar) может пригодиться в разных ситуациях. Для создания индикаторов выполнения, работающих в консоли, можно воспользоваться пакетом progress. Установить его можно так:
npm install progress
Ниже показан пример кода, в котором создаётся индикатор, который можно использоваться для вывода сведений о некоей задаче, состоящей из 10 шагов. В нашем случае на выполнение каждого шага уходит 100 мс. После того, как индикатор заполнится, вызывается команда clearItnerval()
и выполнение программы завершается.
const ProgressBar = require('progress')
const bar = new ProgressBar(':bar', { total: 10 })
const timer = setInterval(() => {
bar.tick()
if (bar.complete) {
clearInterval(timer)
}
}, 100)
▍Приём пользовательского ввода из командной строки
Как сделать приложения командной строки, написанные для платформы Node.js, интерактивными? Начиная с 7 версии Node.js содержит модуль readline, который позволяет принимать данные из потоков, которые можно читать, например, из process.stdin
. Этот поток, во время выполнения Node.js-программы, представляет собой то, что вводят в терминале. Данные вводятся по одной строке за раз.
Рассмотрим следующий фрагмент кода:
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
})
readline.question(`What's your name?`, (name) => {
console.log(`Hi ${name}!`)
readline.close()
})
Здесь мы спрашиваем у пользователя его имя, а после ввода текста и нажатия на клавишу Enter
на клавиатуре, выводим приветствие.
Метод question()
выводит то, что передано ему в качестве первого параметра (то есть — вопрос, задаваемый пользователю) и ожидает завершения ввода. После нажатия на Enter
он вызывает коллбэк, переданный ему во втором параметре и обрабатывает то, что было введено. В этом же коллбэке мы закрываем интерфейс readline
.
Модуль readline
поддерживает и другие методы, подробности о них вы можете узнать в документации, ссылка на которую приведена выше.
Если вам, с использованием этого механизма, надо запросить у пользователя пароль, то лучше не выводить его, в ходе ввода, на экран, а показывать вместо введённых символов символ звёздочки — *
.
Для того чтобы это сделать, можно воспользоваться пакетом readline-sync, устройство которого похоже на то, как устроен модуль readline
, и который поддерживает подобные возможности сразу после установки.
Есть и ещё один пакет, предоставляющий более полное и абстрактное решение подобной проблемы. Это пакет inquirer. Установить его можно так:
npm install inquirer
С его использованием вышеприведённый пример можно переписать следующим образом:
const inquirer = require('inquirer')
var questions = [{
type: 'input',
name: 'name',
message: "What's your name?",
}]
inquirer.prompt(questions).then(answers => {
console.log(`Hi ${answers['name']}!`)
})
Пакет inquirer обладает обширными возможностями. Например, он может помочь задать пользователю вопрос с несколькими вариантами ответа или сформировать в консоли интерфейс с радиокнопками.
Программисту стоит знать о наличии альтернативных возможностей по выполнению неких действий в Node.js. В нашем случае это стандартный модуль readline
, пакеты readline-sync и inquirer. Выбор конкретного решения зависит от целей проекта, от наличия времени на реализацию тех или иных возможностей и от сложности пользовательского интерфейса, который планируется сформировать средствами командной строки.
Система модулей Node.js, использование команды exports
Поговорим о том, как использовать API module.exports
для того, чтобы открывать доступ к возможностям модулей другим файлам приложения. В Node.js имеется встроенная система модулей, каждый файл при этом считается самостоятельным модулем. Общедоступный функционал модуля, с помощью команды require
, могут использовать другие модули:
const library = require('./library')
Здесь показан импорт модуля library.js
, файл которого расположен в той же папке, в которой находится файл, импортирующий его.
Модуль, прежде чем будет смысл его импортировать, должен что-то экспортировать, сделать общедоступным. Ко всему, что явным образом не экспортируется модулем, нет доступа извне. Собственно говоря, API module.exports
позволяет организовать экспорт того, что будет доступно внешним по отношению к модулю механизмам.
Экспорт можно организовать двумя способами.
Первый заключается в записи объекта в module.exports
, который является стандартным объектом, предоставляемым системой модулей. Это приводит к экспорту только соответствующего объекта:
const car = {
brand: 'Ford',
model: 'Fiesta'
}
module.exports = car
//..в другом файле
const car = require('./car')
Второй способ заключается в том, что экспортируемый объект записывают в свойство объекта exports
. Такой подход позволяет экспортировать из модуля несколько объектов, и, в том числе — функций:
const car = {
brand: 'Ford',
model: 'Fiesta'
}
exports.car = car
То же самое можно переписать и короче:
exports.car = {
brand: 'Ford',
model: 'Fiesta'
}
В другом файле воспользоваться тем, что экспортировал модуль, можно так:
const items = require('./items')
items.car
Или так:
const car = require('./items').car
В чём разница между записью объекта в module.exports
и заданием свойств объекта exports
?
В первом экспортируется объект, который записан в module.exports
. Во втором случае экспортируются свойства этого объекта.
Итоги
Сегодня мы поговорили о хостингах для Node.js-приложений, о REPL, о работе с командной строкой, о системе модулей Node.js. В следующий раз мы начнём обстоятельный разговор об npm, и, в частности, рассмотрим особенности файлов package.json
и package-lock.json
.
Уважаемые читатели! Какие хостинги вы используете для своих Node.js-приложений?
Автор: ru_vds