Node.js kullanmak için ES6 içe / dışa aktarma gerekir


930

Üzerinde çalıştığım bir projede, hangi modül sistemini kullanabileceğimiz konusunda iki seçeneğimiz var:

  1. Kullanarak modülleri İçe requireve kullanma ihracat module.exportsve exports.foo.
  2. ES6 kullanarak modülleri içe aktarma importve ES6 kullanarak dışa aktarmaexport

Birini diğerinin üzerinde kullanmanın herhangi bir performans avantajı var mı? Düğüm modülleri üzerinde ES6 modülleri kullanıp kullanmayacağımızı bilmemiz gereken başka bir şey var mı?


9
node --experimental-modules index.mjsimportBabil olmadan kullanmanıza olanak sağlar ve 8.5.0+ düğümünde çalışır. Ayrıca , npm paketlerinizi eski ES6 modülü olarak eski requireyolla geriye dönük uyumlulukla yayınlayabilir (ve göndermelisiniz) .
Dan Dascalescu

Yanıtlar:


728

Birini diğerinin üzerinde kullanmanın herhangi bir performans avantajı var mı?

ES6 modüllerini yerel olarak destekleyen henüz JavaScript motoru bulunmadığını unutmayın. Kendinize Babil kullandığınızı söylediniz. Babel zaten varsayılan olarak CommonJS'ye ( / ) dönüştürür importve exportbildirir . Bu nedenle, ES6 modülü sözdizimini kullansanız bile, kodu Düğümde çalıştırırsanız kaputun altında CommonJS kullanacaksınız.requiremodule.exports

CommonJS ve ES6 modülleri arasında teknik farklılıklar vardır, örneğin CommonJS modülleri dinamik olarak yüklemenize izin verir. ES6 buna izin vermiyor, ancak bunun için geliştirilmekte olan bir API var .

ES6 modülleri standardın bir parçası olduğu için onları kullanırım.


16
Ben kullanmaya çalıştı ES6 importile requireancak farklı çalıştı. CommonJS sadece bir sınıf varken sınıfın kendisini ihraç eder. ES6 dışa aktarmaları, birden fazla sınıf olduğu için .ClassNamedışa aktarılan sınıfı almak için kullanmanız gerekir. Uygulamayı gerçekten etkileyen başka farklılıklar var mı?
Thellimist

78
@Entei: Adlandırılmış bir dışa aktarma yerine varsayılan dışa aktarma istediğiniz gibi görünüyor. module.exports = ...;eşittir export default .... exports.foo = ...eşittir export var foo = ...;
Felix Kling

10
Babel'in nihayetinde Webpack import2 / Rollup (ve ES6 ağacının sallanmasına izin veren başka bir paketleyici) ile birlikte kullanılan Düğümdeki CommonJS'ye aktarılmasına rağmen, eşdeğer kod Düğüm crunches'ından önemli ölçüde daha küçük bir dosyayla kurtarabileceğinizi belirtmek gerekir. kullanarak yoluyla requiretam olarak çünkü gerçeğin ES6 ithalat / ihracat statik analiz sağlar. Bu, Düğüm (henüz) için bir fark yaratmazken, kodun sonunda tek bir tarayıcı paketi olarak sarılması kesinlikle mümkündür.
Lee Benson

5
dinamik bir ithalat yapmanız gerekmedikçe
chulian

3
ES6 Modülleri en son V8'de ve bayrakların arkasındaki diğer tarayıcılara da geliyor. Bakınız: medium.com/dev-channel/…
Nexii Malthus

180

Dikkate almak isteyebileceğiniz birkaç kullanım / özellik vardır:

gerektir:

  • Yüklenen modül adının önceden tanımlanmadığı / statik olmadığı veya yalnızca "gerçekten gerekli" olması durumunda (belirli kod akışına bağlı olarak) şartlı olarak yüklediğiniz dinamik yüklemeye sahip olabilirsiniz.
  • Yükleme senkronize. Bu, birden fazla requires'niz varsa, bunların tek tek yüklenip işlendiği anlamına gelir .

ES6 İthalatı:

  • Yalnızca ihtiyacınız olan parçaları seçmeli olarak yüklemek için adlandırılmış içe aktarımları kullanabilirsiniz. Bu hafızadan tasarruf edebilir.
  • İçe aktarma eşzamansız olabilir (ve mevcut ES6 Modül Yükleyicide aslında) ve biraz daha iyi performans gösterebilir.

