W projekcie, nad którym współpracuję, mamy dwie możliwości wyboru systemu, z którego możemy korzystać:
- Importowanie modułów za pomocą
require
i eksportowanie za pomocąmodule.exports
iexports.foo
. - Importowanie modułów za pomocą ES6
import
i eksportowanie za pomocą ES6export
Czy są jakieś korzyści związane z wydajnością wynikające z używania jednego nad drugim? Czy jest coś jeszcze, co powinniśmy wiedzieć, gdybyśmy mieli używać modułów ES6 zamiast Node?
javascript
node.js
ecmascript-6
babeljs
kpimov
źródło
źródło
node --experimental-modules index.mjs
pozwala używaćimport
bez Babel i działa w węźle 8.5.0+. Możesz (i powinieneś) również publikować swoje pakiety npm jako natywny ESModule , ze starą kompatybilnością wstecznąrequire
.Odpowiedzi:
Pamiętaj, że nie ma jeszcze silnika JavaScript, który natywnie obsługuje moduły ES6. Sam powiedziałeś, że używasz Babel. Babel
import
i tak konwertuje iexport
deklaruje domyślnie na CommonJS (require
/module.exports
). Więc nawet jeśli używasz składni modułu ES6, będziesz używać CommonJS pod maską, jeśli uruchomisz kod w węźle.Istnieją techniczne różnice między modułami CommonJS i ES6, np. CommonJS pozwala na dynamiczne ładowanie modułów. ES6 na to nie pozwala, ale w tym celu opracowano interfejs API .
Ponieważ moduły ES6 są częścią standardu, skorzystałbym z nich.
źródło
ES6 import
zrequire
ale pracował inaczej. CommonJS eksportuje samą klasę, podczas gdy istnieje tylko jedna klasa. ES6 eksportuje, tak jak istnieje wiele klas, więc musisz użyć,.ClassName
aby uzyskać eksportowaną klasę. Czy są jakieś inne różnice, które faktycznie wpływają na wdrożeniemodule.exports = ...;
jest równoważne zexport default ...
.exports.foo = ...
jest równoważne zexport var foo = ...
;import
do CommonJS w węźle, używanego wraz z pakietem Webpack 2 / Rollup (i dowolnym innym programem pakującym, który pozwala na wstrząsanie drzewem ES6), możliwe jest, aby skończyć z plikiem, który jest znacznie mniejszy niż równoważny kod Node chrupie poprzez zastosowanierequire
dokładnie ze względu na fakt, że ES6 umożliwia statyczną analizę importu / eksportu. Chociaż nie ma to znaczenia dla Node (jeszcze), z pewnością może, jeśli kod ostatecznie skończy jako pojedynczy pakiet przeglądarki.Istnieje kilka zastosowań / możliwości, które warto rozważyć:
Wymagać:
require
s, są one ładowane i przetwarzane jeden po drugim.Import ES6:
Ponadto system modułowy Wymagaj nie jest oparty na standardzie. Jest mało prawdopodobne, aby stał się standardem, skoro istnieją moduły ES6. W przyszłości będzie dostępna natywna obsługa modułów ES6 w różnych implementacjach, co będzie korzystne pod względem wydajności.
źródło
require
, więc i tak używasz systemu modułu i modułu ładującego.Główne zalety są składniowe:
Z modułami ES6 raczej nie zauważysz żadnych korzyści w zakresie wydajności. Nadal będziesz potrzebować dodatkowej biblioteki, aby spakować moduły, nawet jeśli przeglądarka ma pełne wsparcie dla funkcji ES6.
źródło
node --experimemntal-modules index.mjs
pozwala używaćimport
bez Babel. Możesz (i powinieneś) również publikować swoje pakiety npm jako natywny ESModule, ze starą kompatybilnością wstecznąrequire
. Wiele przeglądarek obsługuje również import dynamiczny .Obecna odpowiedź brzmi „nie”, ponieważ żaden z obecnych silników przeglądarek nie implementuje
import/export
standardu ES6.Niektóre tabele porównawcze http://kangax.github.io/compat-table/es6/ nie biorą tego pod uwagę, więc kiedy widzisz prawie wszystkie zielenie dla Chrome, bądź ostrożny.
import
słowo kluczowe z ES6 nie zostało uwzględnione.Innymi słowy, obecne silniki przeglądarek, w tym V8, nie mogą importować nowego pliku JavaScript z głównego pliku JavaScript za pomocą żadnej dyrektywy JavaScript.
(Być może będziemy jeszcze za kilka błędów lub za kilka lat, dopóki V8 nie wdroży tego zgodnie ze specyfikacją ES6).
Tego dokumentu potrzebujemy, a tego dokumentu musimy przestrzegać.
A standard ES6 mówi, że zależności modułu powinny istnieć, zanim przeczytamy moduł, tak jak w języku programowania C, w którym mieliśmy
.h
pliki (nagłówki) .Jest to dobra i sprawdzona struktura, i jestem pewien, że eksperci, którzy stworzyli standard ES6, mieli to na uwadze.
Dzięki temu Webpack lub inne programy pakujące pakiety mogą zoptymalizować pakiet w niektórych szczególnych przypadkach i zmniejszyć niektóre zależności od pakietu, które nie są potrzebne. Ale w przypadkach, w których mamy doskonałe zależności, nigdy się to nie wydarzy.
Potrzebne będzie trochę czasu, zanim
import/export
natywna obsługa zostanie uruchomiona, arequire
słowo kluczowe nigdzie nie zniknie.Co to jest
require
?Jest to
node.js
sposób na ładowanie modułów. ( https://github.com/nodejs/node )Węzeł używa metod na poziomie systemu do odczytu plików. Zasadniczo polegasz na tym podczas korzystania
require
.require
zakończy się wywołaniem systemowym, takim jakuv_fs_open
(zależy od systemu końcowego, Linux, Mac, Windows), aby załadować plik / moduł JavaScript.Aby sprawdzić, czy to prawda, spróbuj użyć Babel.js, a zobaczysz, że
import
słowo kluczowe zostanie przekonwertowane narequire
.źródło
import
w procesie kompilacji pakietu Webpack 2 / Rollup może potencjalnie zmniejszyć rozmiar wynikowego pliku przez „wstrząsanie drzewem” nieużywanych modułów / kodu, które w przeciwnym razie mogłyby skończyć w ostatecznym pakiecie. Mniejszy rozmiar pliku = szybsze pobieranie = szybsze uruchamianie / uruchamianie na kliencie.import
słowo kluczowe natywnie. Lub oznacza to, że nie można zaimportować innego pliku JavaScript z pliku JavaScript. Dlatego nie można porównywać korzyści wydajnościowych tych dwóch. Oczywiście narzędzia takie jak Webpack1 / 2 lub Browserify radzą sobie z kompresją. Są od jednegoimport
iexport
są statyczne deklaracje, które importują ścieżkę kodu specyficzny, natomiastrequire
może być dynamiczny, a więc pakiet w kodzie, który nie jest używany. Korzyść z wydajności jest pośrednia - Webpack 2 i / lub pakiet zbiorczy mogą potencjalnie skutkować mniejszymi rozmiarami pakietów, które są szybsze do pobrania, a zatem wydają się być szybsze dla użytkownika końcowego (przeglądarki). Działa to tylko wtedy, gdy cały kod jest zapisany w modułach ES6, dlatego import może być analizowany statycznie.import/export
jest konwertowane narequire
, przyznane. Ale to, co dzieje się przed tym krokiem, można uznać za zwiększenie wydajności. Przykład: Jeślilodash
jest napisane w ES6, a tyimport { omit } from lodash
, ostateczny pakiet będzie zawierał TYLKO „pomiń”, a nie inne narzędzia, podczas gdy prostyrequire('lodash')
zaimportuje wszystko. Zwiększy to rozmiar pakietu, pobieranie potrwa dłużej, a tym samym zmniejszy wydajność. Jest to oczywiście ważne tylko w kontekście przeglądarki.Korzystanie z modułów ES6 może być przydatne do „wstrząsania drzewem”; tzn. umożliwiając Webpack 2, pakietowi zbiorczemu (lub innym programom pakującym) identyfikowanie ścieżek kodu, które nie są używane / importowane, a zatem nie wchodzą w wynikowy pakiet. Może to znacznie zmniejszyć rozmiar pliku poprzez wyeliminowanie kodu, którego nigdy nie potrzebujesz, ale z CommonJS jest domyślnie dołączany do pakietu, ponieważ Webpack i inni nie mają możliwości dowiedzenia się, czy jest potrzebny.
Odbywa się to za pomocą analizy statycznej ścieżki kodu.
Na przykład za pomocą:
... daje programowi pakującemu wskazówkę, która
package.anotherPart
nie jest wymagana (jeśli nie zostanie zaimportowana, nie będzie można jej użyć - prawda?), więc nie będzie kłopotać się jej pakowaniem.Aby włączyć to dla Webpack 2, musisz upewnić się, że twój transpiler nie wyrzuca modułów CommonJS. Jeśli używasz
es2015
wtyczki z Babel, możesz ją wyłączyć w następujący.babelrc
sposób:Pakiet zbiorczy i inne mogą działać inaczej - wyświetl dokumenty, jeśli jesteś zainteresowany.
źródło
Jeśli chodzi o asynchroniczne lub leniwe ładowanie, to
import ()
jest o wiele bardziej wydajny. Zobacz, kiedy wymagamy komponentu w sposób asynchroniczny, a następnie używamyimport
go w sposób asynchroniczny, jak wconst
zmiennej zmiennejawait
.Lub jeśli chcesz użyć
require()
,Rzecz jest w
import()
rzeczywistości asynchroniczna. Jak wspomniał neehar venugopal w ReactConf , możesz go użyć do dynamicznego ładowania komponentów reagujących na architekturę po stronie klienta.Jest także o wiele lepszy, jeśli chodzi o routing. Jest to jedna szczególna rzecz, która powoduje, że dziennik sieciowy pobiera niezbędną część, gdy użytkownik łączy się z określoną witryną internetową do określonego komponentu. np. strona logowania przed pulpitem nawigacyjnym nie pobierałaby wszystkich składników deski rozdzielczej. Ponieważ to, co jest potrzebne, tj. Komponent logowania, zostanie tylko pobrany.
To samo dotyczy
export
: ES6export
są dokładnie takie same jak dla CommonJSmodule.exports
.UWAGA - Jeśli opracowujesz projekt node.js, musisz ściśle używać,
require()
ponieważ węzeł zgłosi błąd wyjątku, takinvalid token 'import'
jakbyś go użyłimport
. Zatem węzeł nie obsługuje instrukcji importu.AKTUALIZACJA - zgodnie z sugestią Dana Dascalescu : Od wersji 8.5.0 (wydanej we wrześniu 2017 r.)
node --experimental-modules index.mjs
Pozwala używaćimport
bez Babel. Możesz (i powinieneś) również publikować swoje pakiety npm jako natywny ESModule, ze starą kompatybilnością wstecznąrequire
.Zobacz to, aby uzyskać więcej informacji o tym, gdzie używać importów asynchronicznych - https://www.youtube.com/watch?v=bb6RCrDaxhw
źródło
Najważniejsze, aby wiedzieć, że moduły ES6 są rzeczywiście oficjalnym standardem, podczas gdy moduły CommonJS (Node.js) nie są.
W 2019 r. Moduły ES6 są obsługiwane przez 84% przeglądarek. Podczas gdy Node.js umieszcza je za flagą --experimental-modules , istnieje również wygodny pakiet węzłów o nazwie esm , który sprawia, że integracja jest płynna.
Innym problemem, na który prawdopodobnie natkniesz się między tymi systemami modułów, jest lokalizacja kodu. Node.js zakłada, że źródło jest przechowywane w
node_modules
katalogu, podczas gdy większość modułów ES6 jest rozmieszczonych w płaskiej strukturze katalogów. Nie jest to łatwe do pogodzenia, ale można to zrobić poprzez zhakowaniepackage.json
pliku za pomocą skryptów przed i po instalacji. Oto przykładowy moduł izomorficzny i artykuł wyjaśniający, jak to działa.źródło
Ja osobiście używam importu, ponieważ możemy zaimportować wymagane metody członków za pomocą importu.
Nazwa pliku: dep.js
Podziękowania należą się Paulowi Shanowi. Więcej informacji .
źródło
require
?const {a,b} = require('module.js');
działa również ... jeśli eksportujesza
ib
module.exports = { a: ()={}, b: 22 }
- Druga część @BananaAcid odpowiadaW chwili obecnej importowanie ES6 eksport jest zawsze kompilowany do CommonJS , więc nie ma żadnych korzyści z używania jednego lub drugiego. Chociaż zalecane jest użycie ES6, ponieważ powinno być korzystne, gdy natywna obsługa przeglądarek zostanie wydana. Powodem jest to, że możesz importować częściowe z jednego pliku, podczas gdy w CommonJS musisz wymagać całego pliku.
ES6 →
import, export default, export
CommonJS →
require, module.exports, exports.foo
Poniżej znajduje się ich powszechne użycie.
Domyślny eksport ES6
ES6 eksportuje wiele i importuje wiele
CommonJS module.exports
Moduł CommonJS. Eksportuje wiele
źródło
Nie jestem pewien, dlaczego (prawdopodobnie optymalizacja - opóźnione ładowanie?) Działa w ten sposób, ale zauważyłem, że
import
nie można przeanalizować kodu, jeśli nie są używane importowane moduły.Czego nie można oczekiwać w niektórych przypadkach.
Weźmy znienawidzoną klasę Foo jako naszą przykładową zależność.
foo.ts
Na przykład:
index.ts
index.ts
index.ts
Z drugiej strony:
index.ts
źródło