Articles

Setting up an ES6 Project Using Babel and webpack

W tym artykule przyjrzymy się tworzeniu konfiguracji kompilacji do obsługi nowoczesnego JavaScriptu (działającego w przeglądarkach internetowych) przy użyciu Babel i webpack.

Jest to potrzebne, aby zapewnić, że nasz nowoczesny kod JavaScriptu jest kompatybilny z szerszą gamą przeglądarek niż mógłby być.

JavaScript, jak większość technologii związanych z internetem, cały czas ewoluuje. W starych dobrych czasach mogliśmy wrzucić kilka znaczników <script> na stronę, być może dołączyć jQuery i kilka wtyczek, a następnie być dobrym do pracy.

Jednakże, od czasu wprowadzenia ES6, rzeczy stały się stopniowo bardziej skomplikowane. Wsparcie przeglądarek dla nowszych funkcji języka jest często fragmentaryczne, a ponieważ aplikacje JavaScript stają się bardziej ambitne, programiści zaczynają używać modułów do organizowania swojego kodu. To z kolei oznacza, że jeśli piszesz dziś nowoczesny JavaScript, będziesz musiał wprowadzić do swojego procesu krok budowania.

Jak widać z poniższych linków, konwersja z ES6 do ES5 dramatycznie zwiększa liczbę przeglądarek, które możemy obsługiwać.

  • Zgodność z ES6
  • Zgodność z ES5

Celem systemu budowania jest zautomatyzowanie przepływu pracy potrzebnego do przygotowania naszego kodu do przeglądarek i produkcji. Może to obejmować takie kroki jak transpilacja kodu do innego standardu, kompilacja Sass do CSS, łączenie plików, minifikacja i kompresja kodu, i wiele innych. Aby zapewnić, że są one konsekwentnie powtarzalne, potrzebny jest system kompilacji, który zainicjuje kroki w znanej sekwencji z jednego polecenia.

Wymagania wstępne

Aby móc podążać za tymi krokami, będziesz musiał mieć zainstalowane zarówno Node.js jak i npm (są one pakowane razem). Zalecam używanie menedżera wersji takiego jak nvm do zarządzania instalacją Node (oto jak), a jeśli chcesz pomocy w zapoznaniu się z npm, sprawdź przyjazny dla początkujących samouczek SitePointa dotyczący npm.

Set Up

Utwórz folder główny gdzieś na swoim komputerze i przejdź do niego z terminala/wiersza poleceń. Będzie to twój folder <ROOT>.

Utwórz plik package.json z tym:

npm init -y

Uwaga: Flaga -y tworzy plik z domyślnymi ustawieniami i oznacza, że nie musisz uzupełniać żadnych zwykłych szczegółów z wiersza poleceń. Można je później zmienić w edytorze kodu, jeśli chcesz.

Wewnątrz folderu <ROOT> utwórz katalogi src, src/js i public. Folder src/js będzie miejscem, gdzie umieścimy nasz nieprzetworzony kod źródłowy, a folder public będzie miejscem, gdzie wyląduje transpilowany kod.

Transpiling with Babel

Aby zacząć działać, zainstalujemy babel-cli, który zapewnia możliwość transpilowania ES6 do ES5, oraz babel-preset-env, który pozwala nam kierować transpilowany kod do konkretnych wersji przeglądarki.

npm install babel-cli babel-preset-env --save-dev

Powinieneś teraz zobaczyć następujące elementy w swoim pliku package.json:

"devDependencies": { "babel-cli": "^6.26.0", "babel-preset-env": "^1.6.1"}

Podczas gdy jesteśmy w pliku package.json, zmieńmy sekcję scripts, aby wyglądała tak:

"scripts": { "build": "babel src -d public"},

Daje nam to możliwość wywoływania Babel przez skrypt, zamiast za każdym razem bezpośrednio z terminala. Jeśli chciałbyś dowiedzieć się więcej o skryptach npm i ich możliwościach, sprawdź ten tutorial SitePoint.

Na koniec, zanim będziemy mogli przetestować, czy Babel robi swoje, musimy utworzyć .babelrc plik konfiguracyjny. To jest to, do czego nasz pakiet babel-preset-env będzie się odwoływał dla swoich parametrów transpilacji.

