JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /ES Modules: import, export, настройка package.json

ES Modules: import, export, настройка package.json

Модуль 4: Node.js, Next.js и Angular
1 уровень , 7 лекция
Открыта

1. Зачем нужны ES Modules и чем они отличаются от CommonJS

В мире фронтенда ES Modules уже давно стали стандартом. Они позволяют явно объявлять, что именно модуль "выставляет наружу" (export), и что он хочет "забрать" из других модулей (import). Это делает код более прозрачным и предсказуемым. В Node.js долгое время использовалась система CommonJS (require/module.exports), но теперь поддержка ESM стала полноценной.

Кратко:

  • CommonJS — старая школа Node.js, синхронные модули, require и module.exports.
  • ES Modules (ESM) — современный стандарт, асинхронная загрузка (в браузере), import и export.

Зачем переходить на ESM?

  • Совместимость с современным фронтендом.
  • Явные зависимости и экспорт.
  • Новые возможности JavaScript (top-level await, динамические импорты и др.).
  • Более строгий и читаемый синтаксис.

2. Синтаксис export: как делиться своими функциями и переменными

В ESM всё начинается с того, что модуль экспортирует то, чем хочет поделиться с другими. Есть два основных способа:

Именованный экспорт (named export)

Можно экспортировать сколько угодно сущностей по имени:

// math.js
export const PI = 3.14159;
export function add(a, b) {
  return a + b;
}
export class Circle {
  constructor(radius) {
    this.radius = radius;
  }
  area() {
    return PI * this.radius * this.radius;
  }
}

Можно экспортировать уже объявленные ранее переменные или функции:

const E = 2.718;
function multiply(a, b) {
  return a * b;
}
export { E, multiply };

Экспорт по умолчанию (default export)

Модуль может экспортировать что-то "главное". Такой экспорт может быть только один на модуль:

// greeting.js
export default function greet(name) {
  return `Hello, ${name}!`;
}

Можно экспортировать по умолчанию любой объект, функцию, класс или даже число:

export default 42;

3. Синтаксис import: как забирать чужое

Импорт именованных экспортов

import { PI, add } from './math.js';

console.log(PI); // 3.14159
console.log(add(2, 3)); // 5

Можно импортировать и переименовывать:

import { PI as piValue } from './math.js';
console.log(piValue);

Импорт "по умолчанию"

import greet from './greeting.js';

console.log(greet('Alice')); // Hello, Alice!

Импортировать всё в объект

import * as math from './math.js';

console.log(math.PI); // 3.14159
console.log(math.add(2, 2)); // 4

Комбинированный импорт

import greet, { PI, add } from './mathAndGreet.js';

4. Как Node.js понимает, что мы хотим использовать ESM

В Node.js по умолчанию всё ещё действует CommonJS. Чтобы использовать ESM, нужно явно указать это в проекте.

Способ 1: type: "module" в package.json

Самый современный и рекомендуемый способ — добавить в package.json:

{
  "type": "module"
}

Теперь все файлы с расширением .js в вашем проекте будут рассматриваться как ES-модули.

Важно!
- После этого все импорты должны быть через import/export.
- require и module.exports больше не работают (если только не используете .cjs расширение).
- Пути к модулям должны быть с расширением: import { foo } from './foo.js' (да, даже если файл называется foo.js — без расширения работать не будет).

Способ 2: расширения .mjs и .cjs

  • Файлы с расширением .mjs всегда считаются ES-модулями, даже если нет type: "module".
  • Файлы с расширением .cjs — всегда CommonJS, даже если в проекте стоит type: "module".

Это позволяет смешивать стили, если вдруг очень хочется (или если есть старые зависимости).

5. Примеры: разбиваем приложение на ESM-модули

Давайте продолжим развивать наше мини-приложение, добавив модуль с математикой и модуль для приветствий.

math.js

// math.js
export const PI = 3.14159;

export function add(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

greeting.js

// greeting.js
export default function greet(name) {
  return `Привет, ${name}!`;
}

main.js

// main.js
import { PI, add } from './math.js';
import greet from './greeting.js';

console.log(greet('Мир')); // Привет, Мир!
console.log(`PI = ${PI}`);
console.log(`2 + 3 = ${add(2, 3)}`);

package.json

{
  "name": "my-esm-app",
  "version": "1.0.0",
  "type": "module"
}

Теперь можно запускать:

node main.js

6. Полезные нюансы

Пути с расширениями

Node.js требует, чтобы в ESM-импортах указывалось расширение файла:

import { foo } from './foo.js'; // обязательно .js!

Если забудете — получите ошибку вроде:

Error [ERR_MODULE_NOT_FOUND]: Cannot find module ...

Нельзя импортировать JSON "в лоб"

В CommonJS было можно:

const data = require('./data.json');

В ESM так уже нельзя без дополнительных флагов/опций. Для этого используйте динамический импорт + опцию assert:

import data from './data.json' assert { type: 'json' };

Но: поддержка assert { type: 'json' } появилась только в Node 17+, и не все инструменты её любят.

Top-level await

ESM позволяет использовать await прямо на верхнем уровне модуля (не только внутри функции):

// main.js
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);

Это невозможно в CommonJS без async IIFE.

Динамический импорт

Можно импортировать модули "на лету":

if (needExtraMath) {
  const math = await import('./math.js');
  console.log(math.PI);
}

Как импортировать сторонние модули из npm

Всё работает привычно:

npm install lodash

В коде:

import _ from 'lodash';
console.log(_.random(1, 10));

Но! Некоторые старые npm-библиотеки не поддерживают ESM. Если возникли ошибки — проверьте документацию пакета. Иногда приходится использовать "default" импорт или динамический импорт.

7. Типичные ошибки при работе с ES Modules в Node.js

Ошибка №1: забыли указать расширение файла в импорте.
В отличие от CommonJS, ESM требует писать import { foo } from './foo.js', а не просто './foo'.

Ошибка №2: смешивание синтаксиса.
Нельзя писать require() и module.exports в файле, который работает как ESM (и наоборот). Если используете ESM — только import/export.

Ошибка №3: забыли добавить "type": "module" в package.json.
Node.js не поймёт, что ваши .js — это ES-модули, и выдаст ошибку синтаксиса при виде import.

Ошибка №4: неправильный импорт default/именованных экспортов.
Если в модуле экспорт только default, импортируйте его как import foo from .... Если именованный — через { ... }. Путаница приводит к undefined.

Ошибка №5: попытка импортировать JSON как модуль без assert.
В ESM для импорта JSON нужен синтаксис import data from './data.json' assert { type: 'json' }; и Node.js 17+.

Ошибка №6: попытка использовать top-level await в CommonJS.
Top-level await работает только в ESM.

1
Задача
Модуль 4: Node.js, Next.js и Angular, 1 уровень, 7 лекция
Недоступна
Экспорт функции по умолчанию и ее использование
Экспорт функции по умолчанию и ее использование
1
Задача
Модуль 4: Node.js, Next.js и Angular, 1 уровень, 7 лекция
Недоступна
Комбинированный импорт и настройка package.json для ESM
Комбинированный импорт и настройка package.json для ESM
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