Jak korzystać z natywnych modułów ES
Marcin Wosinek
Posted on January 9, 2022
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.
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
:
document.body.innerHTML = "lorem ipsum";
log.js
:
console.log("Some test info");
index.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>
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
:
var data = “lorem ipsum”;
data-2.js
:
var data = “sin dolor”;
index.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>
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
:
console.log(data);
data.js
:
const data = ‘some data’;
display-data.js
:
document.html = data;
index.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, 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
:
export const data = "lorem ipsum";
display-data.js
:
import { data } from "./data.js";
document.body.innerHTML = data;
index.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:
- dodanie
type=”module”
do importu<script>
w pliku HTML; - zastosowanie eksportowych i importowych słów kluczowych w plikach JS w celu zdefiniowania i załadowania modułów.
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
:
export const data = "lorem ipsum";
display-data.js
:
import { data } from "./data.js";
document.body.innerHTML = data;
log-data.js
:
import { data } from "./data.js";
console.log(data);
index.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>
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
:
setTimeout(
() =>
import("./data.js").then(({ data }) => {
document.body.innerHTML = data;
}),
500
);
data.js
:
export const data = "lorem ipsum";
index.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>
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ń:
- Importu typów innych niż JS. Obecnie przygotowywane są inne pliki JSON, ale minie jeszcze trochę czasu, zanim będą one obsługiwane przez przeglądarki.
- 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ż zwyczajneimport “library”
. - 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,
- wszystkie przykłady,
- mój wideokurs na temat esbuild,
- mój wideokurs na temat webpack.
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.
Posted on January 9, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024