Время для чашки чая, начать работу с Node.js

Node.js
Время для чашки чая, начать работу с Node.js

Node.js настолько популярен, что почти все фронтенд-инженеры хотят его изучить, и почти все бэкэнд-инженеры хотят его изучить. Когда дело доходит до Node.js, мы сразу же думаем о таких характеристиках, как «асинхронный», «управляемый событиями», «неблокирующий» и «отличная производительность», но действительно ли вы понимаете значение этих слов? Этот учебник поможет вам быстро приступить к работе с Node.js, заложив прочную основу для последующего изучения интерфейса или продвинутого уровня Node.js.

Этот учебник принадлежитПуть обучения бэкенд-инженера Node.jsЧасть, добро пожаловать в звездную волну, поощряйте нас продолжать создавать лучшие учебные пособия и продолжать обновлять ~

Начало

Что такое узел?

Проще говоря, Node (или Node.js, оба эквивалентны) — это тип JavaScript.Рабочая среда. До этого мы знаем, что JavaScript выполняется в браузере для добавления различных динамических эффектов к веб-страницам, поэтому можно сказать, чтоБраузер также является средой выполнения для JavaScript.. Так в чем же разница между этими двумя операционными средами? Пожалуйста, посмотрите на изображение ниже:

Обе среды выполнения содержат ECMAScript, то есть JavaScript, лишенный всех сред выполнения.языковой стандартсам. Скорость разработки ECMAScript сейчас очень поразительна, и почти каждый год можно разрабатывать новую версию.

намекать

Связь между ECMAScript и JavaScript заключается в том, что первый является спецификацией второго, а второй — реализацией первого. В повседневных ситуациях эти два слова взаимозаменяемы. Дополнительные сведения см. в книге Руана Ифэна.История языка JavaScript.

С другой стороны, JavaScript на стороне браузера также включает в себя:

  • Объектная модель браузера (сокращенно BOM), котораяwindowобъект
  • Объектная модель документа (сокращенно DOM), котораяdocumentобъект

Node.js, с другой стороны, включает в себя движок V8. V8 — это движок JavaScript в браузере Chrome. После нескольких лет разработки и оптимизации его производительность и безопасность достигли значительных высот. А Node.js продвигает движок V8 на шаг вперед, превращая его в платформу, на которой можно запускать JavaScript в любой операционной системе.

Предварительные знания

Прежде чем официально приступить к этому руководству, мы надеемся, что у вас есть следующие приготовления:

  • Разбираться в основах языка JavaScript, если у вас есть опыт браузерной JS разработки, будет лучше
  • Установите и настройте Node.js в соответствии с вашим редактором или IDE.
  • Понимание относительных и абсолютных путей

цель обучения

Этот урок научит вас:

  • Взаимосвязь и разница между браузерным JavaScript и Node.js
  • Узнайте, какие глобальные объекты есть в Node.js
  • Узнайте, как Node.js импортирует и экспортирует модули и как работает модульный механизм.
  • Узнайте, как разрабатывать простые приложения командной строки с помощью Node.js.
  • Научитесь использовать возможности сообщества npm для решения проблем, возникающих в процессе разработки, и избегайте «изобретения велосипеда»
  • Понимать основные концепции и использование скриптов npm.
  • Предварительное понимание механизма событий Node.js

Запустить код узла

Как правило, существует два способа запуска кода Node: 1) ввести и запустить его в интерактивном режиме в REPL; 2) записать код в файл JS и выполнить его с помощью Node.

намекать

Полное название REPL — Read Eval Print Loop (цикл чтения-выполнения-вывода), что обычно можно понимать какинтерактивный переводчик, вы можете ввести любое выражение или оператор, и он немедленно выполнится и вернет результат. Если вы использовали REPL Python, он покажется вам знакомым.

Быстрый опыт работы с REPL

Если у вас уже установлен Node, выполните следующую команду, чтобы вывести версию Node.js:

$ node -v
v12.10.0

Затем мы также можем ввести Node REPL (непосредственно введитеnode), затем введите любое допустимое выражение или инструкцию JavaScript:

$ node
Welcome to Node.js v12.10.0.
Type ".help" for more information.
> 1 + 2
3
> var x = 10;
undefined
> x + 20
30
> console.log('Hello World');
Hello World
undefined

Некоторые строки начинаются с>, представляющий подсказку ввода, поэтому>Ниже приведены команды, которые мы хотим ввести, а остальные строки — это возвращаемое значение выражения или стандартный вывод (Standard Output, stdout). Эффект от бега следующий:

Пишите скрипты Node.js