Ayrıca, Modül gerektir sistemi standart tabanlı değildir. ES6 modülleri mevcut olduğunda standart hale gelme olasılığı düşüktür. Gelecekte, performans açısından avantajlı olacak çeşitli uygulamalarda ES6 Modülleri için yerel destek olacaktır.


16
ES6 ithalatının eşzamansız olduğunu düşündüren nedir?
Felix Kling

5
@FelixKling - çeşitli gözlemlerin kombinasyonu. JSPM (ES6 Modül Yükleyici ...) kullanarak Bir içe aktarma genel ad alanını değiştirdiğinde etkinin diğer içe aktarmalarda gözlenmediğini fark ettim (çünkü bunlar eşzamansız olarak gerçekleşir. Bu, aktarılan kodda da görülebilir). Ayrıca, bu davranış olduğu için (1 ithalat başkalarını etkilemez) bunu
Amit

35
Çok önemli bir şeyden bahsediyorsunuz: modül yükleyici. ES6 alma ve verme sözdizimini sağlarken modüllerin nasıl yüklenmesi gerektiğini tanımlamaz. Önemli olan, beyanların statik olarak analiz edilebilmesidir, böylece bağımlılıklar kod yürütülmeden belirlenebilir. Bu, bir modül yükleyicinin bir modülü senkronize veya asenkronize olarak yüklemesine izin verecektir. Ancak ES6 modülleri kendi başlarına senkron veya asenkron değildir.
Felix Kling

5
@FelixKling ES6 modülü yükleyici OP'de etiketlendi, bu yüzden cevabı ile ilgili hale getirdiğini varsayıyorum. Ayrıca gözlemlere dayanarak zaman uyumsuzluğun mevcut davranış ve gelecekteki olasılık (herhangi bir uygulamada) olduğunu, bu yüzden dikkate alınması gereken bir nokta olduğunu belirttim. Bunun yanlış olduğunu düşünüyor musun?
Amit

10
Modül sistemi / sözdizimini modül yükleyiciyle karıştırmamak önemlidir. Örneğin, düğüm için geliştirirseniz, muhtemelen ES6 modüllerini requirezaten derliyorsunuzdur , bu nedenle Node'un modül sistemini ve yükleyicisini zaten kullanıyorsunuzdur.
Felix Kling

41

Ana avantajları sözdizimidir:

  • Daha bildirimsel / kompakt sözdizimi
  • ES6 modülleri temel olarak UMD'yi (Evrensel Modül Tanımı) geçersiz kılacaktır - esas olarak CommonJS ve AMD (sunucuya karşı tarayıcı) arasındaki şemayı kaldırır.

ES6 modülleri ile herhangi bir performans avantajı görmeniz olası değildir. Tarayıcıda ES6 özellikleri için tam destek olsa bile, modülleri paketlemek için ekstra bir kütüphaneye ihtiyacınız olacaktır.


4
Tarayıcılarda tam ES6 modül desteği olsa bile, neden bir paketleyiciye ihtiyaç duyulduğunu açıklayabilir misiniz?
E. Sundin

1
Özür dileriz, daha anlamlı olmak için düzenlendi. İçe aktarma / dışa aktarma modülleri özelliğinin hiçbir tarayıcıda yerel olarak uygulanmadığını kastediyorum. Bir transpiler hala gereklidir.
snozza

16
Bana biraz çelişkili geliyor. Tam destek varsa , paketleyicinin amacı nedir? ES6 spesifikasyonunda eksik bir şey var mı? Tam olarak desteklenen bir ortamda bulunmayan paketçi aslında ne yapardı ?
E. Sundin

1
@Snozza dediği gibi ... "ithalat / ihracat modülleri özelliği safça herhangi tarayıcılarda Uygulanmayan bir transpiler hala gerekli"
robertmain

2
Artık fazladan kütüphaneye ihtiyacınız yok. V8.5.0'dan beri (bir yıldan fazla yayınlandı), Babel olmadan node --experimemntal-modules index.mjskullanmanıza izin verir import. Ayrıca , npm paketlerinizi eski ES6 modülü olarak eski requireyöntemle geriye dönük uyumlulukla yayınlayabilir (ve göndermelisiniz) . Birçok tarayıcı aynı zamanda yerel olarak dinamik içe aktarmayı destekler.
Dan Dascalescu

