Нужна ли папка node_modules в директории проекта?
— Правда, не совсем понял зачем команда 2 раза повторяется: npm install gulp ?
— И второй момент, в папке сайта появилась папка node_modules , аж на 9 мб. Так и должно быть? Если да, то как потом готовый сайт, копировать без этого каталога и файлов gulpfile.js, package.json. Или это всё потом удалить просто.
Поделитесь мастеровитые верстальщики )
- Вопрос задан более трёх лет назад
- 6540 просмотров
Комментировать
Решения вопроса 1

webdesign, webdevelopment. Мой сайт: fun-wiki.ru
Папка node-modules нужна для разработки (проще говоря это различные библиотеки которые ты можешь использовать в проекте). Gulp это task менеджер который посредством различных заданий в итоге соберет твой проект. Чаще всего во фронт энде используют так называемые препроцессоры (Pug, Sass, Less. ) и исходники хранятся в отдельной папке (например src или source), посредством Gulp эти файлы при помощи библиотек node_modules собираются в готовый проект (html, css, js) который и деплоится на хостинг.
А если честно, использовать Gulp не имея хотя бы базовых знаний и представлений о NodeJS еще рано.
Использование модулей Node.js с npm и package.json

Благодаря таким функциям, как оперативное выполнение ввода/вывода и его широко известному синтаксису JavaScript, Node.js быстро стал популярной рабочей средой для разработки веб-приложений на стороне сервера. Но по мере роста интереса создаются более крупные приложения, а управление сложностью базы кода и ее зависимостей становится сложнее. Node.js организует эти сложные процессы с помощью модулей, которые являются любым отдельным файлом JavaScript, содержащим функции или объекты, используемые другими программами или модулями. Группа из одного или нескольких модулей часто называется пакетом, а эти пакеты организованы менеджерами пакетов.
Менеджер пакетов Node.js (npm) — это стандартный и наиболее популярный менеджер пакетов в экосистеме Node.js; он преимущественно используется для установки и управления внешними модулями проекта Node.js. Он также часто используется для установки широкого спектра инструментов командной строки и запуска скриптов проекта. npm отслеживает модули, установленные в проекте с файлом package.json , находящимся в директории проекта и содержащим следующее:
- Все модули, необходимые для проекта, и их установленные версии
- Все метаданные для проекта — например, автор, лицензия и т. п.
- Скрипты, которые можно запустить для автоматизации задач в проекте
При создании более сложных проектов Node.js управление своими метаданными и зависимостями при помощи файла package.json позволит обеспечить более предсказуемые сборки, поскольку все внешние зависимости одинаковы. Файл будет автоматически отслеживать эту информацию. Хотя вы можете изменять файл напрямую для обновления метаданных вашего проекта, вам будет редко требоваться взаимодействовать с ним напрямую для управления модулями.
В этом обучающем руководстве вы будете управлять пакетами с помощью npm. Первый шаг будет состоять в создании и понимании файла package.json . Затем вы сможете использовать его для отслеживания всех модулей, которые вы установили в вашем проекте. В заключение вы укажете зависимости пакетов, обновите пакеты, удалите пакеты и проведете проверку на предмет уязвимостей безопасности.
Предварительные требования
Для данного обучающего руководства вам потребуется следующее:
- Node.js, установленный на вашем компьютере для разработки. В этом обучающем руководстве используется версия 10.17.0. Чтобы установить его в macOS или Ubuntu 18.04, следуйте указаниям руководства Установка Node.js и создание локальной среды разработки в macOS или раздела Установка с помощью PPA руководства Установка Node.js в Ubuntu 18.04. При установке Node.js также выполняется установка npm, в этом обучающем руководстве используется версия 6.11.3.
Шаг 1 — Создание файла package.json
Начнем это обучающее руководство с проекта в качестве примера — гипотетический модуль локатора Node.js, получающий IP-адрес пользователя и возвращающий страну происхождения. Вы не будете кодировать модуль в этом обучающем руководстве. Однако пакеты, которыми вы управляете, будут актуальны, если вы их разрабатывали.
Вначале вы создадите файл package.json , который будет содержать полезные метаданные проекта и поможет вам управлять зависимыми модулями Node.js проекта. Как следует из названия суффикса, это файл JSON (обозначение объектов JavaScript). JSON — стандартный формат, используемый для обмена, основанный на объектах JavaScript и состоящий из данных, сохраненных как пары «ключ-значение». Если хотите узнать больше о JSON, ознакомьтесь с нашей статьей Введение в JSON.
Поскольку файл package.json содержит многочисленные свойства, может быть затруднительно создавать его вручную без копирования и вставки шаблона из другого места. Чтобы упростить процесс, npm предоставляет команду init . Это интерактивная команда, которая задает ряд вопросов и создает файл package.json на основе ваших ответов.
Использование команды init
Вначале настройте проект, чтобы можно было попрактиковаться в управлении модулями. В своей оболочке создайте новую папку под названием locator :
Затем перейдите в новую папку:
Теперь инициализируйте интерактивную командную строку, введя следующее:
Примечание. Если ваш код будет использовать Git для контроля версий, сначала создайте репозиторий Git, а затем запустите npm init . Команда автоматически понимает, что она находится в папке, управляемой Git. Если задан удаленный Git, он автоматически заполняет следующие поля для вашего файла package.json : repository , bugs и homepage . Если вы инициализировали репозиторий после создания файла package.json , вам нужно будет добавить эту информацию самостоятельно. Дополнительную информацию по контролю версий Git можно найти в нашей серии Введение в Git: установка, использование и ответвления.
Результат будет выглядеть следующим образом:
OutputThis utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install ` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (locator)
Сначала вам будет предложено ввести имя нового проекта. По умолчанию команда предполагает, что это имя папки, в которой вы находитесь. Значения по умолчанию для каждого свойства показаны в скобках () . Поскольку в настоящем руководстве будет использоваться значение имени по умолчанию, нажмите ENTER , чтобы принять его.
Следующее значение, которое нужно ввести — версия . Как и в случае с именем , это поле требуется, если ваш проект будет размещаться вместе с другими проектами в репозитории пакетов npm.
Примечание. Пакеты Node.js должны соответствовать руководству по указанию версий Semantic (semver). Поэтому первое число будет номером версии MAJOR , который изменяется только при изменении API. Второе число будет номером версии MINOR , который изменяется при добавлении функций. Последнее число будет номером версии PATCH , который изменяется при исправлении ошибок.
Нажмите ENTER , чтобы принять версию по умолчанию.
Следующее поле описание — полезная строка, чтобы объяснить, что делает ваш модуль Node.js. Наш проект гипотетического локатора получит IP-адрес пользователя и выдаст страну происхождения. Подходящее описание : находит страну происхождения по входящему запросу , поэтому введите нечто подобное и нажмите ENTER . Описание очень полезно, когда люди ищут ваш модуль.
Следующая строка запросит у вас точку входа . Если кто-то осуществляет установку и нуждается в вашем модуле, то указанное вами в точке входа будет первой частью вашей загружаемой программы. Значение должно быть относительным местом расположения файла JavaScript и будет добавлено в свойство main файла package.json . Нажмите ENTER , чтобы сохранить значение по умолчанию.
Примечание. В большинстве модулей файл index.js является основной точкой входа. Это значение по умолчанию для свойства main файла package.json , которое представляет собой точку входа для модулей npm. Если не будет package.json , Node.js попробует загружать index.js по умолчанию.
Далее вам нужно будет ввести команду test , исполняемый скрипт или команду, чтобы запустить тесты проекта. Во многих популярных модулях Node.js тесты написаны и осуществляются с помощью Mocha, Jest, Jasmine или других тестовых структур. Поскольку тестирование выходит за рамки этой статьи, оставьте эту опцию пустой и нажмите ENTER , чтобы продолжить.
Затем команда init запросит репозиторий GitHub проекта. Вы не будете использовать его в данном примере, поэтому оставьте и его пустым.
После строки репозитория команда запросит ключевые слова . Это свойство представляет собой массив строк с полезными терминами, которые можно использовать для поиска репозитория. Лучше всего иметь небольшой набор слов, которые наиболее актуальны для вашего проекта, чтобы обеспечить более целенаправленный поиск. Введите эти ключевые слова как строку с запятыми, разделяющими каждое значение. Для данного проекта, служащего примером, введите ip,geo,country в командной строке. В готовом package.json будет три элемента в массиве для ключевых слов .
Следующее поле в командной строке — автор . Это полезно для пользователей вашего модуля, которые хотят связаться с вами. Например, если кто-то обнаружит средство эксплуатации уязвимостей в вашем модуле, они смогут использовать это поле, чтобы сообщить о проблеме, чтобы вы смогли ее исправить. Поле автор представляет собой строку в следующем формате: » Name \ < Email \>( Website )» . Например, «Sammy \ (https://your_domain)» — подходящее значение поля «автор». Электронная почта и веб-сайт являются опциональными данными — подходящее значение поля «автор» может состоять только из имени. Добавьте ваши контактные данные в качестве автора и подтвердите нажатием ENTER .
В заключение вам будет предложено ввести лицензию . Это определяет юридические разрешения и ограничения, которые будут у пользователей при использовании вашего модуля. Многие модули Node.js имеют открытый исходный код, поэтому npm задает значение ISC по умолчанию.
К этому моменту вы должны будете рассмотреть ваши опции лицензирования и определить, что подходит лучше всего для вашего проекта. Дополнительную информацию по различным видам открытых лицензий можно найти в этом списке лицензий от организации Open Source Initiative. Если вы не захотите предоставлять лицензию для частного репозитория, вы можете ввести UNLICENSED в командной строке. Для данного примера используйте лицензию ISC по умолчанию и нажмите ENTER , чтобы завершить этот процесс.
Теперь команда init отобразит файл package.json , который она будет создавать. Он будет выглядеть примерно так:
OutputAbout to write to /home/sammy/locator/package.json: < "name": "locator", "version": "1.0.0", "description": "Finds the country of origin of the incoming request", "main": "index.js", "scripts": < "test": "echo \"Error: no test specified\" && exit 1" >, "keywords": [ "ip", "geo", "country" ], "author": "Sammy sammy@your_domain> (https://your_domain)", "license": "ISC" > Is this OK? (yes)
Когда информация совпадет с тем, что вы видите здесь, нажмите ENTER , чтобы завершить этот процесс и создать файл package.json . С этим файлом вы можете хранить записи о модулях, которые вы установили для вашего проекта.
Теперь, когда у вас есть файл package.json , вы можете протестировать установку модулей на следующем шаге.
Шаг 2 — Установка модулей
Обычно в разработке программного обеспечения используются внешние библиотеки для выполнения вспомогательных задач в проектах. Это позволяет разработчику сосредотачивать внимание на бизнес-логике и создавать приложения более быстро и эффективно.
Например, если наш модуль локатора , используемый в качестве примера, должен сделать внешний запрос API для получения географических данных, мы можем использовать библиотеку HTTP, чтобы упростить эту задачу. Поскольку наша основная цель — предоставление пользователю соответствующих географических данных, мы можем установить пакет, упрощающий для нас запросы HTTP вместо повторной записи этого кода, т. к. это задача, выходящая за рамки нашего проекта.
Рассмотрим это на примере. В вашем приложении локатора вы будете использовать библиотеку axios, которая поможет вам выполнять запросы HTTP. Установите ее, введя следующее в оболочке:
Вы начинаете эту команду с npm install — это установит данный пакет (для краткости можно использовать npm i ). Затем перечисляете пакеты, которые хотите установить, разделяя их пробелом. В этом случае это axios . В заключение вы заканчиваете команду опциональным параметром —save , который указывает, что axios будет сохранен как зависимость проекта.
После установки библиотеки вы увидите примерно следующее:
Output. + axios@0.19.0 added 5 packages from 8 contributors and audited 5 packages in 0.764s found 0 vulnerabilities
Теперь откройте файл package.json в текстовом редакторе по вашему выбору. В этом обучающем руководстве мы будем использовать nano :
Вы увидите новое свойство, как подчеркнуто ниже:
locator/package.json
< "name": "locator", "version": "1.0.0", "description": "Finds the country of origin of the incoming request", "main": "index.js", "scripts": < "test": "echo \"Error: no test specified\" && exit 1" >, "keywords": [ "ip", "geo", "country" ], "author": "Sammy sammy@your_domain (https://your_domain)", "license": "ISC", "dependencies": "axios": "^0.19.0" > >
Параметр —save указывает npm обновлять package.json с модулем и версиями, которые были только что установлены. Это замечательно, поскольку другие разработчики, работающие над вашим проектами, смогут легко видеть, какие внешние зависимости требуются.
Примечание. Возможно, вы заметили символ ^ перед номером версии для зависимости axios . Вспомните, что семантическое определение версий состоит из трех цифр: MAJOR, MINOR и PATCH. Символ ^ означает, что любая более высокая версия MINOR или PATCH удовлетворит это ограничение версии. Если видите ~ в начале номера версии, это означает, что только более высокие номера версий PATCH удовлетворяют это ограничение.
После завершения просмотра package.json выйдите из файла.
Зависимости разработки
Пакеты, используемые для разработки проекта, но не для его создания или запуска, называются зависимостями разработки. Они не требуются для вашего модуля или приложения в производственной среде, но могут оказаться полезными при написании кода.
Например, разработчики часто используют инструменты статического анализа кода, чтобы обеспечить соответствие кода передовым практикам и единообразие стиля. Хотя это полезно для разработки, это только увеличивает размер дистрибутива без предоставления ощутимых выгод при развертывании в производственной среде.
Установите инструмент статического анализа кода в качестве зависимости разработки для вашего проекта. Попробуйте это в оболочке:
В этой команде вы использовали флаг —save-dev . Это сохранит eslint как зависимость, которая необходима только для разработки. Обратите внимание, что вы добавили @6.0.0 к своему имени зависимости. После обновления модулей на них ставится тег версии. @ указывает npm искать конкретный тег модуля, который вы устанавливаете. Без указанного тега npm устанавливает последнюю версию с тегами. Откройте package.json еще раз:
В результате вы получите следующий вывод:
locator/package.json
< "name": "locator", "version": "1.0.0", "description": "Finds the country of origin of the incoming request", "main": "index.js", "scripts": < "test": "echo \"Error: no test specified\" && exit 1" >, "keywords": [ "ip", "geo", "country" ], "author": "Sammy sammy@your_domain (https://your_domain)", "license": "ISC", "dependencies": < "axios": "^0.19.0" >, "devDependencies": "eslint": "^6.0.0" > >
eslint сохранился как devDependencies , вместе с номером версии, который вы указали ранее. Выйдите из package.json .
Автоматически сгенерированные файлы: node_modules и package-lock.json
Когда вы в первый раз устанавливаете пакет в проекте Node.js, npm автоматически создает папку node_modules для хранения модулей, необходимых для вашего проекта, и файл package-lock.json , который вы проверили ранее.
Подтвердите, что они находятся в вашей рабочей директории. В вашей оболочке введите ls и нажмите ENTER . Результат будет выглядеть следующим образом:
Outputnode_modules package.json package-lock.json
Папка node_modules содержит все установленные зависимости для вашего проекта. В большинстве случаев вы не должны назначать эту папку вашему репозиторию с контролем версии. По мере того как вы будете устанавливать больше зависимостей, размер этой папки будет быстро расти. Кроме того, файл package-lock.json хранит записи точных версий, установленных более сжато, поэтому включение node_modules не требуется.
Хотя файл package.json перечисляет зависимости, которые указывают нам на соответствующие версии, которые должны быть установлены для проекта, файл package-lock.json отслеживает все изменения в package.json или node_modules и указывает на конкретную версию установленного пакета. Обычно вы управляете этим в вашем репозитории с контролем версий, а не в node_modules , т. к. это более прозрачное представление всех ваших зависимостей.
Установка из package.json
С помощью ваших файлов package.json и package-lock.json вы можете быстро задать те же самые зависимости проекта, прежде чем начать разработку нового проекта. Чтобы продемонстрировать это, перейдите на один уровень выше в дереве директорий и создайте новую папку с именем cloned_locator на том же уровне директории, что и locator :
Перейдите в новую директорию:
Теперь скопируйте файлы package.json и package-lock.json из locator в cloned_locator :
Чтобы установить требуемые модули для этого проекта, введите следующее:
npm проверит файл package-lock.json , чтобы установить модули. Если файл lock недоступен, он будет читать из файла package.json , чтобы определить установки. Обычно быстрее устанавливать из package-lock.json , поскольку файл lock содержит точные версии модулей и их зависимости, что означает, что npm может не тратить время на нахождение подходящей версии для установки.
При развертывании в производственную среду вы можете пропустить зависимости разработки. Вспомните, что зависимости разработки хранятся в разделе devDependencies файла package.json и не влияют на управление вашим приложением. При установке модулей в рамках процесса непрерывной интеграции и разработки, чтобы развернуть приложение, пропустите зависимости разработки, введя следующее:
Флаг —production игнорирует раздел devDependencies во время установки. Пока что перейдите к вашей сборке разработки.
Прежде чем перейти к следующему разделу, вернитесь в папку locator :
Глобальная установка
Пока что вы устанавливали модули npm для проекта locator . npm также позволяет устанавливать пакеты на глобальном уровне. Это означает, что пакет доступен вашему пользователю в более широкой системе, как и любая другая команда оболочки. Эта способность полезна для многих модулей Node.js, которые являются инструментами командной строки.
Например, вы хотите завести блог о проекте locator , над которым вы сейчас работаете. Для этого вы можете использовать библиотеку, например Hexo, для создания и управления вашим блогом на статичном веб-сайте. Установите командную строку Hexo на глобальном уровне следующим образом:
Чтобы установить пакет на глобальном уровне, вы добавляете флаг -g к команде.
Примечание. Если вы получите ошибку разрешений, пытаясь выполнить установку этого пакета на глобальном уровне, ваша система может потребовать права суперпользователя для запуска команды. Попробуйте еще раз команду sudo npm i hexo-cli -g .
Проверьте, что пакет успешно установлен, введя следующее:
Вы увидите примерно следующий результат:
Outputhexo-cli: 2.0.0 os: Linux 4.15.0-64-generic linux x64 http_parser: 2.7.1 node: 10.14.0 v8: 7.6.303.29-node.16 uv: 1.31.0 zlib: 1.2.11 ares: 1.15.0 modules: 72 nghttp2: 1.39.2 openssl: 1.1.1c brotli: 1.0.7 napi: 4 llhttp: 1.1.4 icu: 64.2 unicode: 12.1 cldr: 35.1 tz: 2019a
На данный момент вы узнали, как устанавливать модули с помощью npm. Вы можете установить пакеты к проекту локально —либо как зависимость в производственной среде, либо как зависимость разработки. Также вы можете устанавливать пакеты на базе уже существующих файлов package.json или package-lock.json , что позволяет разрабатывать с теми же зависимостями, что и у ваших коллег. В заключение вы можете использовать флаг -g для установки пакетов на глобальном уровне, чтобы получить доступ к ним, независимо от того, находитесь ли вы в проекте Node.js или нет.
Теперь, когда вы можете устанавливать модули, в следующем разделе вы будете практиковать методы управления своими зависимостями.
Шаг 3 — Управление модулями
Полный менеджер пакетов способен на гораздо большее, чем установка модулей. В npm имеется 20 команд, связанных с управлением зависимостями. На этом шаге вы:
- Укажете модули, которые вы установили.
- Обновите модули до последней версии.
- Удалите модули, которые вам больше не нужны.
- Проведете проверку безопасности на ваших модулях, чтобы найти и исправить ошибки безопасности.
Хотя эти примеры будут сделаны в вашей папке locator , все эти команды могут запускаться на глобальном уровне посредством добавления флага -g в конце, как и в случае установки на глобальном уровне.
Указание модулей
Если вы хотите знать, какие модули установлены в проекте, проще использовать команду list или ls вместо чтения package.json напрямую. Для этого введите следующее:
Результат должен выглядеть следующим образом:
Output├─┬ axios@0.19.0 │ ├─┬ follow-redirects@1.5.10 │ │ └─┬ debug@3.1.0 │ │ └── ms@2.0.0 │ └── is-buffer@2.0.3 └─┬ eslint@6.0.0 ├─┬ @babel/code-frame@7.5.5 │ └─┬ @babel/highlight@7.5.0 │ ├── chalk@2.4.2 deduped │ ├── esutils@2.0.3 deduped │ └── js-tokens@4.0.0 ├─┬ ajv@6.10.2 │ ├── fast-deep-equal@2.0.1 │ ├── fast-json-stable-stringify@2.0.0 │ ├── json-schema-traverse@0.4.1 │ └─┬ uri-js@4.2.2 .
По умолчанию ls отображает все дерево зависимостей — модули, от которых зависит ваш проект, и модули, от которых зависят ваши зависимости. Это может быть довольно неудобно, если вы хотите получить общий обзор того, что установлено.
Чтобы вывести только модули, которые вы установили, без их зависимостей, введите следующее в оболочке:
Результат будет выглядеть следующим образом:
Output├── axios@0.19.0 └── eslint@6.0.0
Опция —depth позволяет указать, какой уровень дерева зависимостей вы хотите увидеть. Когда значение 0 , вы увидите только зависимости самого высокого уровня.
Обновление модулей
Это хорошая практика, позволяющая поддерживать ваши модули npm обновленными. Это повышает вашу вероятность получения последних исправлений безопасности для модуля. Используйте команду outdated , чтобы проверить, можно ли обновить какие-либо модули:
Вывод будет выглядеть следующим образом:
OutputPackage Current Wanted Latest Location eslint 6.0.0 6.7.1 6.7.1 locator
Сначала эта команда указывает пакет ( Package ), который установлен, и текущую ( Current ) версию. В столбце Wanted показано, какая версия удовлетворяет вашему требованию версии в package.json . В столбце Latest показана самая последняя опубликованная версия модуля.
В столбце Location показано, где в дереве зависимостей находится пакет. Команда outdated имеет флаг —depth , как ls . По умолчанию depth равняется 0.
Похоже, вы можете обновить eslint до последней версии. Используйте команду update или up следующим образом:
Вывод команды будет содержать установленную версию:
Outputnpm WARN locator@1.0.0 No repository field. + eslint@6.7.1 added 7 packages from 3 contributors, removed 5 packages, updated 19 packages, moved 1 package and audited 184 packages in 5.818s found 0 vulnerabilities
Если хотите обновить все модули одновременно, введите следующее:
Удаление модулей
Команда npm uninstall может удалять модули из ваших проектов. Это означает, что модуль больше не будет установлен в папке node_modules и его нельзя будет увидеть в ваших файлах package.json и package-lock.json .
Удаление зависимостей из проекта — обычное мероприятие в жизненном цикле разработки программного обеспечения. Зависимость может не решить проблему, как заявлено, или может не предоставить удовлетворительный опыт разработки. В этих случаях может быть лучше удалить зависимость и создать собственный модуль.
Представьте, что axios не предоставляет опыт разработки, который вы хотели бы иметь для выполнения запросов HTTP. Удалите axios с помощью команды uninstall или un , введя следующее:
Вывод будет выглядеть следующим образом:
Outputnpm WARN locator@1.0.0 No repository field. removed 5 packages and audited 176 packages in 1.488s found 0 vulnerabilities
Здесь не указано явно, что axios был удален. Чтобы убедиться, что он был удален, еще раз укажите зависимости:
Теперь мы видим только то, что установлен eslint :
Output└── eslint@6.7.1
Это показывает, что вы успешно удалили пакет axios .
Проверка модулей
npm предоставляет команду audit , чтобы выявить потенциальные риски безопасности в ваших зависимостях. Чтобы увидеть проверку в действии, установите устаревшую версию модуля request, выполнив следующее:
После установки устаревшей версии request вы должны увидеть примерно следующий результат:
Output+ request@2.60.0 added 54 packages from 49 contributors and audited 243 packages in 7.26s found 6 moderate severity vulnerabilities run `npm audit fix` to fix them, or `npm audit` for details
npm указывает, что у вас есть уязвимости в ваших зависимостях. Для получения более подробных сведений проверьте весь ваш проект:
Команда audit показывает таблицы вывода, указывающие на недостатки безопасности:
Output === npm audit security report === # Run npm install request@2.88.0 to resolve 1 vulnerability ┌───────────────┬──────────────────────────────────────────────────────────────┐ │ Moderate │ Memory Exposure │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Package │ tunnel-agent │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Dependency of │ request │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Path │ request > tunnel-agent │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ More info │ https://npmjs.com/advisories/598 │ └───────────────┴──────────────────────────────────────────────────────────────┘ # Run npm update request --depth 1 to resolve 1 vulnerability ┌───────────────┬──────────────────────────────────────────────────────────────┐ │ Moderate │ Remote Memory Exposure │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Package │ request │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Dependency of │ request │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Path │ request │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ More info │ https://npmjs.com/advisories/309 │ └───────────────┴──────────────────────────────────────────────────────────────┘ .
Вы видите путь к уязвимости, и иногда npm предоставляет способы ее устранения. Вы можете выполнить команду update, как предложено, или выполнить подкоманду fix команды audit . В своей оболочке введите:
Вы увидите примерно следующий результат:
Output+ request@2.88.0 added 19 packages from 24 contributors, removed 32 packages and updated 12 packages in 6.223s fixed 2 of 6 vulnerabilities in 243 scanned packages 4 vulnerabilities required manual review and could not be updated
npm смог безопасно обновить два пакета, тем самым устранив две уязвимости. Но у вас все еще есть четыре уязвимости в ваших зависимостях. Команда audit fix не всегда устраняет каждую проблему. Хотя версия модуля может иметь уязвимость безопасности, если вы обновите ее до версии с другим API, то это может нарушить код выше в дереве зависимостей.
Вы можете использовать параметр —force , чтобы обеспечить удаление уязвимости:
Как упоминалось ранее, это не рекомендуется, если вы не уверены, что это не нарушит функциональность.
Заключение
В этом обучающем руководстве вы прошли различные упражнения, чтобы продемонстрировать организацию модулей Node.js в пакетах и управление этими пакетами при помощи npm. В проекте Node.js вы использовали пакеты npm как зависимости, создавая и обслуживая файл package.json — запись метаданных вашего проекта, включая модули, которые вы установили. Вы также использовали инструмент командной строки npm для установки, обновления и удаления модулей, помимо указания дерева зависимостей для ваших проектов, а также проверки и обновления модулей, которые устарели.
В будущем использование существующего кода с помощью модулей ускорит время разработки, так как вам не придется дублировать функциональность. Вы также сможете создавать собственные модули npm, и они, в свою очередь, будут управляться другими модулями с помощью команд npm. В качестве дальнейших шагов экспериментируйте с тем, что вы узнали в этом обучающем руководстве, устанавливая и тестируя различные существующие пакеты. Посмотрите, что предоставляет экосистема для упрощения процесса решения проблем. Например, вы можете попробовать TypeScript — расширенную версию JavaScript, или превратить ваш веб-сайт в мобильные приложения с помощью Cordova. Если хотите узнать больше о Node.js, ознакомьтесь с другими нашими обучающими руководствами по Node.js.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Модули CommonJS¶
АПИ является удовлетворительным. Совместимость с NPM имеет высший приоритет и не будет нарушена кроме случаев явной необходимости.
Модули CommonJS — это оригинальный способ упаковки кода JavaScript для Node.js. Node.js также поддерживает стандарт ECMAScript modules, используемый браузерами и другими средами выполнения JavaScript.
В Node.js каждый файл рассматривается как отдельный модуль. Например, рассмотрим файл с именем foo.js :
1 2 3 4
const circle = require('./circle.js'); console.log( `Площадь круга радиуса 4 равна $circle.area(4)>` );
В первой строке foo.js загружает модуль circle.js , который находится в том же каталоге, что и foo.js .
Вот содержимое circle.js :
1 2 3 4 5
const PI > = Math; exports.area = (r) => PI * r ** 2; exports.circumference = (r) => 2 * PI * r;
Модуль circle.js экспортировал функции area() и circumference() . Функции и объекты добавляются в корень модуля путем указания дополнительных свойств специального объекта exports .
Переменные, локальные для модуля, будут приватными, поскольку модуль обернут в функцию Node.js (см. module wrapper). В этом примере переменная PI является приватной для circle.js .
Свойству module.exports может быть присвоено новое значение (например, функция или объект).
Ниже, bar.js использует модуль square , который экспортирует класс Square:
1 2 3
const Square = require('./square.js'); const mySquare = new Square(2); console.log(`Площадь mySquare равна $mySquare.area()>`);
Модуль square определен в square.js :
1 2 3 4 5 6 7 8 9 10
// Назначение на exports не изменит модуль, необходимо использовать module.exports module.exports = class Square constructor(width) this.width = width; > площадь() return this.width ** 2; > >;
Система модулей CommonJS реализована в модуле module core module.
Включение¶
Node.js имеет две системы модулей: CommonJS модули и ECMAScript модули.
По умолчанию Node.js будет считать модулями CommonJS следующее:
- Файлы с расширением .cjs ;
- Файлы с расширением .js , если ближайший родительский файл package.json содержит поле верхнего уровня «type» со значением «commonjs» .
- Файлы с расширением .js , если ближайший родительский package.json файл не содержит поля верхнего уровня «type» . Авторы пакетов должны включать поле тип , даже в пакетах, где все источники являются CommonJS. Явное указание типа пакета облегчит инструментам сборки и загрузчикам определение того, как следует интерпретировать файлы в пакете.
- Файлы с расширением не .mjs , .cjs , .json , .node или .js (если ближайший родительский файл package.json содержит поле верхнего уровня «type» со значением «module» , эти файлы будут распознаны как модули CommonJS только в том случае, если они включаются через require() , но не при использовании в качестве точки входа программы в командной строке).
Более подробную информацию смотрите в Определение системы модулей.
Вызов require() всегда использует загрузчик модулей CommonJS. Вызов import() всегда использует загрузчик модулей ECMAScript.
Доступ к главному модулю¶
Когда файл запускается непосредственно из Node.js, require.main устанавливается в его модуль . Это означает, что можно определить, был ли файл запущен напрямую, проверив require.main === module .
Для файла foo.js это будет true , если он запущен через node foo.js , но false , если запущен через require(‘./foo’) .
Если точка входа не является модулем CommonJS, require.main будет undefined , и главный модуль будет недоступен.
Советы по работе с менеджером пакетов¶
Семантика функции Node.js require() была разработана достаточно общей, чтобы поддерживать разумные структуры каталогов. Программы менеджеров пакетов, такие как dpkg , rpm и npm , надеюсь, найдут возможность собирать собственные пакеты из модулей Node.js без модификации.
Ниже мы приводим предлагаемую структуру каталогов, которая может работать:
Допустим, мы хотим, чтобы папка по адресу /usr/lib/node// содержала содержимое определенной версии пакета.
Пакеты могут зависеть друг от друга. Для того чтобы установить пакет foo , может потребоваться установить определенную версию пакета bar . Пакет bar может сам иметь зависимости, и в некоторых случаях они могут даже сталкиваться или образовывать циклические зависимости.
Поскольку Node.js просматривает realpath всех загружаемых модулей (то есть, разрешает симлинки), а затем ищет их зависимости в папках node_modules , эта ситуация может быть разрешена с помощью следующей архитектуры:
- /usr/lib/node/foo/1.2.3/ : Содержимое пакета foo , версия 1.2.3.
- /usr/lib/node/bar/4.3.2/ : Содержимое пакета bar , от которого зависит foo .
- /usr/lib/node/foo/1.2.3/node_modules/bar : Символическая ссылка на /usr/lib/node/bar/4.3.2/ .
- /usr/lib/node/bar/4.3.2/node_modules/* : Символические ссылки на пакеты, от которых зависит bar .
Таким образом, даже если встретится цикл или возникнут конфликты зависимостей, каждый модуль сможет получить версию своей зависимости, которую он может использовать.
Когда код в пакете foo выполняет require(‘bar’) , он получит версию, которая находится по симлинку в /usr/lib/node/foo/1.2.3/node_modules/bar . Затем, когда код в пакете bar вызовет require(‘quux’) , он получит версию, которая находится по симлинку в /usr/lib/node/bar/4.3.2/node_modules/quux .
Более того, чтобы сделать процесс поиска модулей еще более оптимальным, вместо того, чтобы помещать пакеты непосредственно в /usr/lib/node , мы можем поместить их в /usr/lib/node_modules// . Тогда Node.js не будет утруждать себя поиском отсутствующих зависимостей в /usr/node_modules или /node_modules .
Чтобы сделать модули доступными для Node.js REPL, может быть полезно также добавить папку /usr/lib/node_modules в переменную окружения $NODE_PATH . Поскольку поиск модулей с помощью папок node_modules является относительным и основан на реальном пути к файлам, выполняющим вызовы require() , сами пакеты могут находиться где угодно.
Расширение .mjs ¶
Из-за синхронной природы require() невозможно использовать его для загрузки файлов модулей ECMAScript. Попытка сделать это приведет к ошибке ERR_REQUIRE_ESM . Вместо этого используйте import() .
Расширение .mjs зарезервировано для Модулей ECMAScript, которые не могут быть загружены через require() . Смотрите раздел Определение системы модулей для получения дополнительной информации о том, какие файлы разбираются как модули ECMAScript.
Все вместе¶
Чтобы получить точное имя файла, который будет загружен при вызове require() , используйте функцию require.resolve() .
Если собрать воедино все вышесказанное, вот высокоуровневый алгоритм в псевдокоде того, что делает require() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
require(X) from module at path Y 1. If X is a core module, a. return the core module b. STOP 2. If X begins with '/' a. set Y to be the file system root 3. If X begins with './' or '/' or '../' a. LOAD_AS_FILE(Y + X) b. LOAD_AS_DIRECTORY(Y + X) c. THROW "not found" 4. If X begins with '#' a. LOAD_PACKAGE_IMPORTS(X, dirname(Y)) 5. LOAD_PACKAGE_SELF(X, dirname(Y)) 6. LOAD_NODE_MODULES(X, dirname(Y)) 7. THROW "not found" LOAD_AS_FILE(X) 1. If X is a file, load X as its file extension format. STOP 2. If X.js is a file, load X.js as JavaScript text. STOP 3. If X.json is a file, parse X.json to a JavaScript Object. STOP 4. If X.node is a file, load X.node as binary addon. STOP LOAD_INDEX(X) 1. If X/index.js is a file, load X/index.js as JavaScript text. STOP 2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP 3. If X/index.node is a file, load X/index.node as binary addon. STOP LOAD_AS_DIRECTORY(X) 1. If X/package.json is a file, a. Parse X/package.json, and look for "main" field. b. If "main" is a falsy value, GOTO 2. c. let M = X + (json main field) d. LOAD_AS_FILE(M) e. LOAD_INDEX(M) f. LOAD_INDEX(X) DEPRECATED g. THROW "not found" 2. LOAD_INDEX(X) LOAD_NODE_MODULES(X, START) 1. let DIRS = NODE_MODULES_PATHS(START) 2. for each DIR in DIRS: a. LOAD_PACKAGE_EXPORTS(X, DIR) b. LOAD_AS_FILE(DIR/X) c. LOAD_AS_DIRECTORY(DIR/X) NODE_MODULES_PATHS(START) 1. let PARTS = path split(START) 2. let I = count of PARTS - 1 3. let DIRS = [] 4. while I >= 0, a. if PARTS[I] = "node_modules" CONTINUE b. DIR = path join(PARTS[0 .. I] + "node_modules") c. DIRS = DIR + DIRS d. let I = I - 1 5. return DIRS + GLOBAL_FOLDERS LOAD_PACKAGE_IMPORTS(X, DIR) 1. Find the closest package scope SCOPE to DIR. 2. If no scope was found, return. 3. If the SCOPE/package.json "imports" is null or undefined, return. 4. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE), ["node", "require"]) defined in the ESM resolver. 5. RESOLVE_ESM_MATCH(MATCH). LOAD_PACKAGE_EXPORTS(X, DIR) 1. Try to interpret X as a combination of NAME and SUBPATH where the name may have a @scope/ prefix and the subpath begins with a slash (`/`). 2. If X does not match this pattern or DIR/NAME/package.json is not a file, return. 3. Parse DIR/NAME/package.json, and look for "exports" field. 4. If "exports" is null or undefined, return. 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH, `package.json` "exports", ["node", "require"]) defined in the ESM resolver. 6. RESOLVE_ESM_MATCH(MATCH) LOAD_PACKAGE_SELF(X, DIR) 1. Find the closest package scope SCOPE to DIR. 2. If no scope was found, return. 3. If the SCOPE/package.json "exports" is null or undefined, return. 4. If the SCOPE/package.json "name" is not the first segment of X, return. 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE), "." + X.slice("name".length), `package.json` "exports", ["node", "require"]) defined in the ESM resolver. 6. RESOLVE_ESM_MATCH(MATCH) RESOLVE_ESM_MATCH(MATCH) 1. let RESOLVED_PATH = fileURLToPath(MATCH) 2. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension format. STOP 3. THROW "not found"
Кэширование¶
Модули кэшируются после первой загрузки. Это означает (помимо прочего), что при каждом вызове require(‘foo’) будет возвращен точно такой же объект, если он будет разрешен в тот же файл.
При условии, что require.cache не изменен, многократные вызовы require(‘foo’) не приведут к многократному выполнению кода модуля. Это важная особенность. С ее помощью можно возвращать «частично выполненные» объекты, что позволяет загружать переходные зависимости, даже если они могут вызвать циклы.
Чтобы модуль выполнял код несколько раз, экспортируйте функцию и вызовите ее.
Предостережения по кэшированию модулей¶
Модули кэшируются на основе их разрешенного имени файла. Поскольку модули могут разрешаться в разные имена файлов в зависимости от расположения вызывающего модуля (загрузка из папок node_modules ), это не гарантия того, что require(‘foo’) всегда будет возвращать точно такой же объект, если он разрешается в разные файлы.
Кроме того, в файловых системах или операционных системах, не чувствительных к регистру, разные разрешенные имена файлов могут указывать на один и тот же файл, но кэш все равно будет рассматривать их как разные модули и будет перезагружать файл несколько раз. Например, require(‘./foo’) и require(‘./FOO’) возвращают два разных объекта, независимо от того, являются ли ./foo и ./FOO одним и тем же файлом.
Основные модули¶
Node.js имеет несколько модулей, скомпилированных в двоичный файл. Эти модули более подробно описаны в других разделах этой документации.
Основные модули определены в исходном коде Node.js и находятся в папке lib/ .
Основные модули могут быть определены с помощью префикса node: , в этом случае они обходят кэш require . Например, require(‘node:http’) всегда будет возвращать встроенный модуль HTTP, даже если в require.cache есть запись с таким именем.
Некоторые модули ядра всегда загружаются предпочтительно, если их идентификатор передан в require() . Например, require(‘http’) всегда будет возвращать встроенный модуль HTTP, даже если существует файл с таким именем. Список основных модулей, которые могут быть загружены без использования префикса node: , раскрывается как module.builtinModules .
Циклы¶
Когда есть циклические вызовы require() , модуль может не закончить выполнение, когда он будет возвращен.
Рассмотрим такую ситуацию:
1 2 3 4 5 6
console.log('a starting'); exports.done = false; const b = require('./b.js'); console.log('в a, b.done = %j', b.done); exports.done = true; console.log('a done');
1 2 3 4 5 6
console.log('b started'); exports.done = false; const a = require('./a.js'); console.log('в b, a.done = %j', a.done); exports.done = true; console.log('b done');
1 2 3 4 5 6 7 8
console.log('main starting'); const a = require('./a.js'); const b = require('./b.js'); console.log( 'in main, a.done = %j, b.done = %j', a.done, b.done );
Когда main.js загружает a.js , то a.js в свою очередь загружает b.js . В этот момент b.js пытается загрузить a.js . Чтобы предотвратить бесконечный цикл, незавершенная копия объекта экспорта a.js возвращается в модуль b.js . Затем b.js завершает загрузку, и его объект exports передается модулю a.js .
К тому времени, когда main.js загрузит оба модуля, они оба завершат работу. Вывод этой программы будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9
$ node main.js main starting a starting b starting in b, a.done = false b done in a, b.done = true a done in main, a.done = true, b.done = true
Для того чтобы циклические зависимости модулей корректно работали в приложении, необходимо тщательное планирование.
Модули файлов¶
Если точное имя файла не найдено, то Node.js попытается загрузить требуемое имя файла с добавленными расширениями: .js , .json , и, наконец, .node . При загрузке файла, имеющего другое расширение (например, .cjs ), его полное имя должно быть передано в require() , включая расширение файла (например, require(‘./file.cjs’) ).
Файлы .json анализируются как текстовые файлы JSON, файлы .node интерпретируются как скомпилированные модули аддонов, загруженные с помощью process.dlopen() . Файлы, использующие любое другое расширение (или вообще без расширения), разбираются как текстовые файлы JavaScript. Обратитесь к разделу Определение системы модулей, чтобы понять, какая цель разбора будет использоваться.
Требуемый модуль с префиксом ‘/’ — это абсолютный путь к файлу. Например, require(‘/home/marco/foo.js’) загрузит файл по адресу /home/marco/foo.js .
Требуемый модуль с префиксом ./’ является относительным к файлу, вызывающему require() . То есть, circle.js должен находиться в том же каталоге, что и foo.js , чтобы require(‘./circle’) нашел его.
Без ведущего ‘/’ , ‘/’ или ‘/. /’ для указания файла, модуль должен быть либо основным модулем, либо загружаться из папки node_modules .
Если указанный путь не существует, require() выдаст ошибку MODULE_NOT_FOUND .
Папки как модули¶
Стабильность: 3 – Закрыто
Принимаются только фиксы, связанные с безопасностью, производительностью или баг-фиксы. Пожалуйста, не предлагайте изменений АПИ в разделе с таким индикатором, они будут отклонены.
Существует три способа передачи папки в require() в качестве аргумента.
Первый — создать в корне папки файл package.json , который определяет главный модуль. Пример файла package.json может выглядеть следующим образом:
"name": "some-library", "main": "./lib/some-library.js" >
Если бы это находилось в папке ./some-library , то require(‘./some-library’) попытался бы загрузить ./some-library/lib/some-library.js .
Если в каталоге нет файла package.json , или если запись «main» отсутствует или не может быть разрешена, то Node.js попытается загрузить файл index.js или index.node из этого каталога. Например, если в предыдущем примере не было файла package.json , то require(‘./some-library’) попытается загрузить:
- ./some-library/index.js
- ./some-library/index.node .
Если эти попытки не увенчаются успехом, то Node.js сообщит об отсутствии всего модуля с ошибкой по умолчанию:
Ошибка: Cannot find module 'some-library'
Во всех трех вышеприведенных случаях вызов import(‘./some-library’) приведет к ошибке ERR_UNSUPPORTED_DIR_IMPORT . Использование пакетов subpath exports или subpath imports может обеспечить те же преимущества организации содержимого, что и папки, и модули, и работать как для require , так и для import .
Загрузка из папок node_modules ¶
Если идентификатор модуля, переданный в require() , не является модулем core и не начинается с ‘/’ , ‘../’ или ‘./’ , то Node.js начинает с каталога текущего модуля, добавляет /node_modules и пытается загрузить модуль из этого места. Node.js не будет добавлять node_modules к пути, который уже заканчивается на node_modules .
Если модуль не найден там, то он переходит в родительский каталог, и так далее, пока не будет достигнут корень файловой системы.
Например, если файл по адресу ‘/home/ry/projects/foo.js’ вызывает require(‘bar.js’) , то Node.js будет искать в следующих местах, в таком порядке:
- /home/ry/projects/node_modules/bar.js
- /home/ry/node_modules/bar.js
- /home/node_modules/bar.js
- /node_modules/bar.js
Это позволяет программам локализовать свои зависимости, чтобы они не конфликтовали.
Можно потребовать определенные файлы или подмодули, распространяемые вместе с модулем, включив суффикс пути после имени модуля. Например, require(‘example-module/path/to/file’) разрешит path/to/file относительно того места, где находится example-module . Путь с суффиксом следует той же семантике разрешения модуля.
Загрузка из глобальных папок¶
Если переменная окружения NODE_PATH установлена в список абсолютных путей, разделенных двоеточием, то Node.js будет искать модули по этим путям, если они не найдены в других местах.
В Windows NODE_PATH разграничивается точками с запятой ( ; ) вместо двоеточий.
NODE_PATH был изначально создан для поддержки загрузки модулей из различных путей до того, как был определен текущий алгоритм разрешения модулей.
NODE_PATH все еще поддерживается, но теперь, когда экосистема Node.js пришла к соглашению о расположении зависимых модулей, необходимость в нем отпала. Иногда развертывания, которые полагаются на NODE_PATH , показывают неожиданное поведение, когда люди не знают, что NODE_PATH должен быть установлен. Иногда зависимости модуля меняются, в результате чего при поиске по NODE_PATH загружается другая версия (или даже другой модуль).
Кроме того, Node.js будет искать в следующем списке GLOBAL_FOLDERS:
- 1: $HOME/.node_modules .
- 2: $HOME/.node_libraries
- 3: $PREFIX/lib/node .
Где $HOME — это домашний каталог пользователя, а $PREFIX — это настроенный в Node.js node_prefix .
Это в основном для исторических целей.
Настоятельно рекомендуется размещать зависимости в локальной папке node_modules . Они будут загружаться быстрее и надежнее.
Обертка модуля¶
Прежде чем код модуля будет выполнен, Node.js обернет его функцией-оберткой, которая выглядит следующим образом:
1 2 3 4 5 6 7 8 9
(function ( exports, require, module, __filename, __dirname ) // Код модуля на самом деле находится здесь >);
Делая это, Node.js достигает нескольких вещей:
- Переменные верхнего уровня (определенные с помощью var , const или let ) привязываются к модулю, а не к глобальному объекту.
- Это помогает обеспечить некоторые глобальные на вид переменные, которые на самом деле специфичны для модуля, например:
- Объекты module и exports , которые исполнитель может использовать для экспорта значений из модуля.
- Удобные переменные __filename и __dirname , содержащие абсолютное имя файла и путь к каталогу модуля.
Область применения модуля¶
__dirname ¶
Имя каталога текущего модуля. Это то же самое, что path.dirname() из __filename .
Пример: запуск node example.js из /Users/mjr .
1 2 3 4
console.log(__dirname); // Prints: /Users/mjr console.log(path.dirname(__filename)); // Печатает: /Users/mjr__filename ¶
Имя файла текущего модуля. Это абсолютный путь к файлу текущего модуля с разрешенными симлинками.
Для основной программы это имя не обязательно совпадает с именем файла, используемым в командной строке.
Имя каталога текущего модуля смотрите в __dirname .
Запуск node example.js из /Users/mjr .
1 2 3 4
console.log(__filename); // Печатает: /Users/mjr/example.js console.log(__dirname); // Печатает: /Users/mjrДаны два модуля: a и b , где b является зависимостью от a , а структура каталогов имеет вид:
- /Users/mjr/app/a.js
- /Users/mjr/app/node_modules/b/b.js .
Ссылки на __filename в пределах b.js вернут /Users/mjr/app/node_modules/b/b.js , а ссылки на __filename в пределах a.js вернут /Users/mjr/app/a.js .
exports ¶
Ссылка на module.exports , который короче по типу. Смотрите раздел о ярлыке exports для подробностей о том, когда использовать exports и когда использовать module.exports .
module ¶
Ссылка на текущий модуль, см. раздел об объекте module . В частности, module.exports используется для определения того, что модуль экспортирует и делает доступным через require() .
require(id) ¶
Используется для импорта модулей, JSON и локальных файлов. Модули могут быть импортированы из node_modules . Локальные модули и JSON файлы могут быть импортированы с использованием относительного пути (например, ./ , ./foo , ./bar/baz , ../foo ), который будет разрешен относительно каталога, названного __dirname (если определен) или текущего рабочего каталога. Относительные пути в стиле POSIX разрешаются независимо от ОС, то есть приведенные выше примеры будут работать на Windows так же, как и на Unix-системах.
1 2 3 4 5 6 7 8 9
// Импортирование локального модуля с путем относительно `__dirname` или текущей // рабочему каталогу. (В Windows это будет выглядеть как .\path\myLocalModule). const myLocalModule = require('./path/myLocalModule'); // Импортируем файл JSON: const jsonData = require('./path/filename.json'); // Импортирование модуля из node_modules или встроенного модуля Node.js: const crypto = require('node:crypto');require.cache ¶
Модули кэшируются в этом объекте, когда они требуются. Если удалить значение ключа из этого объекта, то при следующем require модуль будет перезагружен. Это не относится к родным аддонам, для которых перезагрузка приведет к ошибке.
Также возможно добавление или замена записей. Этот кэш проверяется перед встроенными модулями, и если в кэш добавлено имя, совпадающее со встроенным модулем, то только node: -префиксные вызовы require будут получать встроенный модуль. Используйте с care!
1 2 3 4 5 6 7 8
const assert = require('node:assert'); const realFs = require('node:fs'); const fakeFs = <>; require.cache.fs = exports: fakeFs >; assert.strictEqual(require('fs'), fakeFs); assert.strictEqual(require('node:fs'), realFs);require.extensions ¶
Стабильность: 0 – устарело или набрало много негативных отзывов
Эта фича является проблемной и ее планируют изменить. Не стоит полагаться на нее. Использование фичи может вызвать ошибки. Не стоит ожидать от нее обратной совместимости.
Указывает require , как обрабатывать определенные расширения файлов.
Обрабатывать файлы с расширением .sjs как .js :
require.extensions['.sjs'] = require.extensions['.js'];Удалено. В прошлом этот список использовался для загрузки в Node.js не-JavaScript модулей путем их компиляции по требованию. Однако на практике существуют гораздо лучшие способы сделать это, например, загрузить модули через какую-либо другую программу Node.js или скомпилировать их в JavaScript заранее.
Избегайте использования require.extensions . Его использование может вызвать тонкие ошибки, а разрешение расширений становится медленнее с каждым зарегистрированным расширением.
require.main ¶
Объект Module , представляющий сценарий входа, загруженный при запуске процесса Node.js, или undefined , если точка входа программы не является модулем CommonJS. Смотрите «Доступ к главному модулю».
В сценарии entry.js :
console.log(require.main);node entry.js1 2 3 4 5 6 7 8 9 10 11 12
Module id: '.', path: '/absolute/path/to', exports: <>, filename: '/absolute/path/to/entry.js', loaded: false, children: [], paths: [ '/absolute/path/to/node_modules', '/absolute/path/node_modules', '/absolute/node_modules', '/node_modules' ] >require.resolve(request[, options]) ¶
- request Путь к модулю для разрешения.
- options
- paths Пути для разрешения местоположения модуля. Если эти пути присутствуют, они используются вместо путей разрешения по умолчанию, за исключением GLOBAL_FOLDERS, таких как $HOME/.node_modules , которые всегда включаются. Каждый из этих путей используется как начальная точка для алгоритма разрешения модулей, что означает, что иерархия node_modules проверяется с этого места.
Использует внутренний механизм require() для поиска местоположения модуля, но вместо загрузки модуля возвращает только имя разрешенного файла.
Если модуль не может быть найден, выдается ошибка MODULE_NOT_FOUND .
require.resolve.paths(request) ¶
- request Путь модуля, пути поиска которого извлекаются.
- Возвращает:
Возвращает массив, содержащий пути, найденные при разрешении request , или null , если строка request ссылается на основной модуль, например http или fs .
Объект module ¶
В каждом модуле свободная переменная module является ссылкой на объект, представляющий текущий модуль. Для удобства, module.exports также доступен через exports module-global. На самом деле module не является глобальным, а скорее локальным для каждого модуля.
module.children ¶
Объекты модуля, впервые требуемые этим модулем.
module.exports ¶
Объект module.exports создается системой Module . Иногда это неприемлемо; многие хотят, чтобы их модуль был экземпляром какого-либо класса. Чтобы сделать это, назначьте нужный объект экспорта в module.exports . Присвоение нужного объекта в exports просто перепривяжет локальную переменную exports , что, вероятно, не является желаемым.
Например, предположим, что мы создаем модуль под названием a.js :
1 2 3 4 5 6 7 8 9
const EventEmitter = require('node:events'); module.exports = new EventEmitter(); // Выполняем некоторую работу, и через некоторое время выдаем // событие 'ready' из самого модуля. setTimeout(() => module.exports.emit('ready'); >, 1000);Затем в другом файле мы можем сделать следующее:
1 2 3 4
const a = require('./a'); a.on('ready', () => console.log('модуль "a" готов'); >);Присвоение module.exports должно быть сделано немедленно. Это не может быть сделано ни в каких обратных вызовах. Это не работает:
1 2 3
setTimeout(() => module.exports = a: 'hello' >; >, 0);const x = require('./x'); console.log(x.a);Ярлык exports ¶
Переменная exports доступна в области видимости модуля на уровне файлов, и ей присваивается значение module.exports перед оценкой модуля.
Она позволяет сократить время, так что module.exports.f = . может быть записано более кратко как exports.f = . . Однако имейте в виду, что, как и любая переменная, если присвоить exports новое значение, оно больше не будет связано с module.exports :
module.exports.hello = true; // Экспортируется из require модуля exports = hello: false >; // Не экспортируется, доступен только в модулеКогда свойство module.exports полностью заменяется новым объектом, обычно также переназначают exports :
1 2 3
module.exports = exports = function Constructor() // . и т.д. >;Чтобы проиллюстрировать поведение, представьте эту гипотетическую реализацию require() , которая очень похожа на то, что на самом деле делает require() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14
function require(/* . */) const module = exports: <> >; ((module, exports) => // Код модуля здесь. В этом примере определите функцию. function someFunc() <> exports = someFunc; // На данный момент exports больше не является сокращением до module.exports, и // этот модуль по-прежнему будет экспортировать пустой объект по умолчанию. module.exports = someFunc; // Теперь модуль будет экспортировать someFunc, а не объект по умолчанию. // объекта по умолчанию. >)(module, module.exports); return module.exports; >module.filename ¶
Полностью разрешенное имя файла модуля.
module.id ¶
Идентификатор для модуля. Обычно это полностью разрешенное имя файла.
module.isPreloading ¶
module.loaded ¶
Завершил ли модуль загрузку или находится в процессе загрузки.
module.parent ¶
Стабильность: 0 – устарело или набрало много негативных отзывов
Эта фича является проблемной и ее планируют изменить. Не стоит полагаться на нее. Использование фичи может вызвать ошибки. Не стоит ожидать от нее обратной совместимости.
Пожалуйста, используйте require.main и module.children вместо этого.
Модуль, который первым потребовал данный модуль, или null , если текущий модуль является точкой входа текущего процесса, или undefined , если модуль был загружен чем-то, что не является модулем CommonJS (например: REPL или import ).
module.path ¶
Имя каталога модуля. Обычно оно совпадает с path.dirname() из module.id .
module.paths ¶
Пути поиска для модуля.
module.require(id) ¶
- id
- Возвращает: экспортированное содержимое модуля
Метод module.require() предоставляет возможность загрузить модуль так, как если бы require() был вызван из исходного модуля.
Для этого необходимо получить ссылку на объект module . Поскольку require() возвращает module.exports , а module обычно только доступен в коде конкретного модуля, для использования он должен быть явно экспортирован.
Основы работы с Node.js
Node.js использует модульную систему. То есть вся встроенная функциональность разбита на отдельные пакеты или модули. Модуль представляет блок кода, который может использоваться повторно в других модулях.
При необходимости мы можем подключать нужные нам модули. Какие встроенные модули есть в node.js и какую функциональность они предоставляют, можно узнать из документации.
Вообще в мире JavaScript есть различные системы модулей. По умолчанию Node.js применяет систему модулей CommonJS , которая рассматривает отдельный файл как модуль, для загрузки модулей применяет функцию require() , в которую передается название модуля. К примеру, в первом приложении из предыдущей темы для получения и обработки запроса был необходим модуль http :
const http = require("http");После получения модуля мы сможем использовать весь определенный в нем функционал, который опять же можно посмотреть в документации.
Подобным образом мы можем загружать и использовать другие встроенные модули. Например, используем модуль os, который предоставляет информацию об окружении и операционной системе. Для этого определим файл app.js со следующим кодом:
const os = require("os"); // получим имя текущего пользователя const userName = os.userInfo().username; console.log(userName);Запустим это приложение командой node app.js , и на консоль будет выведено имя текущего пользователя:
c:\app> node app.js Eugene c:\app>
Создание своих модулей
Мы не ограничены встроенными модулями и при необходимости можем создать свои. Так, добавим в каталог приложения (где находится файл app.js) новый файл greeting.js и определим в нем следующий код:
console.log("Hello METANIT.COM");В файле app.js подключим наш модуль:
const greeting = require("./greeting");В отличие от встроенных модулей для подключения своих модулей надо передать в функцию require относительный путь с именем файла (расширение файла необязательно). Запустим приложение:
c:\app> node app.js Hello METANIT.COM c:\app>
На консоль выводится та строка, которая определена в файле greeting.js.
Стоит отметить, что внутри модуля с помощью специального объекта module мы можем получить инфрмацию о модуле. Например, изменим файл greeting.js следующим образом:
console.log(module);
Повторно запустим файл app.js . В моем случае вывод будет следующим:
c:\app> node app.js < id: '/Users/eugene/Documents/app/greeting.js', path: '/Users/eugene/Documents/app', exports: <>, filename: '/Users/eugene/Documents/app/greeting.js', loaded: false, children: [], paths: [ '/Users/eugene/Documents/app/node_modules', '/Users/eugene/Documents/node_modules', '/Users/eugene/node_modules', '/Users/node_modules', '/node_modules' ] > c:\app>
Здесь мы видим, что объект module содержит ряд свойств, которые позволяют получить каталог и путь к файлу текущего модуля, а также пути, где будет идти поиск модулей.
Экспорт из модуля
Теперь определим в модуле greeting.js какое-нибудь более интересное содержимое. Что может определять модуль? По сути модуль — это обычный файл кода javascript, который может определять переменные, константы, функции, вызывать свои и другие функции. Например, изменим файл greeting.js следующим образом:
const currentDate = new Date(); // в зависимости от часа выводим определенное сообщение function printMessage(name) < const hour = currentDate.getHours(); if(hour >16) console.log("Добрый вечер,", name); else if(hour > 10) console.log("Добрый день,", name); else console.log("Доброе утро,", name); >Здесь определена константа currentDate , которая хранит текущую дату. Также определена функция printMessage, в которую передается имя пользователя, и в зависимости от текущего часа выводится то или иное сообщение.
Мы можем использовать константу date и функцию printMessage внутри модуля greeting.js, однако извне они не доступны. Для проверки доступности в файле app.js выведем модуль greeting на консоль:
const greeting = require("./greeting"); console.log(greeting); // <>Запустим файл app.js
c:\app> node app.js <> c:\app>
Мы видим, что модуль greeting представляет пустой объект. Но в реальности в нем определена константа и функция. И чтобы получить их в других модулях, их надо экспортировать. Для этого изменим файл greeting.js следующим образом:
const currentDate = new Date(); exports.date = currentDate; // экспортируем константу currentDate под именем date // экспортируем функцию exports.printMessage = function (name) < const hour = currentDate.getHours(); if(hour >16) console.log("Добрый вечер,", name); else if(hour > 10) console.log("Добрый день,", name); else console.log("Доброе утро,", name); >Чтобы какие переменные/константы или функции модуля были доступны, необходимо определить их в объекте module.exports . Объект module.exports — это то, что возвращает функция require() при получении модуля.
В частности, здесь определяется свойство exports.date , которое хранит значение currentDate, и свойство exports.printMessage , которое хранит функцию.
Далее изменим файл app.js :
const os = require("os"); const greeting = require("./greeting"); // имя текущего пользователя const userName = os.userInfo().username; console.log(`Дата запроса: $`); greeting.printMessage(userName);Все экспортированные переменные/константы и функции доступны через свойства, которые были установлены при экспорте — greeting. date и greeting. printMessage .
Перезапустим приложение. В моем случае я получу следующий результат:
c:\app> node app.js Дата запроса: Tue Nov 21 2023 18:23:06 GMT+0300 (Москва, стандартное время) Добрый вечер, Eugene c:\app>
Импорт компонентов модуля
В примере выше мы импортировали всю функциональность модуля как единое целое и через имя greeting могли обращаться к этой функциональности:
const greeting = require("./greeting");Однако также мы можем импортировать компоненты по отдельности:
// получаем компоненты модуля по отдельности const = require("./greeting"); // имя текущего пользователя const userName = "Tom"; console.log(`Дата запроса: $`); printMessage(userName);Здесь для получения компонентов модуля указываем их имена в фигурных скобках
const = require("./greeting");Передаваемые имена — имена объектов, установленных при экспорте. После этого можно использовать имена этих компонентов без имени модуля:
printMessage(userName);
Необязательно получать все компоненты. Можно получать только некоторые, которые мы собираемся использовать:
// получаем только функцию printMessage const = require("./greeting"); // или так // const printMessage = require("./greeting").printMessage; const userName = "Tom"; printMessage(userName);Можно определить свое имя для компонента модуля:
// функция print представляет функцию из printMessage const print = require("./greeting").printMessage; const userName = "Bob"; print(userName);Определение конструкторов и объектов в модуле
Кроме определения простейших функций или свойств в модуле могут определяться сложные объекты или функции конструкторов, которые затем используются для создания объектов. Так, добавим в папку проекта новый файл user.js :
function User(name, age) < this.name = name; this.age = age; this.print = function()< console.log(`Имя: $Возраст: $`); > > User.prototype.sayHi = function() < console.log(`Привет, меня зовут $`); >; module.exports = User;
Здесь определена стандартная функция конструктора User, которая принимает два параметра. При этом весь модуль теперь указывает на эту функцию конструктора:
module.exports = User;
Подключим и используем этот модуль в файле app.js :
// получаем только функцию printMessage const User = require("./user.js"); const eugene = new User("Eugene", 22); eugene.print(); eugene.sayHi();c:\app> node app.js Имя: Eugene Возраст: 22 Привет, меня зовут Eugene c:\app>