REPL часто используются для экспериментов с некоторым кодом. При создании конкретного приложения больше нужно создавать файлы Node. Начнем с создания простейшего файла сценария Node.js с именем timer.js со следующим кодом:

console.log('Hello World!');

Затем выполните этот файл с помощью интерпретатора Node:

$ node timer.js
Hello World!

Это выглядит довольно банально, но эта строка кода — работа команды Node.js, стоящей за ней. Давайте сравним, в чем разница между выполнением этой строки кода в браузере и в среде Node:

  • запустить в браузереconsole.logВызывается спецификация, которая фактически выполняетсяwindow.console.log('Hello World!')
  • Node сначала создает новый процесс в операционной системе, где он находится, а затем выводит указанную строку в стандартный вывод, который фактически выполняетсяprocess.stdout.write('Hello World!\n')

Короче говоря, Node предоставляет нам среду выполнения кода JavaScript, которая не зависит от браузера и может напрямую взаимодействовать с операционной системой!

Предварительное исследование глобальных объектов узла

Если у вас есть опыт написания JavaScript, вы должны быть знакомы с глобальными объектами. В браузере имеемdocumentа такжеwindowи другие глобальные объекты, а Node содержит только ECMAScript и V8, а не BOM и DOM, поэтому его нет в Node.documentа такжеwindow; вместо этого глобальный объект, специфичный для узла,process. В этом разделе мы впервые рассмотрим глобальный объект Node.

Классификация глобальных объектов JavaScript

Перед этим давайте взглянем на сравнение глобальных объектов каждой среды выполнения JavaScript, как показано на следующем рисунке:

Вы видите, что глобальные объекты JavaScript можно разделить на четыре категории:

  1. Специфичный для браузера, например.window,alertтак далее;
  2. Специфичный для узла, например.process,Buffer,__dirname,__filenameтак далее;
  3. Общий для браузеров и Node, ноРеализация отличается,Напримерconsole(упоминается в первом разделе),setTimeout,setIntervalЖдать;
  4. Браузеры и Node совместно используют и принадлежатОпределение языка ECMAScriptчастьDate,String,PromiseЖдать;

Глобальное разрешение объекта для конкретного узла

process

processМожно сказать, что глобальный объект — это душа Node.js.Это объект, который управляет текущим состоянием процесса Node.js и обеспечивает простой интерфейс с операционной системой.

Сначала давайте изучимprocessважные свойства предметов. Откройте Node REPL и давайте посмотримprocessНекоторые свойства объектов:

  • pid: идентификатор процесса
  • env: переменные системной среды
  • argv: Входные параметры при выполнении этого скрипта из командной строки
  • platform: платформа текущей операционной системы

намекать

Вы можете попробовать эти объекты в Node REPL. Введите REPL, как указано выше (ваш вывод, скорее всего, будет отличаться от моего):

$ node
Welcome to Node.js v12.10.0.
Type ".help" for more information.
> process.pid
3
> process.platform
'darwin'

Buffer

BufferГлобальный объект позволяет JavaScript легко обрабатывать потоки двоичных данных, а в сочетании с потоковым интерфейсом Node (Stream) он может обеспечить эффективную обработку двоичных файлов. Этот учебник не будет охватыватьBuffer.

__filenameа также__dirname

Представляет путь к файлу и путь к каталогу запущенного в данный момент сценария Node соответственно.

предупреждать

__filenameа также__dirnameМожет использоваться только в файлах сценариев Node, не определенных в REPL.

Использование глобального объекта Node

Далее мы будем использовать глобальный объект Node в только что написанном файле скрипта, охватывающем три вышеуказанные категории:

  • Эксклюзивно для узла:process
  • Общие глобальные объекты с разными реализациями:consoleа такжеsetTimeout
  • Глобальные объекты, определенные языком ECMAScript:Date

намекать

setTimeoutИспользуется для выполнения определенной логики через определенное время, первый параметр — это функция (функция обратного вызова), которая должна быть выполнена по истечении времени, а второй параметр — время ожидания. Например:

setTimeout(someFunction, 1000);

будет в1000выполнить через миллисекундыsomeFunctionфункция.

код показывает, как показано ниже:

setTimeout(() => {
  console.log('Hello World!');
}, 3000);

console.log('当前进程 ID', process.pid);
console.log('当前脚本路径', __filename);

const time = new Date();
console.log('当前时间', time.toLocaleString());

Запустив приведенный выше скрипт, вывод на моей машине выглядит следующим образом (Hello World! будет выводиться после трехсекундной задержки):

