Перейти к основному содержимому

ES-модули в браузере и обзор Temporal API

Разработчику Архитектору

Два мира модулей

В структуре и подключении кода разобраны теги <script>, bundler и CommonJS в Node. Переиспользуемые теги без сборки — Web Components. В браузере без сборки стандарт — ES-модули: файлы с import / export, отдельная область видимости, строгий режим по умолчанию.

Классический скриптES-модуль
Подключение<script src="app.js"><script type="module" src="app.js">
Область видимостиглобальная (засоряет window)модульная
import / exportсинтаксическая ошибкада
Отложенная загрузкаdefer вручнуюкак defer по умолчанию
CORSобычный скриптмодули — только same-origin или CORS

Подключение в HTML

<script type="module" src="./main.js"></script>
<script type="module">
import { init } from './widgets/carousel.js';
init();
</script>

Второй блок — inline-модуль; пути import считаются от URL страницы, не от файла (для предсказуемости выносите логику в .js).

Именованный и экспорт по умолчанию

utils.js:

export const PI = 3.14159;

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

export default function createId() {
return crypto.randomUUID();
}

main.js:

import createId, { sum, PI } from './utils.js';

console.log(sum(2, PI));
console.log(createId());

Расширение .js в пути обязательно в нативных модулях браузера (в отличие от некоторых bundler-конфигов).

Динамический import

Загрузка по условию (ленивая вкладка, маршрут):

button.addEventListener('click', async () => {
const { renderChart } = await import('./chart.js');
renderChart('#host');
});

Возвращает промис модуля — удобно с async/await.

import.meta

Метаданные текущего модуля:

console.log(import.meta.url); // адрес файла модуля

В dev-средах (Vite) через import.meta.env пробрасывают переменные окружения — это расширение инструмента, не часть чистого стандарта HTML.


Отличие от сборки (Vite, Webpack)

Браузер загружает каждый import отдельным HTTP-запросом. В продакшене почти всегда сборщик склеивает дерево в один или несколько chunk-файлов, но синтаксис import/export тот же.

Правила проекта (алиасы @/components, импорт без .js) настраиваются в bundler; в учебных примерах «голого» браузера пишите относительные пути с .js.


Модули и Node

В Node ESM включается через "type": "module" в package.json или расширение .mjs — см. Node.js. Один и тот же файл .js с export может работать и в браузере (через type="module"), и в Node при правильной конфигурации.


Temporal API — зачем смотреть вперёд

Объект Date в JavaScript исторически неудобен: месяцы с нуля, мутации через setMonth, путаница локали и UTC, слабая арифметика интервалов.

Temporal (стадия стандарта ECMAScript) разделяет понятия:

ТипНазначение
Temporal.PlainDateкалендарная дата без часового пояса
Temporal.PlainTimeвремя суток
Temporal.PlainDateTimeдата + время без пояса
Temporal.Instantмомент на шкале UTC
Temporal.ZonedDateTimeмомент в конкретном часовом поясе
Temporal.Durationдлительность
Temporal.Now«сейчас» в нужном представлении

Пример стиля API (когда движок поддерживает глобальный Temporal):

const birth = Temporal.PlainDate.from('1990-06-15');
const today = Temporal.Now.plainDateISO();
const years = today.since(birth, { largestUnit: 'year' }).years;

Пока поддержка в браузерах неполная, в продакшене используют Date, Intl.DateTimeFormat (см. материалы про Intl в объектах) или библиотеки вроде Luxon / date-fns / Day.js.

Практическая рекомендация

  • Хранить и передавать на сервер ISO 8601 (2024-05-25T14:00:00.000Z).
  • Показывать пользователю — Intl.DateTimeFormat с локалью.
  • Считать интервалы — миллисекунды или библиотека; следить за обновлением Temporal в истории языка.

Краткий итог

type="module" даёт изолированные файлы с import/export прямо в браузере; import() — ленивая загрузка. Temporal — будущая замена хрупкому Date; до широкой поддержки опирайтесь на ISO-строки, Intl и проверенные библиотеки.


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).