Jak korzystać z natywnych modułów ES
Published: 2022-01-09 12:52:13

W niniejszym artykule prezentuję przykłady modułów ECMAScript (ES) – co można z nimi zrobić i jakie są ich ograniczenia. Moduły ES są obsługiwane przez wszystkie przeglądarki wydane po maju 2018 roku, więc możesz założyć, że można z nich bezpiecznie korzystać w większości przypadków.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r326rfj7bsch131k3dk0.png)

[źródło](https://caniuse.com/es6-module)

## Kodowanie bez modułów ES

Przed powstaniem modułów ES wszystkie JS trzeba było importować globalnie. Każdy plik miał dostęp do wcześniej zdefiniowanych zmiennych i mógł zostawić informację dla kodu wykonywanego później. Kolejność importowania miała znaczenie, zwłaszcza że elementy zaimportowane później mogły zmienić te, które zostały zaimportowane wcześniej. Starsze importy wyglądały mniej więcej tak:

`display-data.js`:
```JS
document.body.innerHTML = "lorem ipsum";
```

`log.js`:
```JS
console.log("Some test info");
```

`index.html`:
```HTML
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>No modules</title>
    <link rel="shortcut icon" href="#" />
  </head>

  <body>
    <script src="./display-data.js"></script>
    <script src="./log.js"></script>
  </body>
</html>
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9y44dtszs3ih3k2qxc17.png)

[Konkretny przykład w akcji](https://how-to-js.github.io/es-modules/no-modules/)


### Problemy

Podejście to jest problematyczne głównie z dwóch powodów:

I. Zanieczyszcza zakresu globalnego przez kod. Jeśli kilka plików określa tę samą wartość, będą one ze sobą kolidować i będą się na siebie wzajemnie nadpisywać: powodzenia w szukaniu i naprawianiu powstałych w związku z tym bugów. Przykład:

`data-1.js`:
```JS
var data = “lorem ipsum”;
```

`data-2.js`:
```JS
var data = “sin dolor”;
```

`index.html`:
```HTML
  <html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Name collision</title>
    <link rel="shortcut icon" href="#" />
  </head>

  <body>
    <script src="./data-1.js"></script>
    <script src="./data-2.js"></script>
    <script>
      document.body.innerHTML = data;
    </script>
  </body>
</html>
```

[Przykład](https://how-to-js.github.io/es-modules/name-collision/)

Obchodzenie tego problemu polegało najczęściej na wykorzystywaniu natychmiastowo wywoływanego wyrażenia funkcyjnego. Pozwalało to na izolację bloków kodu i chroniło przed zanieczyszczeniem zakresu globalnego, ale jednocześnie sprawiało, że kod stawał się bardziej zagmatwany.

II. Wszelkimi zależnościami trzeba zarządzać ręcznie. Jeśli jeden plik zależy od drugiego, pliki te trzeba zaimportować w odpowiedniej kolejności. Na przykład:

`log-data.js`:
```JS
console.log(data);
```

`data.js`:
```JS
const data = ‘some data’;
```

`display-data.js`:
```JS
document.html = data;
```

`index.html`:
```HTML
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>File order</title>
    <link rel="shortcut icon" href="#" />
  </head>

  <body>
    <script src="./log-data.js"></script>
    <script src="./data.js"></script>
    <script src="./display-data.js"></script>
  </body>
</html>
```

Jak widać [tutaj](https://how-to-js.github.io/es-modules/file-order/), część wyświetlająca dane działa, jak należy, a część z logowaniem danych – nie.

## Moduły ES w akcji

Co się zmieni, gdy to samo zrobimy z modułami ES? Przede wszystkim zależności definiowane są na poziomie kodu. Jeśli więc chcesz, żeby plik wykorzystywał wartości z innego pliku, definiujesz to bezpośrednio w tym pliku. Podejście to wiele zmienia, zwłaszcza w zakresie czytania kodu: aby poznać cały kontekst stosowany przez konkretny plik, wystarczy go otworzyć i przeczytać.

Jak zatem stosować moduły ES?

`data.js`:
```JS
export const data = "lorem ipsum";
```

`display-data.js`:
```JS
import { data } from "./data.js";

document.body.innerHTML = data;
```

`index.html`:
```HTML
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Simple modules</title>
    <link rel="shortcut icon" href="#" />
  </head>

  <body>
    <script type="module" src="./display-data.js"></script>
  </body>
</html>
```

Główne zmiany wprowadzone w kodzie:
1. dodanie `type=”module”` do importu `<script>` w pliku HTML;
2. zastosowanie eksportowych i importowych słów kluczowych w plikach JS w celu zdefiniowania i załadowania modułów.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lstp3t98qvygroyeh56o.png)

[Działający przykład](https://how-to-js.github.io/es-modules/simple-modules/)

## Import tego samego pliku przez kilka innych plików

Opisywany przykład można urozmaicić poprzez zaimportowanie tych samych plików dwukrotnie. Jako że każdy z plików musi być niezależny od tego drugiego, import zostanie dodany dwa razy – osobno dla każdego pliku. Przeglądarki zarządzają importem prawidłowo i ładują dany plik tylko raz.

`data.js`:
```JS
export const data = "lorem ipsum";
```

`display-data.js`:
```JS
import { data } from "./data.js";

document.body.innerHTML = data;
```

`log-data.js`:
```JS
import { data } from "./data.js";

console.log(data);
```

`index.html`:
```HTML
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Shared import</title>
    <link rel="shortcut icon" href="#" />
  </head>

  <body>
    <script type="module" src="./display-data.js"></script>
    <script type="module" src="./log-data.js"></script>
  </body>
</html>
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ed7jwg2oebzdxesvpsfa.png)