$ node timer.js
当前进程 ID 7310
当前脚本路径 /Users/mRc/Tutorials/nodejs-quickstart/timer.js
当前时间 12/4/2019, 9:49:28 AM
Hello World!

Вы также можете получить представление о магии асинхронности Node.js из приведенного выше кода:setTimeoutВ течение 3 секунд ожидания программа небез блокировки, нопродолжать вниз, это асинхронный неблокирующий Node.js!

намекать

В реальной среде приложения часто выполняется много операций ввода-вывода (таких как сетевые запросы, запросы к базе данных и т. д.), которые занимают много времени, и Node.js может продолжать обрабатывать новые запросы во время ожидания, что значительно улучшает производительность. пропускная способность системы.

В следующем уроке у нас будет подробное руководство по асинхронному программированию в Node.js, так что следите за обновлениями!

Понимание механизма модуля Node

Еще одним важным изменением Node.js по сравнению с предыдущим браузерным JavaScript является введение механизма модулей. Этот раздел очень длинный, но это самый важный шаг для начала работы с Node.js, давай 💪!

Дорога JavaScript к модульности

Определено Эриком Рэймондом в книге «Искусство программирования UNIX».модульность(Модульность) правила:

Разработчики должны создавать программы из простых частей, соединенных четко определенными интерфейсами, чтобы проблемы были локализованы, а части программы можно было заменить в будущих версиях для поддержки новых функций. Это правило предназначено для экономии времени при отладке сложного, многословного и нечитаемого кода.

Идея «разделяй и властвуй» очень распространена в компьютерном мире, но до стандарта ES2015 (Неважно, если вы не понимаете, я расскажу об этом позже), само определение языка JavaScript не имеет модульного механизма, и нет единого стандарта интерфейса для создания сложных приложений. Люди обычно используют ряд<script>тег для импорта соответствующего модуля (зависимости):

<head>
  <script src="fileA.js"></script>
  <script src="fileB.js"></script>
</head>

Есть много проблем с этим способом организации кода JS, в том числе:

  • Несколько импортированных файлов JS действуют непосредственно на глобальное пространство имен, которое легко создать.конфликт имен
  • Импортированные файлы JS не могут получить доступ друг к другу, например, содержимое в файле A.js не может быть доступно в файле B.js, что очень неудобно.
  • импортированный<script>не может быть легко удален или изменен

Люди постепенно осознали проблемы, вызванные отсутствием механизма модульности JavaScript, поэтому были предложены две спецификации модульности:

  1. Спецификация AMD (определение асинхронного модуля), который чаще используется в браузерах, а самые классические реализации включаютRequireJS;
  2. Спецификация CommonJS, стремится предоставить единый интерфейс API для экосистемы JavaScript, и Node.js реализует этот стандарт модуля.

намекать

Стандарт ECMAScript 2015 (также известный как ES6) вводит новый модульный механизм (называемый модулями ES, полное название — модули ECMAScript) для языка JavaScript и предоставляетimportа такжеexportКлючевые слова, если вы заинтересованы, вы можете обратиться кэта статья. Но пока поддержка Node.js для ES-модулей все еще находится в стадии разработки.экспериментальная стадия, поэтому эта статья не объясняет и не пропагандирует его использование.

Что такое Node-модули

Прежде чем формально анализировать механизм модуля Node, нам нужно четко определить, что такое модуль Node. Вообще говоря, модули Node можно разделить на две категории:

  • основной модуль: встроенные модули, предоставляемые Node, которые компилируются в Node при установке Node.двоичный исполняемый файл
  • файловый модуль: Модули, написанные пользователями, которые могут быть написаны самими или установлены через npm (описано позже).

Среди них файловый модуль может бытьотдельный файл.js,.nodeили.jsonконец), илисодержание. Когда этот модуль является каталогом,Имя модуля - это имя каталога, возможны два случая:

  1. В директории есть файл package.json, в нем запись этого модуля Nodemainфайл, на который указывает поле;
  2. В каталоге есть файл index с расширением.js,.nodeили.json, этот файл является файлом входа модуля.

Неважно, если вы не сможете переварить все это сразу, вы можете сначала прочитать следующий контент, а если вы забудете определение модуля, вы сможете вернуться и просмотреть его.

Анализ механизма узловых модулей

Зная конкретное определение модуля Node, давайте посмотрим, как Node реализует модульный механизм. В частности, Node представляет три новых глобальных объекта (которые по-прежнему являются эксклюзивными для Node): 1)require;2)exportsи 3)module. Ниже мы объясним один за другим.

require

