# 05. Browser Modules

Спецификация ES6 модулей поддерживается в браузерах при помощи тега `<script type="module">`. Выполняемый в таком скрипте код поддерживает ES6 и спецификацию модулей (импорты/экспорты).

Основные особенности:

1. Для подключения используется `<script type="module">`.
2. Нативные модули в `"strict mode"` по умолчанию;
3. Все определения в модулях не являются частью глобального объекта.
4. `window` по прежнему доступен, но `this` в модуле является `undefined`
5. Модули по умолчанию асинхронны работают как `deferred` скрипты (такое же как у `<script type="text/javascript" defer />`).

Со временем нативные JS-модули будут поддерживаться браузерами без дополнительных утилит. Сейчас браузерной поддержки почти нет. Поэтому ES-модули используются в сочетании с системами сборки, такими как webpack и другими, при подключённом Babel.

```markup
<script type="module">
  import $ from "lib/jquery";
  var x = 123;

  // The current scope is not global
  console.log("$" in window); // false
  console.log("x" in window); // false

  // `this` still refers to the global object
  console.log(this === window); // true
</script>
```

Скрипты и модули не смешиваемы друг с другом:

* Переменные, определенные в скриптах недоступны в модулях
* В скриптах нельзя использовать ES6 импорты/экспорты, но можно библиотеки модулей (UMD и др.).

## Module execution

Главное отличие нативных модулей от обычных скриптов заключается в том, что обычные скрипты загружаются и выполняются сразу же, блокируя парсинг html. Нативные модули по умолчанию имеют поведение `deffered` скриптов вне зависимости от того, импортируют ли они что-нибудь или нет. По умолчанию скрипты в модулях не блокируют, загружаются параллельно и выполняются, когда страница завершает парсинг html. Можно изменить это поведение, добавив атрибут `async`, тогда скрипт будет выполнен, как только он загрузится.

![alt text](/files/-M5buRq_eUoyut_kpcDr)

> The order should be `2.js`, `1.js`, `3.js`.

```markup
<!-- This script will execute after… -->
<script type="module" src="1.js"></script>

<!-- …this script… -->
<script src="2.js"></script>

<!-- …but before this script. -->
<script defer src="3.js"></script>
```

Пример с `async`:

> The fast-downloading scripts should execute before the slow ones.

```markup
<!-- This executes as soon as its imports have fetched -->
<script async type="module">
  import { addTextToBody } from "./utils.js";

  addTextToBody("Inline module executed.");
</script>

<!-- This executes as soon as it & its imports have fetched -->
<script async type="module" src="1.js"></script>
```

## Import paths

Импорт внутри модуля:

* он может начинаться и заканчиваться пробелами;
* он должен быть абсолютным URL-ом или:
* он должен начинаться с `“/”`, `“./”`, или `“../”`.
* `.js` расширение не может быть опущено в директиве `import`;

Еще одно отличие модулей - это возможность загружать файлы с других доменов (например, загрузка модулей с CDN).

```javascript
// Supported:
import { foo } from "https://jakearchibald.com/utils/bar.js";
import { foo } from "/utils/bar.js";
import { foo } from "./bar.js";
import { foo } from "../bar.js";

// Not supported:
import { foo } from "bar.js";
import { foo } from "utils/bar.js";
```

## `sctipt` attributes

Как в классических скриптах, есть много атрибутов, которые можно использовать в `script type=”module”`.

* Атрибут `type` используется для установки типа `"module"`.
* `src` мы используем, чтобы загрузить файл с определенным URI.
* `defer` не нужен для скриптов типа `“module”`, так как это поведение по умолчанию
* Если вы используете `async` атрибут, модуль будет выполнен сразу же как только будет доступен, без `defer` поведения по умолчанию, когда скрипты выполняются по порядку после анализа документа, но перед событием `DOMContentLoaded`.
* `integrity` по-прежнему может быть использован, чтобы убедиться, что выбранные файлы (например, из `cdn`) не были подменены на что-то другое.
* атрибут `crossorigin` дает возможность контролировать обмен данными, которые отправляются с помощью CORS запросов.
* `nonce` — это генерируемый случайным образом хеш, который добавляется в заголовок на сервер и добавляется в тег `script`.

Используйте события `onload` и `onerror` у `script` элемента, чтобы обнаружить, может ли модуль быть успешно выполнен или не может загрузиться.

## `nomodule` attribute

Атрибут `nomodule` означает, что скрипт должен быть выполнен только если браузер не поддерживает ES модули:

```markup
<script type="module" src="app.js"></script>
<script nomodule defer src="bundle.js"></script>
```

В данном примере кода если браузер поддерживает модули, то будет загружен и выполнен `app.js`, в противном случае будет выполнен `bundle.js`.

## CORS and credentials

Unlike regular scripts, module scripts (and their imports) are fetched with CORS. This means cross-origin module scripts must return valid CORS headers such as `Access-Control-Allow-Origin: *`.

```markup
<!-- This will not execute, as it fails a CORS check -->
<script type="module" src="https://….now.sh/no-cors"></script>

<!-- This will not execute, as one of its imports fails a CORS check -->
<script type="module">
  import "https://….now.sh/no-cors";

  addTextToBody("This will not execute.");
</script>

<!-- This will execute as it passes CORS checks -->
<script type="module" src="https://….now.sh/cors"></script>
```

Most CORS-based APIs will send credentials (cookies etc) if the request is to the same origin, but `fetch()` and module scripts are exceptions – they don't send credentials unless you ask for them.

You can add credentials to a same-origin module by including the `crossorigin` attribute (which seems a bit weird to me, and I've questioned this in the spec). If you want to send credentials to other origins too, use `crossorigin="use-credentials"`. Note that the other origin will have to respond with the `Access-Control-Allow-Credentials: true` header.

```markup
<!-- Fetched with credentials (cookies etc) -->
<script src="1.js"></script>

<!-- Fetched without credentials -->
<script type="module" src="1.js"></script>

<!-- Fetched with credentials -->
<script type="module" crossorigin src="1.js?"></script>

<!-- Fetched without credentials -->
<script type="module" crossorigin src="https://other-origin/1.js"></script>

<!-- Fetched with credentials-->
<script
  type="module"
  crossorigin="use-credentials"
  src="https://other-origin/1.js?"
></script>
```

Also, there's a gotcha related to the "Modules only execute once" rule. Modules are keyed by their URL, so if you request a module without credentials, then request it with credentials, you'll get the same without-credentials module back. This is why I've used a ? in the URLs above, to make them unique.

## References

* [Jake Archibald Blog: ES6 modules in browsers](https://jakearchibald.com/2017/es-modules-in-browsers/)
* [Native ES6 modules: the new features and differences from Webpack modules](https://habrahabr.ru/company/tuturu/blog/329918/)
* [Native ES6 modules: `nomodule` attribute for the migration](https://hospodarets.com/native-ecmascript-modules-nomodule)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://strctr.gitbook.io/programming/01-languages/javascript/01-language/d-modules/05-browser-script-module.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
