Три различия между требованием и импортом в Node.js

Эти различия относятся к оператору import, а не к выражению import (см. Эту страницу для получения дополнительной информации о последнем, который можно использовать для динамического импорта модулей).

Это первая часть из четырех частей, посвященных модулям JavaScript.

  1. Три различия между требованием и импортом в Node.js
  2. Использование модулей ES с модулями CommonJS в Node.js
  3. Использование модулей ES с модулями CommonJS в браузере
  4. Использование модулей ES с модулями CommonJS с webpack

Ознакомьтесь с полными примерами кода здесь: https://github.com/arcticmatt/javascript_modules/tree/master/import_vs_require.

  1. При использовании import ... from ... путь к модулю должен быть строковым литералом. При использовании require путь к модулю может быть динамическим.

Например, это работает:

const name = "module2";
const obj = require(`./${name}`);

Но это приведет к ошибке SyntaxError: Unexpected template string при запуске с node.

const name = "module2";
import { func } from `./${name}`;

Код типа import { func } from ("./partial/path" + someVariable); также приведет к синтаксической ошибке.

Почему это? Смотрите следующий пункт.

2. Порядок исполнения различается. require будет запущен встроенным после выполнения приведенного выше кода. import запускается перед остальной частью скрипта.

Предположим, что module2.js имеет console.log("require module2"); вверху, тогда, если мы запустим этот код:

console.log("require module1");
const obj = require("./module2");
console.log(`module2 = ${obj.module2}`);

это приводит к следующему:

require module1
require module2
module2 = require module2

С другой стороны, с модулями ES…

console.log("require module1");
import module2 from "./module2.js";
console.log(`module2 = ${module2}`);

Выполнение этого приводит к следующему:

require module2
require module1
module2 = require module2

Модули ES: Глубокое погружение в мультфильм рассматривает эту тему гораздо глубже.

3. Вы можете оставить .js расширение при импорте локального модуля с require, но не можете сделать то же самое при использовании import.

Это верно по умолчанию в браузере и в Node.js. Например, require("./module2") работает, но эквивалент с использованием import должен быть записан как import module2 from "./module2.js". Если вы опустите расширение в Node.js, вы получите сообщение об ошибке вида: Error [ERR_MODULE_NOT_FOUND]: Cannot find module …

В Node.js вы можете использовать параметр --experimental-specifier-resolution=node, чтобы обойти это поведение, т.е. это позволит вам опускать .js расширения при использовании import.

Кроме того, в webpack есть опция, изменяющая это поведение. В частности, если resolve.enforceExtension равно true, требуются расширения. По умолчанию для этого параметра установлено значение false, что объясняет, почему во многих фреймворках (например, Next.js, который негласно использует webpack) вы можете использовать import без указания расширений файлов.