requireИспользуется для импорта других модулей Node, его параметр принимает строку, представляющую имя или путь модуля, обычно называемыйидентификатор модуля. В частности, существуют следующие три формы:

  • Напишите имя модуля напрямую, обычно это основной модуль или сторонний файловый модуль, напримерos,expressЖдать
  • Относительный путь к модулю, указывающий на другие модули Node в проекте, например../utils
  • Абсолютный путь к модулю (Не рекомендуется!),Например/home/xxx/MyProject/utils

намекать

При импорте модулей по пути обычно опускают.jsсуффикс.

Пример кода выглядит следующим образом:

// 导入内置库或第三方模块
const os = require('os');
const express = require('express');

// 通过相对路径导入其他模块
const utils = require('./utils');

// 通过绝对路径导入其他模块
const utils = require('/home/xxx/MyProject/utils');

Вам может быть интересно, импортируя модули Node по имени (например,express), где вы нашли этот модуль? На самом деле каждый модуль имеет список поиска путиmodule.paths, объяснил позжеmoduleобъект будет чистым.

exports

Мы научились использоватьrequireИмпортируйте содержимое из других модулей, так как же написать модуль Node и экспортировать его содержимое? Ответ заключается в использованииexportsобъект.

Например, мы пишем Node-модуль myModule.js:

// myModule.js
function add(a, b) {
  return a + b;
}

// 导出函数 add
exports.add = add;

поставивaddфункция добавлена ​​вexportsВ объекте внешние модули могут использовать эту функцию с помощью следующего кода. Создайте main.js рядом с myModule.js со следующим кодом:

// main.js
const myModule = require('./myModule');

// 调用 myModule.js 中的 add 函数
myModule.add(1, 2);

намекать

Если вы знакомы с ECMAScript 6присваивание деструктуризации, то его можно получить более изящным способомaddфункция:

const { add } = require('./myModule');

module

пройти черезrequireа такжеexports, мы уже знаем, как импортировать и экспортировать содержимое в модули Node, но механизм модуля Node может показаться вам немного загадочным. Далее мы приоткроем завесу этой тайны и узнаем о главном герое, стоящем за...moduleОбъект модуля.

Мы можем добавить эту строку кода в конец файла myModule.js прямо сейчас:

console.log('module myModule:', module);

В конец main.js добавляем:

console.log('module main:', module);

После запуска он напечатает что-то вроде этого (myModule слева, модуль справа):

можно увидетьmoduleОбъект имеет следующие поля:

  • id: уникальный идентификатор модуля, или если это основная запущенная программа (например, main.js).., если это импортированный модуль (например, myModule.js), эквивалентно этому имени файла (т. е. следующееfilenameполе)
  • pathа такжеfilename: Путь и имя файла модуля, ни о чем не говорит
  • exports: что на самом деле экспортирует модульпредыдущийexportsобъект указывает наmodule.exportsцитаты. Например, для myModule.js мы только что экспортировалиaddфункция, таким образом появляясь в этомexportsполе, а main.js ничего не экспортирует, поэтомуexportsполе пусто
  • parentа такжеchildren: используется для записи отношения импорта между модулями, например, в main.jsrequiremyModule.js, тогда основным является myModuleparent, myModule является основнымchildren
  • loaded: загружен ли модуль, из рисунка выше видно, что толькоchildrenМодули, перечисленные в, будут загружены
  • paths: это узелСписок путей для поиска файловых модулей, Node будет искать указанный модуль Node от первого до последнего пути, импортировать его, если он будет найден, и сообщить об ошибке, если он не будет найден.

намекать

Если вы посмотрите внимательно, путь поиска файлового модуля Node (module.paths) на самом деле так: сначала найдите node_modules в текущем каталоге, если нет, то ищите node_modules в каталоге верхнего уровня, если не нашли, продолжайте искать до node_modules в корневом каталоге.

Глубокое пониманиеmodule.exports

Ранее мы упоминали,exportsОбъект по существуmodule.exportsцитаты. То есть следующие две строки кода эквивалентны:

// 导出 add 函数
exports.add = add;

// 和上面一行代码是一样的
module.exports.add = add;

На самом деле есть второй способ экспорта, напрямуюaddназначение функцийmodule.exportsОбъект:

module.exports = add;

В чем разница между этим и первым методом экспорта? Первый способ, вexportsДобавьте свойство к объекту с именемadd, значение этого свойства равноaddфункция; второй способ, непосредственно сделатьexportsобъектaddфункция. Это может немного сбивать с толку, но убедитесь, что вы понимаете большую разницу между ними!

существуетrequire, разница между ними очевидна:

// 第一种导出方式,需要访问 add 属性获取到 add 函数
const myModule = require('myModule');
myModule.add(1, 2);

// 第二种导出方式,可以直接使用 add 函数
const add = require('myModule');
add(1, 2);

предупреждать

написать напрямуюexports = add;Не могу экспортироватьaddфункцию, потому чтоexportsпо существу указывает наmoduleизexportsСсылка на свойство, непосредственно наexportsМеняется только назначениеexports,правильноmodule.exportsНет эффекта. Если вам сложно понять, то мы используемappleа такжеpriceаналогияmoduleа такжеexports:

apple = { price: 1 };   // 想象 apple 就是 module
price = apple.price;    // 想象 price 就是 exports
apple.price = 3;        // 改变了 apple.price
price = 3;              // 只改变了 price,没有改变 apple.price

мы можем только пройтиapple.price = 1настраиватьpriceсвойства, а непосредственноpriceНазначение не меняетсяapple.price.

Рефакторинг скрипта таймера

После стольких разговоров о механике модулей Node пришло время вернуться к нашему предыдущему сценарию таймера, timer.js. Сначала мы создаем новый модуль Node info.js для вывода информации о системе, код выглядит следующим образом:

const os = require('os');

function printProgramInfo() {
  console.log('当前用户', os.userInfo().username);
  console.log('当前进程 ID', process.pid);
  console.log('当前脚本路径', __filename);
}

module.exports = printProgramInfo;

Здесь мы импортируем встроенные модули Nodeos, и пройтиos.userInfo()Запрашивается системное имя пользователя, а затем черезmodule.exportsэкспортируетсяprintProgramInfoфункция.

Затем создайте второй модуль Node datetime.js, чтобы вернуть текущее время, код выглядит следующим образом:

function getCurrentTime() {
  const time = new Date();
  return time.toLocaleString();
}

exports.getCurrentTime = getCurrentTime;

В приведенном выше модуле мы решили передатьexportsэкспортgetCurrentTimeфункция.

Наконец, мы передаем timer.jsrequireИмпортируйте два модуля прямо сейчас и вызовите функции в модулях соответственно.printProgramInfoа такжеgetCurrentTime, код показан ниже:

const printProgramInfo = require('./info');
const datetime = require('./datetime');

setTimeout(() => {
  console.log('Hello World!');
}, 3000);

printProgramInfo();
console.log('当前时间', datetime.getCurrentTime());

Запустите timer.js еще раз, и результат должен быть точно таким же, как и раньше.

Читая это, я хочу поздравить вас с прохождением самой сложной части начала работы с Node.js! Если вы действительно поняли механизм модуля Node, то я считаю, что следующее обучение будет очень легким.

Разработка командной строки: принять входные параметры

Как платформа, которая может запускать код JavaScript непосредственно в операционной системе, Node.js открывает бесконечные возможности для разработчиков интерфейсов, включая ряд инструментов командной строки для реализации рабочих процессов автоматизации интерфейса, таких какGrunt,GulpИ знаменитыйWebpack.

На этом шаге мы превратим timer.js в приложение командной строки. В частности, мы хотим, чтобы timer.js мог указывать время ожидания с помощью аргументов командной строки (timeварианты) и окончательная выходная информация (messageвариант):

$ node timer.js --time 5 --message "Hello Tuture"

пройти черезprocess.argvчитать аргументы командной строки

Я говорил о глобальном объекте раньшеprocessупомянулargvсвойство для получения массива аргументов командной строки. Создайте файл args.js со следующим кодом:

console.log(process.argv);

Затем выполните следующие команды:

$ node args.js --time 5 --message "Hello Tuture"

Вывести массив:

[
  '/Users/mRc/.nvm/versions/node/v12.10.0/bin/node',
  '/Users/mRc/Tutorials/nodejs-quickstart/args.js',
  '--time',
  '5',
  '--message',
  'Hello Tuture'
]

можно увидеть,process.argv0-й элемент массиваnodeФактический путь , первый элемент — это путь args.js, за которым следуют все входные параметры.

Реализовать приложения командной строки

Согласно только что проведенному анализу, мы можем получить его очень просто и грубо.process.argv3-й и 5-й элементы соответственно можно получитьtimeа такжеmessageпараметр. Поэтому измените код timer.js следующим образом:

const printProgramInfo = require('./info');
const datetime = require('./datetime');

const waitTime = Number(process.argv[3]);
const message = process.argv[5];

setTimeout(() => {
  console.log(message);
}, waitTime * 1000);

printProgramInfo();
console.log('当前时间', datetime.getCurrentTime());