38

Birini diğerinin üzerinde kullanmanın herhangi bir performans avantajı var mı?

Mevcut yanıt import/exporthayırdır , çünkü mevcut tarayıcı motorlarının hiçbiri ES6 standardından uygulanmaz .

Bazı karşılaştırma çizelgeleri http://kangax.github.io/compat-table/es6/ bunu dikkate almaz, bu nedenle Chrome için neredeyse tüm yeşillikleri gördüğünüzde dikkatli olun. importES6 anahtar kelimesi dikkate alınmadı.

Başka bir deyişle, V8 dahil mevcut tarayıcı motorları alamıyor yeni bir JavaScript dosyası gelen ana JavaScript dosyası herhangi bir JavaScript yönergesi üzerinden.

( V8 ES6 şartnamesine göre bunu uygulayana kadar hala birkaç hata uzakta veya yıllarca uzakta olabiliriz.)

Bu belge ihtiyacımız olan şey ve bu belge de uymamız gereken şey.

Ve ES6 standardı, modülü (başlık) .hdosyalarımız olduğu C programlama dilinde olduğu gibi modülü okumadan önce modül bağımlılıklarının orada olması gerektiğini söyledi .

Bu iyi ve iyi test edilmiş bir yapı ve eminim ki ES6 standardını yaratan uzmanlar bunu akılda tuttular.

Bu, Webpack veya diğer paket paketleyicilerin bazı özel durumlarda paketi optimize etmesini ve paketten gerekli olmayan bazı bağımlılıkları azaltmasını sağlar. Ancak mükemmel bağımlılıklarımız olduğunda, bu asla olmayacak.

import/exportYerel desteğin yayına girmesi biraz zaman requirealacak ve anahtar kelime uzun süre hiçbir yere gitmeyecek.

Nedir require?