[Przykład](https://how-to-js.github.io/es-modules/shared-import/)

## Leniwe ładowanie aka lazy load

Lazy load opóźnia ładowanie aplikacji do chwili, w której kod zaczyna być potrzebny. Ta technika optymalizacji jest bardziej skomplikowana niż ładowanie wszystkiego od razu, ale pozwala na większą kontrolę nad tym, co i kiedy się ładuje. W poniższym przykładzie ładuję i wyświetlam dane z około półsekundowym opóźnieniem:

`display-data.js`:
```JS
setTimeout(
  () =>
    import("./data.js").then(({ data }) => {
      document.body.innerHTML = data;
    }),
  500
);
```

`data.js`:
```JSON
export const data = "lorem ipsum";
```

`index.html`:
```HTML
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Lazy load</title>
    <link rel="shortcut icon" href="#" />
  </head>

  <body>
    <script type="module" src="./display-data.js"></script>
  </body>
</html>
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/acdi10zerepftb9gmqgl.png)

[Przykład lazy load] (https://how-to-js.github.io/es-modules/lazy-load/)

## Czy moduł ES obejmuje wszystko, czego potrzeba w nowoczesnym JS?

Chociaż natywne moduły ES znacznie poprawiają wcześniejsze modele wprowadzania nowych elementów, brakuje im kilku cech kluczowych w nowoczesnym procesie programowania w JavaScript. Aktualnie nie można wykonać następujących działań:

1. Importu typów innych niż JS. Obecnie przygotowywane są inne pliki [JSON] (https://tc39.es/proposal-json-modules/), ale minie jeszcze trochę czasu, zanim będą one obsługiwane przez przeglądarki.
2. Importu zewnętrznych bibliotek tak jak w Node.js. Można by skopiować pliki podczas kompilacji i zaimportować je z lokalizacji w `node_modules`, ale proces ten wydaje się dużo bardziej skomplikowany niż zwyczajne `import “library”`.
3. Transpilacji. Wiele nowoczesnych kodów JS pisze się w różnych językach – na przykład w TypeScript. Nawet czysty JS wymaga transpilacji do obsługi starszych przeglądarek lub wykorzystywania najnowszych funkcji języków.

Z tych względów większość projektów wykorzystuje narzędzia typu JS bundler, czyli swego rodzaju kompilatorów, które przygotowują build na poszczególne wdrożenia. Jeśli zaciekawił Cię temat bundlerów, daj znać w komentarzu i sprawdź poniższe linki.

## Linki

* [Repozytorium przykładów] (https://github.com/how-to-js/es-modules),
* [wszystkie przykłady] (https://how-to-js.github.io/es-modules/),
* [mój wideokurs na temat esbuild] (https://bit.ly/esbuild-course),
* [mój wideokurs na temat webpack] (https://bit.ly/esbuild-course.).

## Podsumowanie

W niniejszym artykule omówiłem istotne przypadki użycia modułów ES. Kolejnym krokiem będzie skonfigurowanie JS bundlera tak, aby obejść ograniczenia modułów natywnych.