напоминать,setTimeoutЕдиница времени указана в миллисекундах, а единица указанного нами параметра времени — в секундах, поэтому умножьте на 1000.

Запустите timer.js со всеми только что упомянутыми параметрами:

$ node timer.js --time 5 --message "Hello Tuture"

Подождав 5 секунд, вы увидите текст Hello Tuture!

Но очевидно, что в текущей версии есть большая проблема: формат входных параметров фиксированный и очень негибкий, например обменtimeа такжеmessageПорядок ввода будет неверным, и невозможно проверить, ввел ли пользователь заданные параметры, правильный ли формат и т.д. Если вы хотите реализовать упомянутые выше функции самостоятельно, это потребует больших усилий, и может быть много багов. Есть ли лучшее решение?

npm: Сила доисторических времен дана вам

Начиная с этого раздела, вы больше не будете писать код в одиночку. Вас поддержат миллионы разработчиков JavaScript, все с npm. нпм включает в себя:

  • Инструмент командной строки npm (также устанавливается при установке узла)
  • централизованный репозиторий зависимостей npm (реестр), в котором хранятся пакеты npm, совместно используемые другими разработчиками JavaScript.
  • npm Веб-сайт, вы можете искать необходимые пакеты npm, управлять учетными записями npm и т. д.

Первый взгляд на npm

Давайте сначала откроем терминал (командную строку) и проверимnpmДоступна ли команда:

$ npm -v
6.10.3

Затем выполните следующую команду в текущем каталоге (то есть в папке, где находится отредактированный файл timer.js), чтобы инициализировать текущий проект как проект npm:

$ npm init

В это время npm задаст ряд вопросов, вы можете пройти весь путь, чтобы войти, или вы можете внимательно ответить, и, наконец, будет создан файл package.json. Файл package.json является ядром проекта npm и записывает всю ключевую информацию о проекте следующим образом:

{
  "name": "timer",
  "version": "1.0.0",
  "description": "A cool timer",
  "main": "timer.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/mRcfps/nodejs-quickstart.git"
  },
  "author": "mRcfps",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/mRcfps/nodejs-quickstart/issues"
  },
  "homepage": "https://github.com/mRcfps/nodejs-quickstart#readme"
}

Большинство полей имеют четкие значения, такие какnameназвание проекта,versionномер версии,descriptionописывать,authorАвтор и так далее. Но этоscriptsПоля могут сбивать вас с толку, мы подробно опишем их в следующем разделе.

Установить npm-пакет

Далее мы объясним наиболее распространенные команды npm —install. Да, не будет преувеличением сказать, что наиболее часто используемая команда npm программистом на JavaScript — этоnpm install.

Перед установкой необходимых нам пакетов npm нам нужно изучить, какие пакеты доступны для нас. Обычно мы можемофициальный сайт нпмИщем по ключевым словам (не забудьте использовать английский язык), например ищем командную строку:

Первый результат, который выходит, командир, очень подходит для наших нужд.Нажав на него, инструкция по установке иРабота с документацией. Мы также хотим, чтобы анимация загрузки улучшала взаимодействие с пользователем. Попробуйте выполнить поиск по ключевому слову загрузки:

Второй результат ora также соответствует нашим потребностям. Итак, давайте установим эти два пакета npm:

$ npm install commander ora

Немного подождав, вы увидите, что в package.json есть очень важнаяdependenciesПоле:

"dependencies": {
  "commander": "^4.0.1",
  "ora": "^4.0.3"
}

В этом поле записывается наш проектпрямая зависимость. а такжепрямая зависимостьпротивоположностькосвенная зависимость, например зависимости commandor и ora, нам обычно все равно. Все пакеты npm (прямые и косвенные зависимости) хранятся в каталоге проекта node_modules.

намекать

node_modules обычно имеет много файлов, поэтому он не будет добавлен в систему контроля версий Git.В проекте npm, который вы загружаете из Интернета, обычно будет только package.json.В этом случае просто запуститеnpm install(ничего не следует), вы можете скачать и установить все зависимости.

Весь код package.json выглядит так:

{
  "name": "timer",
  "version": "1.0.0",
  "description": "A cool timer",
  "main": "timer.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/mRcfps/nodejs-quickstart.git"
  },
  "author": "mRcfps",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/mRcfps/nodejs-quickstart/issues"
  },
  "homepage": "https://github.com/mRcfps/nodejs-quickstart#readme",
  "dependencies": {
    "commander": "^4.0.1",
    "ora": "^4.0.3"
  }
}

О номере версии

