javascript - Korištenje Node.js require nasuprot ES6 import/export
U projektu na kojem surađujem imamo dvije opcije kojeg modulski sustav možemo koristiti:
- Uvoz modula koristeći
require, i izvoz koristećimodule.exportsiexports.foo. - Uvoz modula koristeći ES6
import, i izvoz koristeći ES6export
Postoje li ikakve prednosti u izvedbi pri korištenju jednog naspram drugog? Postoji li još nešto što bismo trebali znati ako bismo koristili ES6 module umjesto Node.js modulima?
Odgovor #1
Ažuriraj
Od Node.js v12 (travanj 2019.), podrška za ES module je omogućena prema zadanim postavkama, a od Node.js v15 (listopad 2020.) je stabilna (vidi ovdje). Datoteke, uključujući Node.js module, moraju završavati s .mjs ili najbliža package.json datoteka mora sadržavati "type": "module". Dokumentacija Node.js-a sadrži puno više informacija, također o interoperabilnosti između CommonJS i ES modula.
Što se tiče performansi, uvijek postoji mogućnost da novije značajke nisu tako dobro optimizirane kao postojeće značajke. Međutim, budući da se datoteke modula evaluiraju samo jednom, aspekt performansi se vjerojatno može zanemariti. Na kraju, svejedno morate pokrenuti testove izvedbe kako biste dobili konačan odgovor.
ES module se mogu dinamički učitati putem funkcije import(). Za razliku od require, ovo vraća obećanje.
Prethodni odgovor
Postoje li ikakve prednosti u performansama pri korištenju jednog u odnosu na drugi?
Imajte na umu da još ne postoji JavaScript motor koji nativno podržava ES6 module. Sam ste rekli da koristite Babel. Babel u svakom slučaju po defaultu pretvara import i export deklaracije u CommonJS (require/module.exports). Tako da čak i ako koristite sintaksu ES6 modula, u pozadini ćete koristiti CommonJS ako pokrenete kod u Node.js.
Postoje tehničke razlike između CommonJS i ES6 modula, npr. CommonJS vam dopušta dinamičko učitavanje modula. ES6 to ne dopušta, ali postoji API u razvoju za to.
Budući da su ES6 moduli dio standarda, koristio bih ih.
Odgovor #2
Postoji nekoliko načina uporabe/mogućnosti koje biste možda željeli razmotriti:
Zahtijevati :
- Možete imati dinamičko učitavanje gdje ime učitanog modula nije unaprijed definirano /static, ili gdje uvjetno učitavate modul samo ako je 'zaista potreban' (ovisno o određenom tijeku koda).
- Učitavanje je sinkrono. To znači da ako imate više
requireizraza, oni se učitavaju i obrađuju jedan po jedan.
ES6 Uvozi:
- Možete koristiti imenovane uvoze kako biste selektivno učitali samo dijelove koji su vam potrebni. To može uštedjeti memoriju.
- Uvoz može biti asinhron (i u trenutnom ES6 Module Loaderu, on to zapravo jest) i može raditi malo brže.
Također, sustav modula Require nije standardiziran. Vrlo je malo vjerojatno da će postati standard sada kada postoje ES6 moduli. U budućnosti će postojati izvorna podrška za ES6 module u različitim implementacijama, što će biti korisno u pogledu performansi.
Odgovor #3
Trenutno se ES6 import i export uvijek prevodi u CommonJS, tako da nema koristi od korištenja jednog ili drugog. Iako se preporučuje korištenje ES6 jer bi trebalo biti korisno kada bude dostupna izvorna podrška preglednika. Razlog je taj što možete uvesti dijelove iz jedne datoteke, dok kod CommonJS morate zahtijevati cijelu datoteku.
ES6 → import, export default, export
CommonJS → require, module.exports, exports.foo
Ispod je uobičajena uporaba tih.
ES6 izvoz zadano
// hello.js
function hello() {
return 'hello'
}
export default hello
// app.js
import hello from './hello'
hello() // returns hello
ES6 izvoz više i uvoz više
// hello.js
function hello1() {
return 'hello1'
}
function hello2() {
return 'hello2'
}
export { hello1, hello2 }
// app.js
import { hello1, hello2 } from './hello'
hello1() // returns hello1
hello2() // returns hello2
CommonJS module.exports
// hello.js
function hello() {
return 'hello'
}
module.exports = hello
// app.js
const hello = require('./hello')
hello() // returns hello
CommonJS module.exports više
// hello.js
function hello1() {
return 'hello1'
}
function hello2() {
return 'hello2'
}
module.exports = {
hello1,
hello2
}
// app.js
const hello = require('./hello')
hello.hello1() // returns hello1
hello.hello2() // returns hello2
Odgovor #4
Glavne prednosti su sintaktičke:
- Više deklarativna/kompaktna sintaksa
- ES6 moduli će u osnovi učiniti UMD (Universal Module Definition) zastarjelim - u biti uklanja podjelu između CommonJS i AMD (server protiv preglednika).
Vjerojatno nećete primijetiti nikakve koristi u performansama s ES6 modulima. I dalje će vam trebati dodatna biblioteka za spajanje modula, čak i kada preglednik u potpunosti podržava ES6 značajke.
Odgovor #5
Postoje li ikakve prednosti u performansama pri korištenju jednog u odnosu na drugi?
Trenutni odgovor je ne, jer nijedan od trenutnih pregledničkih enginea ne implementira import/export iz ES6 standarda.
Neki usporedni grafikoni http://kangax.github.io/compat-table/es6/ ne uzimaju ovo u obzir, pa kad vidite gotovo sve zelene za Chrome, budite oprezni. Ključna riječ import iz ES6 nije uzeta u obzir.
Drugim riječima, trenutni preglednički motori uključujući V8 ne mogu uvesti novu JavaScript datoteku iz glavne JavaScript datoteke putem bilo koje JavaScript direktive.
(Možda smo još uvijek samo nekoliko bugova daleko ili nekoliko godina daleko dok V8 to ne implementira prema ES6 specifikaciji.)
Ovaj dokument je ono što trebamo, i ovaj dokument je ono što moramo poštovati.
I standard ES6 rekao je da ovisnosti modula trebaju biti prisutne prije nego što pročitajmo modul, poput programskog jezika C, gdje smo imali (zaglavlja) .h datoteke.
Ovo je dobra i dobro testirana struktura, i siguran sam da su stručnjaci koji su stvorili ES6 standard to imali na umu.
Ovo omogućuje Webpacku ili drugim alatima za pakiranje paketa da optimiziraju paket u nekim posebnim slučajevima i smanje neke ovisnosti iz paketa koje nisu potrebne. Ali u slučajevima kada imamo savršene ovisnosti, to se nikada neće dogoditi.
Trebat će neko vrijeme dok import/export izvorna podrška ne postane dostupna, a ključna riječ require neće nigdje nestati još dugo vremena.
Što je require?
Ovo je node.js način za učitavanje modula. ( https://github.com/nodejs/node )
Node koristi metode na razini sustava za čitanje datoteka. U suštini se oslanjate na to kada koristite require. require će završiti u nekom sustavnom pozivu poput uv_fs_open (ovisi o krajnjem sustavu, Linux, Mac, Windows) kako bi se učitala JavaScript datoteka/modul.
Da biste provjerili da je ovo istina, pokušajte koristiti Babel.js, i vidjet ćete da će se ključna riječ import pretvoriti u require.
Odgovor #6
Korištenje ES6 modula može biti korisno za 'tree shaking'; tj. omogućavanje Webpack 2, Rollup (ili drugih bundlera) da prepoznaju dijelove koda koji nisu korišteni/uvođeni, i stoga ne ulaze u rezultatni bundle. To može znatno smanjiti veličinu datoteke eliminiranjem koda koji vam nikada neće trebati, dok se kod CommonJS po defaultu bundla jer Webpack i slični nemaju način da znaju je li potreban.
Ovo se radi korištenjem statičke analize puta koda.
Na primjer, koristeći:
import { somePart } 'of/a/package';
... daje bundleru naznaku da package.anotherPart nije potreban (ako nije importiran, ne može se koristiti - zar ne?), pa ga se neće truditi bundlati.
Da biste ovo omogućili za Webpack 2, trebate osigurati da vaš transpajler ne generira CommonJS module. Ako koristite es2015 dodatak s bableom, možete ga onemogućiti u svom .babelrc na sljedeći način:
{
"presets": [
["es2015", { modules: false }],
]
}
Rollup i drugi mogu raditi drugačije - pogledajte dokumentaciju ako ste zainteresirani.
Odgovor #7
Kada je riječ o asinhronom ili možda lijenom učitavanju, tada je import() puno moćniji. Vidite, kada zahtijevamo komponentu na asinhroni način, tada je koristimo s import na neki asinhroni način, kao u const varijabli koristeći await.
const module = await import('./module.js');
Ili ako želite koristiti require(), onda,
const converter = require('./converter');
Stvar je u tome da je import() zapravo asinkron po svojoj prirodi. Kao što je spomenuo neehar venugopal na ReactConfu, možete ga koristiti za dinamičko učitavanje React komponenti za arhitekturu na strani klijenta.
Također je mnogo bolje kada je riječ o usmjeravanju (Routing). To je ta posebna stvar koja omogućuje mrežnom dnevniku da preuzme nužan dio kada se korisnik poveže na određenu web stranicu za njen određeni komponent. Npr., stranica za prijavu prije nadzorne ploče (dashboard) ne bi preuzela sve komponente nadzorne ploče. Jer ono što je trenutno potrebno, tj. komponenta za prijavu, samo to će biti preuzeto.
Isto vrijedi i za export: ES6 export su potpuno isti kao za CommonJS module.exports.
Napomena: Ako razvijate Node.js projekt, tada morate strogo koristiti require() jer će Node.js baciti iznimku s greškom invalid token 'import' ako koristite import. Dakle, Node.js ne podržava import izjave.
Ažuriranje - Kao što je predložio Dan Dascalescu: Od verzije v8.5.0 (izdano rujan 2017.), node --experimental-modules index.mjs omogućuje vam korištenje import bez Babela. Također možete (i trebali biste) objaviti svoje npm pakete kao izvorni ES modul, s unatrag kompatibilnošću za stari require način.
Pogledajte ovo za više informacija o tome gdje koristiti asinhrona učitavanja: Neehar Venugopal - Vodič za početnike za razdvajanje koda u vašoj React aplikaciji - React Conf 2017
Odgovor #8
Najvažnije za znati je da su ES6 moduli, doista, službeni standard, dok CommonJS (Node.js) moduli nisu.
Godine 2019. ES6 moduli podržani su od strane 84% preglednika. Dok ih Node.js stavlja iza zastavice --experimental-modules, postoji i praktični Node paket nazvan esm, koji olakšava integraciju.
Još jedan problem na koji ćete vjerojatno naići između ovih sustava modula je lokacija koda. Node.js pretpostavlja da se izvorni kod čuva u direktoriju node_modules, dok se većina ES6 modula distribuira u ravnoj strukturi direktorija. Ovo nije lako uskladiti, ali se može učiniti izmjenom vašeg package.json datoteke pomoću skripti prije i nakon instalacije. Evo primjera izomorfnog modula i članka koji objašnjava kako to funkcionira.
Odgovor #9
Ja osobno koristim import jer možemo uvesti potrebne metode i članove koristeći import.
import {foo, bar} from "dep";
ImeDatoteke: dep.js
export foo function(){};
export const bar = 22
Zasluge idu Paulu Shanu. Više informacija.