Utwórz nowy plik w swoim katalogu <ROOT> o nazwie .babelrc i wklej do niego następujące informacje:

{ "presets": } } ] ]}

To ustawi Babel do transpilacji dla dwóch ostatnich wersji każdej przeglądarki, plus Safari w wersji v7 lub wyższej. Inne opcje są dostępne w zależności od tego, które przeglądarki trzeba obsługiwać.

Po zapisaniu tego, możemy teraz przetestować rzeczy z przykładowym plikiem JavaScript, który używa ES6. Dla celów tego artykułu, zmodyfikowałem kopię leftpada, aby używać składni ES6 w kilku miejscach: literały szablonów, funkcje strzałek, const i let.

Zapisz to jako src/js/leftpad.js i w swoim terminalu wykonaj następujące polecenia:

npm run build

Jeśli wszystko jest zgodne z przeznaczeniem, w swoim folderze public powinieneś teraz znaleźć nowy plik o nazwie js/leftpad.js. Jeśli go otworzysz, zobaczysz, że nie zawiera on już żadnej składni ES6 i wygląda tak:

Organizing Your Code with ES6 Modules

Moduł ES6 jest plikiem JavaScript zawierającym funkcje, obiekty lub prymitywne wartości, które chcesz udostępnić innemu plikowi JavaScript. Ty export z jednego, a import do drugiego. Każdy poważny, nowoczesny projekt JavaScript powinien rozważyć użycie modułów. Pozwalają one na rozbicie kodu na samodzielne jednostki i w ten sposób ułatwiają utrzymanie; pomagają uniknąć zanieczyszczenia przestrzeni nazw; i pomagają uczynić kod bardziej przenośnym i wielokrotnego użytku.

Podczas gdy większość składni ES6 jest szeroko dostępna w nowoczesnych przeglądarkach, nie dotyczy to jeszcze modułów. W czasie pisania tego tekstu są one dostępne w Chrome, Safari (w tym w najnowszej wersji na iOS) i Edge; są ukryte za flagą w Firefoksie i Operze; i nie są dostępne (i prawdopodobnie nigdy nie będą) w IE11, ani w większości urządzeń mobilnych.

W następnej sekcji przyjrzymy się, jak możemy zintegrować moduły z naszą konfiguracją kompilacji.

Eksport

Słowo kluczowe export jest tym, co pozwala nam udostępnić nasze moduły ES6 innym plikom, i daje nam dwie opcje, aby to zrobić – nazwane i domyślne. Z nazwanym eksportem, możesz mieć wiele eksportów na moduł, a z domyślnym eksportem masz tylko jeden na moduł. Eksport nazwany jest szczególnie przydatny, gdy musisz wyeksportować kilka wartości. Na przykład, możesz mieć moduł zawierający wiele funkcji użytkowych, które muszą być dostępne w różnych miejscach w twoich aplikacjach.

Zamieńmy więc nasz plik leftPad w moduł, który możemy następnie wymagać w drugim pliku.

Nazwany eksport

Aby utworzyć nazwany eksport, dodaj następujące elementy do dolnej części pliku leftPad:

export { leftPad };

Możemy również usunąć deklarację "use strict"; z górnej części pliku, ponieważ moduły domyślnie działają w trybie ścisłym.

Defult Export

Jako że w pliku leftPad jest tylko jedna funkcja do wyeksportowania, może on być faktycznie dobrym kandydatem do użycia export default zamiast tego:

export default function leftPad(str, len, ch) { ...}

Ponownie, możesz usunąć deklarację "use strict"; z góry pliku.

Import

Aby skorzystać z wyeksportowanych modułów, musimy je teraz zaimportować do pliku (modułu), w którym chcemy ich użyć.

W przypadku opcji export default wyeksportowany moduł można zaimportować pod dowolnie wybraną nazwą. Na przykład, moduł leftPad może być zaimportowany tak:

import leftPad from './leftpad';

Albo może być zaimportowany pod inną nazwą, tak:

import pineapple_fritter from './leftpad';

