ES Modules в браузере

ES Modules (ECMAScript Modules, ESM) представляют собой стандартный способ организации JavaScript-кода с использованием импортов и экспортов. Они обеспечивают модульность, изоляцию и поддержку повторного использования кода без необходимости использования сборщиков вроде Webpack или Rollup.

Основные концепции

Экспорт и импорт — ключевые элементы ESM. Модуль может экспортировать значения, функции, классы или объекты, а другой модуль может их импортировать.

// math.js
export function add(a, b) {
  return a + b;
}

export const PI = 3.14159;
// main.js
import { add, PI } from './math.js';

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

Существует два типа экспорта:

  1. Именованный экспорт (export const name = ...) — позволяет экспортировать несколько значений из одного модуля.
  2. Экспорт по умолчанию (export default ...) — предназначен для экспорта одного основного объекта, функции или класса из модуля.
// defaultExport.js
export default function greet(name) {
  return `Hello, ${name}`;
}
// main.js
import greet from './defaultExport.js';
console.log(greet('Vue')); // Hello, Vue

Подключение модулей в браузере

Для использования ESM в браузере требуется указывать атрибут type="module" в теге <script>. Это сообщает браузеру, что скрипт является модулем.

<script type="module" src="main.js"></script>

Особенности модульного скрипта в браузере:

  • Асинхронная загрузка — модуль загружается асинхронно, но выполняется строго в порядке импортов.
  • Использование строгого режима — все модули работают в strict mode по умолчанию.
  • Локальная область видимости — переменные, объявленные в модуле, не загрязняют глобальный объект window.
  • Поддержка относительных и абсолютных путей — пути к модулям указываются явно с расширением .js или .mjs.

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

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

button.addEventListener('click', async () => {
  const module = await import('./lazyModule.js');
  module.showMessage();
});

Преимущества динамического импорта:

  • Отложенная загрузка кода до момента его использования.
  • Возможность разбиения приложения на чанки без сборщика.
  • Поддержка условного импорта.

Использование Vue.js с ES Modules

Vue.js полностью совместим с ESM, что позволяет импортировать компоненты, утилиты и плагины через модули. Например, стандартная структура проекта может выглядеть так:

// App.vue
<template>
  <div>
    <HelloWorld msg="Привет, Vue!" />
  </div>
</template>

<script type="module">
import HelloWorld from './components/HelloWorld.js';

export default {
  components: { HelloWorld }
};
</script>

Компоненты и утилиты могут быть организованы как отдельные модули. Это улучшает читаемость и облегчает поддержку кода:

// utils/calc.js
export function multiply(a, b) {
  return a * b;
}
// components/Calculator.js
import { multiply } from '../utils/calc.js';

export default {
  methods: {
    calculate(x, y) {
      return multiply(x, y);
    }
  }
};

Ограничения ES Modules в браузере

  • CORS-политика — модули должны загружаться с того же происхождения или с корректно настроенными заголовками.
  • Отсутствие поддержки старых браузеров — для Internet Explorer и некоторых старых версий Safari требуется транспиляция через Babel.
  • Не поддерживаются относительные импорты без расширения в некоторых браузерах — всегда указывать .js или .mjs.

Рекомендации по структуре проекта

  • Каждая логическая часть приложения должна быть вынесена в отдельный модуль.
  • Утилиты, хелперы и константы стоит держать в папке utils.
  • Компоненты Vue в виде отдельных модулей повышают повторное использование и упрощают тестирование.
  • Для динамического импорта тяжелых модулей можно использовать ленивую загрузку, что ускоряет первоначальное рендеринг страницы.

ES Modules в браузере обеспечивают современную модульную архитектуру, позволяя строить масштабируемые приложения Vue.js без необходимости полной сборки на этапе разработки. Такой подход упрощает организацию кода, повышает читаемость и уменьшает вероятность конфликтов между глобальными переменными.