ES-модуль - файл JavaScript з власною областю видимості, який явно експортує те, що дає назовні, й імпортує те, що йому потрібно.
// money.js
export function formatPrice(amount) {
return `${amount.toFixed(2)} грн`;
}
// app.js
import { formatPrice } from './money.js';
console.log(formatPrice(100));
<script type="module" src="/app.js"></script>
Як було до модулів: усі <script> на сторінці ділили одну глобальну область видимості. Функція formatPrice з одного файлу ставала глобальною і могла перезаписати однойменну з іншого. Порядок підключення скриптів доводилося вибудовувати вручну, а залежності між файлами ніде не були записані.
Чим модулі відрізняються:
- власна область видимості: змінні верхнього рівня модуля не потрапляють у
window; - явні залежності: з
importвидно, звідки що береться, - це читає і людина, і збирач; - строгий режим завжди (
'use strict'не потрібен); - виконуються один раз: скільки б модулів не імпортували
money.js, він завантажиться й виконається лише раз, а всі отримають той самий екземпляр; - відкладене виконання:
<script type="module">поводиться якdefer- виконується після розбору HTML і не блокує його; - top-level
awaitдозволений; thisна верхньому рівні -undefined, а неwindow;- завантаження з іншого домену підпорядковується CORS.
Імпорти статичні: import - лише на верхньому рівні модуля, шлях - рядок-константа. Завдяки цьому залежності відомі ще до виконання коду - звідси можливість tree shaking. Для завантаження «за потреби» є динамічний import().
Модулі в Laravel-проєкті: resources/js/app.js - точка входу ES-модуля. Vite збирає граф імпортів, а директива @vite в Blade підключає результат як <script type="module">. Пакети з npm імпортуються за назвою (import axios from 'axios') - браузер такого шляху не розуміє, тому розв'язує його збирач (або import map без збирача).