node.jsModülleri yüklemenin yolu budur . ( https://github.com/nodejs/node )

Düğüm, dosyaları okumak için sistem düzeyinde yöntemler kullanır. Kullanırken temelde buna güveniyorsunuz require. JavaScript dosyasını / modülünü yüklemek için (son sisteme, Linux, Mac, Windows'a bağlı olarak) requiregibi bazı sistem çağrılarında uv_fs_opensona erecektir.

Bunun doğru olup olmadığını kontrol etmek için Babel.js'yi kullanmayı deneyin ve importanahtar kelimenin dönüştürüleceğini göreceksiniz require.

resim açıklamasını buraya girin


2
Aslında, performans bir yerde var olabilir bohça boyutu - iyileştirilmelidir. importBir Webpack 2 / Rollup derleme işleminde kullanmak , kullanılmayan modülleri / kodu 'ağaç sallayarak' ile sonuçta ortaya çıkan dosya boyutunu potansiyel olarak küçültebilir, aksi takdirde son pakete sarılabilir. Daha küçük dosya boyutu = daha hızlı indirilmesi = istemcide başlatılması / çalıştırılması daha hızlıdır.
Lee Benson

2
akıl yürütme dünya gezegeninde mevcut bir tarayıcı olmadığı için import doğal olarak anahtar kelimeye izin verir . Veya bu, bir JavaScript dosyasından başka bir JavaScript dosyasını içe aktaramayacağınız anlamına gelir. Bu yüzden bu ikisinin performans avantajlarını karşılaştıramazsınız. Ancak elbette, Webpack1 / 2 veya Browserify gibi araçlar sıkıştırma ile başa çıkabilir. Boyundan boyuna: gist.github.com/substack/68f8d502be42d5cd4942
prosti

4
'Ağaç titremesine' bakıyorsunuz. Gist bağlantınızın hiçbir yerinde ağaç titremesi tartışılmaz. ES6 modüllerinin kullanılması bunu mümkün kılar, çünkü importve exportbelirli bir kod yolunu içe aktaran statik bildirimlerdir, oysa requiredinamik olabilir ve bu nedenle kullanılmayan kodda toplanabilir. Performans yararı indirect-- WebPack 2 ve / veya toplaması olabilir , potansiyel olarak daha hızlı indirmek için daha küçük paket boyutları neden olur ve bu nedenle de (bir tarayıcı), son kullanıcıya akıcı görünür. Bu, yalnızca tüm kod ES6 modüllerinde yazıldıysa çalışır ve bu nedenle içe aktarmalar statik olarak analiz edilebilir.
Lee Benson

2
@LeeBenson cevabını güncelledim, tarayıcı motorlarının yerel desteğini düşünürsek, henüz kıyaslayamadığımızı düşünüyorum. Webpack kullanarak kullanışlı üç çalkalama seçeneği olarak gelen şey, CommonJS modüllerini ayarlamadan önce bile elde edilebilir, çünkü gerçek uygulamaların çoğunda hangi modüllerin kullanılması gerektiğini biliyoruz.
prosti

1
Cevabınız tamamen geçerli, ancak bence iki farklı özelliği karşılaştırıyoruz. Tümü import/export dönüştürülür require, verilir. Ancak bu adımdan önce ne olduğu "performans" arttırıcı olarak değerlendirilebilir. Örnek: lodashES6'da yazılırsa ve siz import { omit } from lodash, nihai paket SADECE diğer yardımcı programları değil, sadece 'atlamayı' içerecektir, oysa basit bir require('lodash')şey her şeyi içe aktaracaktır. Bu, paket boyutunu artıracak, indirilmesi daha uzun sürecek ve dolayısıyla performansı düşürecektir. Bu sadece bir tarayıcı bağlamında geçerlidir.
Lee Benson

31

ES6 modüllerini kullanmak 'ağaç sallamak' için yararlı olabilir; yani, Webpack 2, Toplama (veya diğer paketleyicilerin) kullanılmayan / içe aktarılmayan kod yollarını tanımlamasını ve dolayısıyla sonuçta ortaya çıkan paketin içine girmemesini sağlamak. Bu, asla ihtiyacınız olmayacak kodu ortadan kaldırarak dosya boyutunu önemli ölçüde azaltabilir, ancak Webpack ve diğerlerinin gerekli olup olmadığını bilmenin bir yolu olmadığından, CommonJS ile varsayılan olarak paketlenmiştir.

Bu, kod yolunun statik analizi kullanılarak yapılır.

Örneğin, şunu kullanarak:

import { somePart } 'of/a/package';

... paketleyiciye package.anotherPartgerekli olmayan bir ipucu verir (içe aktarılmadıysa, kullanılamaz - değil mi?), bu yüzden paketlemeyi rahatsız etmez.

Webpack 2 için bunu etkinleştirmek için, transpilerinizin CommonJS modüllerini tükürmediğinden emin olmanız gerekir. Eğer kullanıyorsanız es2015eklentiyi babel ile, kendi içinde devre dışı bırakabilirsiniz .babelrcşöyle:

{
  "presets": [
    ["es2015", { modules: false }],
  ]
}

Toplama ve diğerleri farklı çalışabilir - ilgileniyorsanız dokümanları görüntüleyin.


2
ağaç sallamak için de harika 2ality.com/2015/12/webpack-tree-shaking.html
prosti

25

Zaman uyumsuz veya belki de tembel yükleme söz konusu olduğunda import (), çok daha güçlüdür. importBileşene eşzamansız olarak ne zaman ihtiyaç duyduğumuza bakın, daha sonra constdeğişken kullanımdaki gibi bazı eşzamansız bir şekilde kullanırız await.

const module = await import('./module.js');

Veya o zaman kullanmak istiyorsanız require(),

const converter = require('./converter');

Şey import()aslında doğada zaman uyumsuzdur. İçinde neehar Venugopal tarafından belirtildiği gibi ReactConf , istemci tarafı mimarisi için bileşenleri tepki olarak yüke için kullanabilirsiniz.

Yönlendirme söz konusu olduğunda da çok daha iyi. Bu, kullanıcı belirli bir web sitesine kendi özel bileşenine bağlandığında ağ günlüğünü gerekli bir parçayı indirmesini sağlayan özel bir şeydir. örneğin, kontrol panelinden önceki giriş sayfası, kontrol panelinin tüm bileşenlerini indirmez. Geçerli olan yani oturum açma bileşeni gerekli olduğundan, bu sadece indirilecektir.

exportŞunun için de geçerlidir : ES6 export, CommonJS ile tamamen aynıdır module.exports.

NOT - Eğer bir node.js projesi geliştiriyorsanız, kesinlikle kullanacağınız require()gibi düğümün istisna hatası atacağı için invalid token 'import'kullanmanız gerekir import. Bu nedenle düğüm, içe aktarma ifadelerini desteklemez.

GÜNCELLEME - Dan Dascalescu tarafından önerildiği gibi : v8.5.0'dan (Eylül 2017'de piyasaya sürüldüğünden), Babel olmadan node --experimental-modules index.mjskullanmanıza izin veriyor import. Ayrıca , npm paketlerinizi eski ES6 modülü olarak eski requireyöntemle geriye dönük uyumlulukla yayınlayabilir (ve göndermelisiniz) .

Eşzamansız ithalatların nerede kullanılacağı hakkında daha fazla bilgi için buna bakın - https://www.youtube.com/watch?v=bb6RCrDaxhw


1
Peki bu gereksinim senkronize edilecek ve bekleyecek mi?
baklazan

1
Aslında söyleyebilirim!
Zaveri ile tanışın

15

Bilmeniz gereken en önemli şey, ES6 modüllerinin gerçekten resmi bir standart olmasına rağmen CommonJS (Node.js) modülleri değildir.

2019'da ES6 modülleri tarayıcıların % 84'ü tarafından destekleniyor . Node.js onları - deneysel modüller bayrağının arkasına koyarken , esm adında , tümleştirmeyi kolaylaştıran kullanışlı bir düğüm paketi de vardır .

Bu modül sistemleri arasında karşılaşabileceğiniz bir diğer sorun da kod konumudur. node_modulesÇoğu ES6 modülü düz bir dizin yapısında konuşlandırılırken, Node.js kaynağın bir dizinde tutulduğunu varsayar . Bunların mutabakatı kolay değildir, ancak package.jsonyükleme öncesi ve sonrası komut dosyalarıyla dosyanızı hackleyerek yapılabilir . Örnek bir izomorfik modül ve nasıl çalıştığını açıklayan bir makale .


8

Şahsen import kullanıyorum çünkü gerekli yöntemleri, üyeleri import kullanarak içe aktarabiliriz.

import {foo, bar} from "dep";

Dosya Adı : dep.js

export foo function(){};
export const bar = 22

Kredi Paul Shan'a gidiyor. Daha fazla bilgi .



6
aynı şeyi gerektiren ile yapabilirsiniz!
Suisse

4
const {a,b} = require('module.js'); iyi çalışıyor ... Eğer ihracat aveb
BananaAcid

module.exports = { a: ()={}, b: 22 }- @BananaAcid'in ikinci bölümü
Seth McClaine

7

Şu anda ES6 ithalatı itibariyle, ihracat her zaman CommonJS'ye derlenmektedir , bu nedenle birini veya diğerini kullanmanın bir faydası yoktur . ES6 kullanımı önerilmesine rağmen, tarayıcılardan yerel destek verildiğinde avantajlı olması gerekir. Nedeni, bir dosyadan kısmi alabilirsiniz iken CommonJS ile tüm dosya gerekir.

ES6 → import, export default, export

CommonJS → require, module.exports, exports.foo

Bunların ortak kullanımı aşağıdadır.

ES6 dışa aktarma varsayılanı

// hello.js
function hello() {
  return 'hello'
}
export default hello

// app.js
import hello from './hello'
hello() // returns hello

ES6 çoklu gönderme ve çoklu alma

// 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 çoklu

// 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

0

Neden (muhtemelen optimizasyon - tembel yükleme?) Emin değilim, ama importithal modüller kullanılmazsa kodu ayrıştırmayan fark ettim .
Bazı durumlarda beklenen davranış bu olmayabilir.

Örnek bağımlılığımız olarak nefret edilen Foo sınıfını ele alalım.

foo.ts

export default class Foo {}
console.log('Foo loaded');

Örneğin:

index.ts

import Foo from './foo'
// prints nothing

index.ts

const Foo = require('./foo').default;
// prints "Foo loaded"

index.ts

(async () => {
    const FooPack = await import('./foo');
    // prints "Foo loaded"
})();

Diğer yandan:

index.ts

import Foo from './foo'
typeof Foo; // any use case
// prints "Foo loaded"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.