В разработке программного обеспечения номер версии является очень важным понятием, и между разными версиями программного обеспечения могут быть большие или малые различия. npm использует семантическое управление версиями (сокращенно семантическое управление версиями)semver), конкретные положения заключаются в следующем:

  • Формат версии: основной номер версии дополнительный номер версии номер редакции
  • Изменение основного номера версии означаетНесовместимые модификации API
  • Изменение дополнительного номера версии означает, чтоФункциональные дополнения для обратной совместимости
  • Изменение номера версии означает, чтоИсправлены ошибки обратной совместимости

намекать

обратная совместимостьПростое понимание состоит в том, чтоХарактеристики только увеличиваются.

Итак, в package.jsondependenciesполе версия может быть указана следующими способами:

  • точная версия:Например1.0.0, он установит только версию как1.0.0зависимость
  • Блокировка основных и дополнительных версий: можно записать как1.0,1.0.xили~1.0.0, тогда можно было бы установить, например.1.0.8зависимость
  • Заблокировать только основную версию: можно записать как1,1.xили^1.0.0(npm installформа по умолчанию), то можно установить, например.1.1.0зависимость
  • Последняя версия: можно записать как*илиx, затем установите последнюю версию напрямую (не рекомендуется)

Вы могли заметить, что npm также создает package-lock.json, который используется дляЗафиксируйте точные номера версий всех прямых и косвенных зависимостей., или предоставляет точное описание каталога node_modules, гарантируя, что все разработчики в этом проекте имеют точно такие же зависимости npm.

Стоя на плечах гигантов

Прочитав документацию по command и ora, мы можем начать его использовать.Измените код timer.js следующим образом:

const program = require('commander');
const ora = require('ora');
const printProgramInfo = require('./info');
const datetime = require('./datetime');

program
  .option('-t, --time <number>', '等待时间 (秒)', 3)
  .option('-m, --message <string>', '要输出的信息', 'Hello World')
  .parse(process.argv);

setTimeout(() => {
  spinner.stop();
  console.log(program.message);
}, program.time * 1000);

printProgramInfo();
console.log('当前时间', datetime.getCurrentTime());
const spinner = ora('正在加载中,请稍后 ...').start();

На этот раз мы снова запускаем timer.js:

$ node timer.js --message "洪荒之力!" --time 5

Включите его!

Попробуйте npm-скрипты

В последнем разделе этого руководства мы кратко представим сценарии npm, также известные как сценарии npm. Как упоминалось ранее, в package.json есть поле с именемscripts, это поле определяет все скрипты npm. Мы обнаружили, что с помощьюnpm initПо умолчанию файл package.json создается приtestсценарий:

"test": "echo \"Error: no test specified\" && exit 1"

Эта строка команд и будет выполняться тестовым скриптом, мы можем передатьnpm testКоманда выполняет скрипт:

$ npm test

> timer@1.0.0 test /Users/mRc/Tutorials/nodejs-quickstart
> echo "Error: no test specified" && exit 1

Error: no test specified
npm ERR! Test failed.  See above for more details.

После первоначального опыта работы со сценариями npm необходимо понимать, что сценарии npm делятся на две категории:

  • предопределенный сценарий:Напримерtest,start,install,publishподожди, пройди прямоnpm <scriptName>бежать, напр.npm test, можно просмотреть все предустановленные скриптыДокументация
  • пользовательский скрипт: В дополнение к другим скриптам, которые идут в комплекте с вышеперечисленными скриптами, вам нужно пройтиnpm run <scriptName>бежать, напр.npm run custom

Теперь давайте начнем добавлять в проект таймера два npm-скрипта, которыеstartа такжеlint. Первый предопределен для запуска нашего timer.js, второй — статическая проверка кода для проверки нашего кода во время разработки. Установить первымESLintнпм-пакет:

$ npm install eslint --save-dev
$ # 或者
$ npm install eslint -D

Обратите внимание, что мы добавили-Dили--save-devвариант, представляющийeslintЯвляетсязависимости развития, который не требуется при выпуске или развертывании фактического проекта. npm добавит все зависимости разработки вdevDependenciesв поле. а затем добавитьstartа такжеlintСкрипт, код такой:

{
  "name": "timer",
  "version": "1.0.0",
  "description": "A cool timer",
  "main": "timer.js",
  "scripts": {
    "lint": "eslint **/*.js",
    "start": "node timer.js -m '上手了' -t 3",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/mRcfps/nodejs-quickstart.git"
  },
  "author": "mRcfps",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/mRcfps/nodejs-quickstart/issues"
  },
  "homepage": "https://github.com/mRcfps/nodejs-quickstart#readme",
  "dependencies": {
    "commander": "^4.0.1",
    "ora": "^4.0.3"
  },
  "devDependencies": {
    "eslint": "^6.7.2"
  }
}

