Модули являются важной концепцией для понимания проектов Node.js. В этом посте мы рассмотрим модули Node: require
, exports
и будущее import
.
Модули Node.js позволяют писать повторно используемый код. Вы можете вкладывать их друг в друга. Используя Node Package Manager (NPM) вы можете опубликовать свои модули и сделать их доступными для сообщества. Кроме того, NPM позволяет повторно использовать модули, созданные другими разработчиками.
[note] Мы используем Node 12.x для примеров и синтаксис ES6 +. Тем не менее, концепции действительны для любой версии. [/note]
В этом разделе мы рассмотрим, как создавать модули Node и каждый из его компонентов:
- Require
- Exports
- Module (module.exports vs. export)
- Import
require
— подключение модулей в Node.js
require
используются для подключения модулей. Это позволяет вам включать модули в ваши программы. Вы можете добавить встроенные базовые модули Node.js, модули, написанные сообществом разработчиков из папки node_modules
и локальные модули.
Допустим, мы хотим прочитать файл из файловой системы. Node.js имеет для этого встроенный модуль под названием «fs»:
const fs = require('fs'); fs.readFile('./file.txt', 'utf-8', (err, data) => { if(err) { throw err; } console.log('data: ', data); });
Как видите, мы импортировали модуль «fs» в наш код. Это позволяет нам использовать любую прикрепленную к нему функцию, например «readFile» и многие другие.
Функция require
будет искать файлы в следующем порядке:
- Built-in. Встроенные в Node.js модули (такие как
fs
) - NPM Modules. Устанавливаемые модули из репозитория npm, их можно посмотреть в папке
node_modules
. - Local Modules. Локальные модули, которых надо подгружать из вашего локального места через обращение к пути
./
,/
or../
. Модули соответствуют расширениям:*.js
,*.json
,*.mjs
,*.cjs
,*.wasm
и*.node
.
Давайте теперь объясним каждый чуть подробнее
1. Встроенные модули Node.js
Когда вы устанавливаете Node.js, он поставляется со многими встроенными модулями. Node.js поставляется с батарейками в комплекте :).
Некоторые из наиболее используемых основных модулей:
- fs: позволяет манипулировать (создавать / читать / писать) файлами и каталогами .
- path: утилиты для работы с файлами и каталогами путей.
- http: создавать HTTP-серверы и клиенты для веб-разработки .
- url: утилиты для разбора URL и извлечения из него элементов .
Эти модули вам не нужно устанавливать, вы можете импортировать их и использовать в своих программах из коробки.
2. Модули из репозитория NPM
Модули NPM — это сторонние модули, которые вы можете использовать после их установки. Назвать несколько:
- lodash: коллекция служебных функций для манипулирования массивами, объектами и строками.
- request: HTTP-клиент проще в использовании, чем встроенный модуль
http
. - express: HTTP сервер для создания сайтов и API. Опять же, проще в использовании, чем встроенный модуль
http
.
Это вы должны сначала установить их, вот так:
$ npm install express
и затем вы можете ссылаться на них, как на встроенные модули, но на этот раз они будут обслуживаться из папки node_modules, которая содержит все сторонние библиотеки.
const express = require('express');
3. Собственные модули
Если вы не можете найти встроенную или стороннюю библиотеку, которая делает то, что вы хотите, вам придется разрабатывать ее самостоятельно. В следующих разделах вы узнаете, как это сделать с помощью экспорта exports
.
exports
— экспорт данных из модуля Node.js
Ключевое слово export дает вам возможность «экспортировать» ваши объекты и методы. Давайте сделаем пример:
const PI = 3.14159265359; exports.area = radius => (radius ** 2) * PI; exports.circumference = radius => 2 * radius * PI;
В приведенном ниже коде мы экспортируем функции area
и circumference
. Мы определили константу PI
, но она доступна только внутри модуля. Только элементы, связанные с exports
, доступны за пределами модуля.
Итак, мы можем использовать его, используя require
в другом файле, как показано ниже:
const circle = require('./circle'); const r = 3; console.log(`Circle with radius ${r} has area: ${circle.area(r)}; circumference: ${circle.circumference(r)}`);
Заметил, что на этот раз мы добавляем имя модуля к ./
. Это означает, что модуль является локальным файлом.
Обертка модуля в Node.js
Вы можете рассматривать каждый модуль Node.js как отдельную функцию, подобную следующей:
(function (exports, require, module, __filename, __dirname) { module.exports = exports = {}; // Your module code ... });
Мы уже описывали exports
и require
. Обратите внимание на связь между module.exports
и exports
. Они указывают на одну и ту же ссылку. Но если вы назначите что-то напрямую для exports
, вы прервете его ссылку на module.exports
— подробнее об этом в следующем разделе.
Для нашего удобства определены __filename
и __dirname
. Они предоставляют полный путь к текущему файлу и каталогу. Последний исключает имя файла и печатает путь к каталогу.
Например, для нашего модуля ./circle.js
это будет примерно так:
__filename
:/User/websofter/code/circle.js
__dirname
:/User/websofter/code
Хорошо, мы рассмотрели exports
, require
, __filename
и __dirname
. Единственное, что мы не рассмотрели, это module
. Давай сделаем это!
Что использовать module.exports
vs. exports
?
Модуль module
не является глобальным и является локальным для каждого модуля. Он содержит метаданные о модуле, такие как id
, exports
, parent
, children
и так далее.
exports
— это псевдоним module.exports
. Следовательно, все, что вы назначаете для exports
, также доступно в module.exports
. Однако, если вы назначите что-то напрямую для экспорта, вы потеряете ярлык для module.exports
. Например.
class Cat { makeSound() { return `${this.constructor.name}: Meowww`; } } // exports = Cat; // It will not work with `new Cat();` // exports.Cat = Cat; // It will require `new Cat.Cat();` to work (yuck!) module.exports = Cat;
Попробуйте следующий случай с exports
, а затем с module.exports
.
const Cat = require('./cat'); const cat = new Cat(); console.log(cat.makeSound());
Подводя итог, когда использовать module.exports
против exports
:
Используйте exports
для:
- Экспорт именованной функции. например
exports.area
,exports.circumference
.
Используйте module.exports
для:
- если вы хотите экспортировать объект, класс, функцию на корневом уровне (например,
module.exports = Cat
) - Если вы предпочитаете возвращать один объект, который предоставляет несколько назначений. например
module.exports = {area, circumference};
imports
— новые возможности импорта модулей
Начиная с версии 8.5.0+, Node.js нативно поддерживает модули ES с флагом функции и новым расширением файла * .mjs
.
Например, наш предыдущий circle.js
можно переписать как circle.mjs
следующим образом:
const PI = 3.14159265359; export function area(radius) { return (radius ** 2) * PI; } export function circumference(radius) { return 2 * radius * PI; }
Затем мы можем использовать импорт:
import { area, circumference } from './circle.mjs'; const r = 3; console.log(`Circle with radius ${r} has area: ${area(r)}; circunference: ${circumference(r)}`);
И, наконец, вы можете запустить его, используя флаг функции экспериментального модуля:
$ node --experimental-modules main.mjs
Если вам не нравятся экспериментальные модули, другой альтернативой является использование транспилятора. Это преобразует современный JavaScript в более старые версии для вас. Хорошими вариантами являются TypeScript, Babel и Rollup.
Устранение неполадок при использовании imports
и require
Экспериментальный Флаг
Если вы не используете экспериментальный флаговый узел --experimental-modules
и пытаетесь использовать import
, вы получите ошибку, подобную этой:
internal/modules/cjs/loader.js:819
throw new ERR_REQUIRE_ESM(filename);
^
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: bla bla blah
Расширения файлов .mjs vs .js (или .cjs)
Если у вас есть код модуля написан в файле с расширением *.mjs
, то вы не можете использовать require
, он выдаст ошибку (ReferenceError: require не определен)
. .mjs
предназначен для импорта через imports
в соответствии ECMAScript, а .js
— для регулярного использования require
.
Однако с помощью *.mjs
вы можете загружать оба вида модулей!
import { area, circumference } from './circle.mjs'; import Cat from './cat.js'; const r = 3; console.log(`Circle with radius ${r} has area: ${area(r)}; circumference: ${circumference(r)}`); const cat = new Cat(); console.log(cat.makeSound());
Обратите внимание, что cat.js
использует модули commonJS.
Резюме
Мы узнали о том, как создавать модули Node.js, и использовали этот опыт в нашем коде. Модули позволяют легко повторно использовать код. Они обеспечивают функциональность, которая изолирована от других модулей. Функция require
используется для загрузки модулей. exports
и module.exports
позволяют нам определить, какие части нашего кода мы хотим представить. Мы также исследовали разницу между module.exports
и exports
. Наконец, мы быстро выбрали, что будет с модулями, использующими imports
.