W Node.js każdy plik jest traktowany jako osobny moduł, a że nie piszemy całej aplikacji w jednym pliku to musimy w jakiś sposób nimi zarządzać. Do tej pory w środowisku Node.js korzystaliśmy z modułów CommonJS ale to się może zmienić w przyszłości kiedy stabilne będą moduły ECMAScript.
Moduły CommonJS
Z tymi modułami zetknął się każdy kto chodź raz pisał jakiś skrypt w Node.js. Do zaimportowania zewnętrznego modułu wykorzystujemy składnię reqiure
. Natomiast aby udostępnić jakieś funkcje czy wartości dla innych modułów mamy module.exports
lub exports
const { database } = require('./database')
module.exports = {
connection: database.connect('...')
}
Zastanawialiście się kiedyś jak to działa? Skąd się bierze metoda require
i obiekt module
? Idea mechanizmu jest prosta w przeciwieństwie do implementacji (jeśli ktoś ma chwilę wolnego czasu to może zerknąć do repozytorium). Każdy moduł, który używamy w naszej aplikacji jest opakowywany w funkcję
(function (exports, require, module, __filename, __dirname) {
// zwartość naszego modułu
});
Nasz moduł wygląda wtedy następująco:
(function (exports, require, module, __filename, __dirname) {
const { database } = require('./database')
module.exports = {
connection: database.connect('...')
}
});
Dzięki temu, w taki trochę magiczny i ukryty sposób, możemy eksportować i importować moduły w CommonJS.
Moduły ECMAScript
To po co nam właściwie inny sposób radzenia sobie z modułami? Po co nam nowy mechanizm skoro stary się sprawował dobrze przez tyle lat? Ponieważ tak naprawdę to moduły ECMAScript są oficjalnym sposobem na radzenie sobie z modułami w JS’ie. Dzięki temu, że zostały wprowadzone to mamy to zunifikowane dla JS’a i możemy tworzyć biblioteki, które będą z tego mechanizmu korzystały w każdym miejscu gdzie możemy użyć języka JavaScript.
Jak teraz będzie wyglądało korzystanie z innych modułów? Mamy tutaj składnię, którą kojarzymy np.: z React’a czyli import
i export
import { database } from './database'
export const connection = database.connect('...')
Oczywiście nie możemy od razu z tego skorzystać gdyż jest to ciągle funkcjonalność eksperymentalna. Na szczęście nie musimy też jej włączać żadnymi flagami gdyż jest domyślnie włączona. Aby skorzystać z tej funkcjonalności mamy dwie drogi:
- W pliku
package.json
dołożyć wpis"type": "module"
- Użyć rozszerzenia
*.mjs
zamiast*.js
do plików
Top-level await
Jest jeszcze jedna zaleta korzystania z modułów ECMAScript. Od jakiegoś czasu w najnowszej wersji Node’a jest dla tych modułów uruchomiony top-level await. Dzięki temu nie musimy opakowywać naszego kodu w IIFE (Immediately-invoked function expression) tylko możemy skorzystać z await
bezpośrednio
await Promise.resolve(console.log('promise'));
console.log('after Promise');
W przypadku Node’a jest to o tyle wygodne, że wiele rzeczy dzieje się asynchronicznie a na które potrzebujemy czekać np.: nawiązanie połączenia z bazą danych żeby wystartować resztę aplikacji. Jednak aby włączyć tę funkcjonalność musimy ją jeszcze aktywować przy pomocy flagi
node --harmony-top-level-await nazwa_pliku.mjs
A wy co sądzicie o nowych modułach i top-level await? Korzystaliście już z tego czy czekacie aż będzie to rozwiązanie stabilne? Myślicie, że zmieni to sposób w jaki tworzymy aplikacje?