Funkcjonalnie, oba będą działać dokładnie tak samo, ale oczywiście ma sens użycie albo tej samej nazwy, pod jaką został wyeksportowany, albo czegoś, co czyni import zrozumiałym – być może w przypadku, gdy wyeksportowana nazwa kolidowałaby z inną nazwą zmiennej, która już istnieje w module odbierającym.

W przypadku opcji eksportu nazwanego, musimy zaimportować moduł używając tej samej nazwy, pod jaką został wyeksportowany. Dla naszego przykładowego modułu, zaimportowalibyśmy go w podobny sposób, jak użyliśmy składni export default, ale w tym przypadku, musimy zawinąć importowaną nazwę w nawiasy klamrowe:

import { leftPad } from './leftpad';

Nawiasy są obowiązkowe w przypadku eksportu nazwanego, i zakończy się on niepowodzeniem, jeśli nie zostaną użyte.

Możliwa jest zmiana nazwy nazwanego eksportu podczas importu, jeśli zajdzie taka potrzeba, i aby to zrobić, musimy nieco zmodyfikować naszą składnię używając składni import as . Podobnie jak w przypadku export, istnieje wiele sposobów, aby to zrobić, z których wszystkie są szczegółowo opisane na stronie importu MDN.

import { leftPad as pineapple_fritter } from './leftpad_es6';

Powtarzam, zmiana nazwy jest trochę bezsensowna, ale ilustruje punkt, że można je zmienić na cokolwiek. Powinieneś trzymać się dobrych praktyk nazewnictwa przez cały czas, chyba że oczywiście piszesz procedury przygotowywania przepisów na bazie owoców.

Konsumowanie wyeksportowanego modułu

Aby wykorzystać wyeksportowany moduł leftPad, utworzyłem następujący plik index.js w folderze src/js. Tutaj, zapętlam tablicę numerów seryjnych i poprzedzam je zerami, aby utworzyć z nich ośmioznakowy łańcuch. Później wykorzystamy to i wyślemy je do elementu listy uporządkowanej na stronie HTML. Zauważ, że ten przykład używa domyślnej składni eksportu:

Tak jak wcześniej, uruchom skrypt budujący z katalogu <ROOT>:

npm run build

Babel utworzy teraz plik index.js w katalogu public/js. Podobnie jak w przypadku naszego pliku leftPad.js, powinieneś zauważyć, że Babel zastąpił całą składnię ES6 i pozostawił tylko składnię ES5. Możesz również zauważyć, że przekonwertował składnię modułu ES6 na opartą na Node module.exports, co oznacza, że możemy go uruchomić z linii poleceń:

node public/js/index.js// 

Twój terminal powinien teraz wylogować tablicę łańcuchów poprzedzonych zerami, aby wszystkie miały długość ośmiu znaków. Po wykonaniu tych czynności, czas przyjrzeć się webpack.

Wprowadzenie webpack i integracja z Babel

Jak wspomniano, moduły ES6 pozwalają programistom JavaScript na rozbicie ich kodu na łatwe do zarządzania kawałki, ale konsekwencją tego jest to, że te kawałki muszą być serwowane do żądającej przeglądarki, potencjalnie dodając dziesiątki dodatkowych żądań HTTP z powrotem do serwera – coś, czego naprawdę powinniśmy starać się uniknąć. W tym miejscu wkracza webpack.

webpack to modułowy bundler. Jego głównym celem jest przetwarzanie aplikacji poprzez śledzenie wszystkich jej zależności, a następnie pakowanie ich w jeden lub więcej pakietów, które mogą być uruchamiane w przeglądarce. Jednakże, może on być czymś znacznie więcej, w zależności od tego jak jest skonfigurowany.

Konfiguracjawebpacka opiera się na czterech kluczowych komponentach:

  • punkt wejścia
  • lokalizacja wyjściowa
  • loadery
  • pluginy

Wejście: To przechowuje punkt startowy twojej aplikacji, z którego webpack może zidentyfikować swoje zależności.

Output: Określa to, gdzie chciałbyś, aby przetworzony pakiet został zapisany.

Loadery: Są to sposoby konwersji jednej rzeczy jako wejścia i generowania czegoś innego jako wyjścia. Mogą być używane do rozszerzenia możliwości webpacka, aby obsługiwać więcej niż tylko pliki JavaScript, a zatem konwertować je również do poprawnych modułów.