Для использования ESLint требуется файл конфигурации для создания файла .eslintrc.js (обратите внимание, что впереди есть точка), код выглядит следующим образом:

module.exports = {
    "env": {
        "es6": true,
        "node": true,
    },
    "extends": "eslint:recommended",
};

бегатьnpm start, вы можете видеть, что наш скрипт timer.js был успешно запущен, во время работыnpm run lint, не выдал никаких результатов (это означает, что статическая проверка прошла).

Скрипты npm выглядят пресно, но они могут обеспечить очень удобный рабочий процесс для разработки проекта. Например, для построения проекта ранее требовались очень сложные команды, но если вы реализуетеbuildnpm, затем, когда ваш коллега получит код, просто выполнитеnpm run buildВы можете начать строить, не заботясь о технических деталях. В последующем изучении Node.js или внешнего интерфейса мы будем использовать различные сценарии npm в реальных проектах, чтобы определить наш рабочий процесс, и все постепенно оценят его мощь.

Увидимся в следующий раз: слушайте событие выхода

В последнем разделе этого руководства мы дадим вам краткий обзор механики событий Node. Механизм событий Node относительно сложен, его хватит на половину книги, но это руководство надеется дать вам предварительное представление о событиях Node на очень простом примере.

намекать

Если у вас есть опыт написания обработки событий (например, щелчков мышью) в веб-разработке (или другом пользовательском интерфейсе), то способ обработки событий в Node кажется вам знакомым и интуитивно понятным.

Ранее мы кратко упомянули функции обратного вызова. Фактически функция обратного вызова и механизм событий вместе составляют асинхронный мир Node. В частности, события в Nodeeventsв основном модулеEventEmitterреализуется этим классом.EventEmitterВключая два наиболее важных метода:

  • on: Используется для прослушивания инцидента
  • emit: Используется для запуска новых событий

Взгляните на следующий фрагмент кода:

const EventEmitter = require('events').EventEmitter;
const emitter = new EventEmitter();

// 监听 connect 事件,注册回调函数
emitter.on('connect', function (username) {
  console.log(username + '已连接');
});

// 触发 connect 事件,并且加上一个参数(即上面的 username)
emitter.emit('connect', '一只图雀');

Запуск приведенного выше кода выведет следующее:

一只图雀已连接

Можно сказать, что многие объекты в Node наследуются отEventEmitter, в том числе знакомоеprocessглобальный объект. В предыдущем скрипте timer.js мы слушалиexitсобытие (то есть процесс Node завершается) и добавьте пользовательскую функцию обратного вызова для вывода сообщения «увидимся в следующий раз»:

const program = require('commander');
const ora = require('ora');
const printProgramInfo = require('./info');
const datetime = require('./datetime');

program
  .option('-t, --time <number>', '等待时间 (秒)', 3)
  .option('-m, --message <string>', '要输出的信息', 'Hello World')
  .parse(process.argv);

setTimeout(() => {
  spinner.stop();
  console.log(program.message);
}, program.time * 1000);

process.on('exit', () => {
  console.log('下次再见~');
});

printProgramInfo();
console.log('当前时间', datetime.getCurrentTime());
const spinner = ora('正在加载中,请稍后 ...').start();

После запуска строка «Увидимся в следующий раз~» будет напечатана после выхода из программы. Вы спросите, почему бы и нетsetTimeoutКак насчет добавления логики выхода из программы в функцию обратного вызова? Потому что в дополнение к обычному завершению работы (то есть ожиданию указанного времени) наша программа, вероятно, завершится по другим причинам (например, создание исключения или использованиеprocess.exitПринудительный выход), в это время отслеживаяexitсобытий, вы можете обеспечить выполнение во всех случаяхexitФункция обратного вызова события. Если вы все еще не понимаете, вы можете взглянуть на следующую схему:

намекать

processОбъекты также поддерживают другие часто используемые события, такие какSIGINT(срабатывает, когда пользователь нажимает Ctrl+C) и т. д., вы можете обратиться к этомуДокументация.

На этом мы завершаем это руководство по быстрому запуску Node.js. Надеюсь, оно станет краеугольным камнем вашего дальнейшего изучения Node.js или разработки интерфейса. Событие выхода сработало, увидимся в следующий раз~

Хотите узнать больше интересных практических технических руководств? ПриходитьСообщество ТукеМагазин вокруг.