Загрузка…
Загрузка…
Представьте, что вы строите большой замок из Лего. Вы бы не свалили все кусочки в одну огромную кучу. Вместо этого вы бы организовали их в более мелкие, удобные коробки: одну для кубиков, одну для окон, одну для минифигурок и т. д. Каждая коробка представляет собой «модуль».
В программировании модули помогают нам:
Node.js, будучи средой выполнения JavaScript на стороне сервера, нуждался в способе обработки модулей задолго до того, как браузеры стандартизировали свои собственные. Это привело к созданию CommonJS. Позже, по мере развития JavaScript, появилась стандартизированная система модулей: ES Modules.
Давайте углубимся в детали.
CommonJS был исходной системой модулей Node.js по умолчанию. Он разработан в первую очередь для серверных сред, где доступ к файловой системе является синхронным.
module.exports
или
exports
.
// myModule.js (CommonJS)
const PI = 3.14;
function add(a, b) {
return a + b;
}
// Option 1: Assign to module.exports (replaces the default empty object)
module.exports = {
PI,
add
};
// Option 2: Add properties to the 'exports' object (convenience reference to module.exports)
// Note: If you assign directly to module.exports later, it will override anything added to 'exports'.
// exports.PI = PI;
// exports.add = add;
```
* **Импорт**: вы загружаете модули, используя синхронный
`require()`
функция.
```javascript
// app.js (CommonJS)
const myModule = require('./myModule'); // Path relative to the current file
console.log(myModule.PI); // Output: 3.14
console.log(myModule.add(2, 3)); // Output: 5
```
### 2. Разрешение модуля и механизм загрузки
* **Синхронная загрузка**: при звонке
`require()`
, Node.js прекращает выполнение текущего кода, загружает запрошенный модуль с диска, выполняет его и затем продолжает. Вот почему CJS часто называют «синхронным».
* **Аналогия**: представьте, что вы печете торт (ваш
`app.js`
). Вы понимаете, что вам нужна мука (модуль). Вы *прекращаете* выпекать, идете в кладовую, берете муку и *затем* возобновляете выпечку.
* **Динамическая загрузка**: Вы можете позвонить
`require()`
в любом месте вашего кода, условно.
```javascript
// Conditional loading in CJS
if (process.env.NODE_ENV === 'development') {
const devTool = require('./dev-tool');
devTool.init();
}
```
* **Кэширование модуля**: после того, как модуль
`require`
d впервые Node.js кэширует свои
`module.exports`
объект. Последующий
`require`
вызовы одного и того же модуля вернут кэшированную версию без повторного выполнения кода модуля. Это предотвращает избыточную работу и возможные побочные эффекты.
* **Аналогия**: После того, как вы впервые достали муку из кладовой, вы ее маркируете. В следующий раз, когда вам понадобится мука, вы просто проверите свой список и поймете, что она у вас уже есть, не заходя снова в кладовую.
* **Копия значения**: когда вы
`require`
модуль, вы получаете *копию*
`module.exports`
объект в момент его загрузки. Если исходный модуль позже внутренне изменит экспортированное значение, ваша импортированная копия не отразит это изменение.
* **Аналогия**: вы просите у друга копию его любимого рецепта. Если ваш друг напишет новый ингредиент в своем оригинальном рецепте, ваша копия останется неизменной.
* **
`this`
Контекст**: Внутри модуля CJS
`this`
на верхнем уровне относится к
`module.exports`
.
```javascript
// module.js (CommonJS)
console.log(this === module.exports); // Output: true
```
* **
`__dirname`
и
`__filename`
**: Эти глобальные переменные доступны в модулях CJS и содержат имя каталога и имя текущего файла модуля соответственно.
### 3. Последствия для проектирования и объединения приложений
* **Фокус на стороне сервера**: CJS идеально подходил для первоначального варианта использования Node.js на стороне сервера, где синхронный доступ к файлам приемлем и часто желателен.
* **Никакой «тряски деревьев»**: Потому что
`require()`
является динамическим, сборщики (например, Webpack) не могут точно знать, какие части модуля используются, а какие не используются, без фактического запуска кода. Это делает «встряхивание дерева» (удаление неиспользуемого кода) трудным или невозможным при использовании CJS, что потенциально может привести к увеличению размеров пакетов для клиентских приложений.
* **Упрощенное объединение (исторически)**: Для простого объединения на стороне сервера или браузерных приложений, где размер пакета не был главной проблемой, CJS был простым.
* **Устаревшее**: многие существующие пакеты и приложения Node.js по-прежнему написаны на CJS.
---
## ES-модули (ESM)
Модули ES — это официальная стандартизированная система модулей для JavaScript. Они были разработаны с учетом как браузерной, так и серверной среды, с упором на статический анализ и асинхронную загрузку.
### 1. Синтаксис
* **Экспорт**: вы используете
`export`
ключевое слово.
```javascript
// myModule.mjs (ES Module)
export const PI = 3.14; // Named export
export function add(a, b) { // Named export
return a + b;
}
// Default export (only one per module)
const subtract = (a, b) => a - b;
export default subtract;
```
* **Импорт**: вы используете
`import`
ключевое слово.
```Javascript
// app.mjs (модуль ES)
импортировать {PI, добавить} из './myModule.mjs'; // Именованный импорт
импортируйте mySubtract из './myModule.mjs'; // Импорт по умолчанию (можно назвать как угодно)
импортировать * как myUtils из './myModule.mjs'; // Импортируем все как объект пространства имен
console.log(PI); // Вывод: 3.14
console.log(добавить(2, 3)); // Вывод: 5
console.log(mySubtract(5, 2)); // Вывод: 3
console.log(myUtils.PI); // Вывод: 3.14
## Итог
Ключевой вывод для собеседования: Понимание модулей: зачем они нам нужны?