Wtyczki: Są one używane do rozszerzenia możliwości webpacka o inne zadania poza bundlingiem – takie jak minifikacja, linting i optymalizacja.

Aby zainstalować webpack, wykonaj następujące czynności z katalogu <ROOT>:

npm install webpack webpack-cli --save-dev

To instaluje webpack lokalnie w projekcie, a także daje możliwość uruchamiania webpacka z linii poleceń poprzez dodanie webpack-cli. Powinieneś teraz zobaczyć webpack na liście w swoim pliku package.json. Będąc w tym pliku, zmodyfikuj sekcję scripts w następujący sposób, aby teraz wiedziała, że ma używać webpacka zamiast bezpośrednio Babela:

"scripts": { "build": "webpack --config webpack.config.js"},

Jak widzisz, ten skrypt wywołuje plik webpack.config.js, więc stwórzmy go w naszym katalogu <ROOT> z następującą zawartością:

To jest mniej więcej najprostszy plik konfiguracyjny, jakiego potrzebujesz z webpackiem. Możesz zobaczyć, że używa on sekcji wejściowej i wyjściowej opisanych wcześniej (mógłby funkcjonować tylko z nimi), ale także zawiera ustawienie mode: 'development'.

webpack ma możliwość używania trybu „development” lub „production”. Ustawienie mode: 'development' optymalizuje szybkość budowania i debugowania, podczas gdy mode: 'production' optymalizuje szybkość wykonywania w czasie działania i rozmiar pliku wyjściowego. Istnieje dobre wyjaśnienie trybów w artykule Tobiasa Koppersa „webpack 4: tryb i optymalizacja”, jeśli chcesz przeczytać więcej o tym, jak można je skonfigurować poza domyślnymi ustawieniami.

Następnie usuń wszystkie pliki z folderu public/js. Następnie ponownie uruchom to:

npm run build

Zobaczysz, że teraz zawiera on pojedynczy plik ./public/bundle.js. Otwórz jednak nowy plik, a dwa pliki, z którymi zaczynaliśmy, wyglądają zupełnie inaczej. To jest ta część pliku, która zawiera kod index.js. Mimo że jest on dość mocno zmodyfikowany w stosunku do naszego oryginału, nadal można wyłapać jego nazwy zmiennych:

Jeśli uruchomisz node public/js/bundle.js z folderu <ROOT>, zobaczysz, że otrzymasz takie same wyniki, jak poprzednio.

Transpiling

Jak już wspomnieliśmy, loadery pozwalają nam przekształcić jedną rzecz w coś innego. W tym przypadku chcemy, aby ES6 zostało przekonwertowane na ES5. Aby to zrobić, będziemy potrzebować kilku dodatkowych pakietów:

npm install babel-loader babel-core --save-dev

Aby je wykorzystać, webpack.config.js potrzebuje sekcji modułu dodanej do niego po sekcji wyjściowej, jak poniżej:

To używa instrukcji regex do identyfikacji plików JavaScript, które mają być transponowane z babel-loader, jednocześnie wykluczając wszystko w folderze node_modules z tego. Na koniec, babel-loader jest informowany o użyciu pakietu babel-preset-env zainstalowanego wcześniej, aby ustalić parametry transpilacji ustawione w pliku .babelrc.

Po wykonaniu tej czynności możesz ponownie uruchomić to:

npm run build

Potem sprawdź nowy public/js/bundle.js i zobaczysz, że wszystkie ślady składni ES6 zniknęły, ale nadal produkuje on takie same dane wyjściowe jak poprzednio.

Przeniesienie do przeglądarki

Po zbudowaniu działającego webpacka i Babela, nadszedł czas na przeniesienie tego, co zrobiliśmy, do przeglądarki. Potrzebny jest mały plik HTML, który należy utworzyć w folderze <ROOT>, jak poniżej:

Do wyświetlenia listy potrzeba trochę więcej JavaScriptu, więc zmieńmy ./src/js/index.js, aby tak się stało:

Teraz, jeśli otworzysz index.html w przeglądarce, powinieneś zobaczyć, że pojawia się uporządkowana lista, jak poniżej:

Taking it Further

Jak skonfigurowano powyżej, nasz system budowania jest całkiem gotowy do działania. Możemy teraz użyć webpacka, aby połączyć nasze moduły i przetransponować kod ES6 do ES5 za pomocą Babel.

Jednakże jest to trochę kłopotliwe, że aby przetransponować nasz kod ES6, musimy uruchomić npm run build za każdym razem, gdy dokonujemy zmiany.

Dodanie 'watch’

Aby pokonać potrzebę wielokrotnego uruchamiania npm run build, możesz ustawić 'watch' na swoich plikach i kazać webpackowi rekompilować się automatycznie za każdym razem, gdy zauważy zmianę w jednym z plików w folderze ./src. Aby to zrealizować, zmodyfikuj sekcję scripts pliku package.json, jak poniżej:

"scripts": { "watch": "webpack --watch", "build": "webpack --config webpack.config.js"},

Aby sprawdzić, czy to działa, uruchom npm run watch z terminala, a zobaczysz, że nie wraca już do wiersza poleceń. Teraz wróć do src/js/index.js i dodaj dodatkową wartość do tablicy serNos i zapisz ją. Mój teraz wygląda tak:

const serNos = ;

Jeśli teraz sprawdzisz terminal, zobaczysz, że jest wylogowany, i że ponownie uruchomił zadanie webpack build. A po powrocie do przeglądarki i odświeżeniu, zobaczysz nową wartość dodaną na koniec listy, po przetworzeniu jej za pomocą leftPad.

Odświeżaj przeglądarkę automatycznie

Byłoby naprawdę dobrze, gdybyśmy mogli sprawić, by webpack automatycznie odświeżał przeglądarkę za każdym razem, gdy dokonujemy zmiany. Zróbmy to poprzez zainstalowanie dodatkowego pakietu npm o nazwie webpack-dev-server. Nie zapomnijmy jednak najpierw Ctrl + c z zadania watch!

npm install webpack-dev-server --save-dev

Po wykonaniu tej czynności dodajmy nowy skrypt do pliku package.json, aby wywołać nowy pakiet. Sekcja scripts powinna teraz zawierać to:

Zauważ flagę --open-page dodaną na końcu skryptu. To mówi webpack-dev-server, aby otworzyć określoną stronę w domyślnej przeglądarce przy użyciu jej trybu iframe.

Teraz uruchom npm start i powinieneś zobaczyć nową kartę przeglądarki otwieraną pod adresem z wyświetlaną listą części. Aby pokazać, że 'watch' działa, przejdź do src/js/index.js i dodaj kolejną nową wartość do końca tablicy serNos. Kiedy zapiszesz swoje zmiany, powinieneś zauważyć ich odzwierciedlenie niemal natychmiast w przeglądarce.

Po zakończeniu tych czynności pozostaje już tylko ustawić tryb w webpack.config.js na production. Gdy to zostanie ustawione, webpack również zminifikuje kod, który wypisuje do ./public/js/bundle.js. Należy zauważyć, że jeśli mode nie jest ustawione, webpack będzie domyślnie używał konfiguracji production.

Zakończenie

W tym artykule, zobaczyłeś jak skonfigurować system budowania dla nowoczesnego JavaScriptu. Początkowo użyto Babel z linii poleceń, aby przekonwertować składnię ES6 na ES5. Następnie zobaczyliśmy, jak wykorzystać moduły ES6 ze słowami kluczowymi export i import, jak zintegrować webpack, aby wykonać zadanie wiązania, oraz jak dodać zadanie obserwacyjne, aby zautomatyzować uruchamianie webpacka za każdym razem, gdy wykryte zostaną zmiany w pliku źródłowym. W końcu zobaczyłeś, jak zainstalować webpack-dev-server, aby odświeżyć stronę automatycznie za każdym razem, gdy dokonywana jest zmiana.

Jeśli chcesz pójść dalej, sugerowałbym przeczytanie SitePoint’s deep dive into webpack and module bundling, jak również zbadanie dodatkowych loaderów i wtyczek, które pozwolą webpack do obsługi Sass i zadań kompresji aktywów. Spójrz również na eslint-loader i wtyczkę dla Prettier too.

Happy bundling …

.