Node.jsの使用には、ES6のインポート/エクスポートが必要です。


930

私が共同で取り組んでいるプロジェクトでは、使用できるモジュールシステムについて2つの選択肢があります。

  1. を使用してモジュールをインポートしrequire、およびを使用module.exportsしてエクスポートしexports.fooます。
  2. ES6を使用したモジュールのインポートimport、およびES6を使用したエクスポートexport

どちらを使用してもパフォーマンス上のメリットはありますか?ノードモジュールでES6モジュールを使用する場合に知っておくべきことは他にありますか?


9
node --experimental-modules index.mjsimportBabelなしで使用でき、Node 8.5.0以降で動作します。npmパッケージをネイティブESModuleとして公開することもできます(そうする必要がありますrequire。以前の方法との下位互換性があります。
Dan Dascalescu、

回答:


728

どちらを使用してもパフォーマンス上のメリットはありますか?

ES6モジュールをネイティブでサポートするJavaScriptエンジンはまだないことに注意してください。あなたは自分がバベルを使っていると言っていました。とにかくimport、BabelはデフォルトでexportCommonJS(require/ module.exports)に変換して宣言します。したがって、ES6モジュール構文を使用していても、Nodeでコードを実行すると、CommonJSが内部で使用されます。

CommonJSとES6モジュールの間には技術的な違いがあります。たとえば、CommonJSではモジュールを動的にロードできます。ES6はこれを許可していませんが、開発中のAPIがあります

ES6モジュールは標準の一部なので、私はそれらを使用します。


16
で使用しようとしましたがES6 import、動作requireが異なりました。CommonJSは、クラスが1つしかないときにクラス自体をエクスポートします。ES6は複数のクラスがあるように.ClassNameエクスポートするので、エクスポートされたクラスを取得するために使用する必要があります。実際の実装をもたらす任意の他の違いがあります
Thellimist

78
@Entei:名前付きエクスポートではなく、デフォルトのエクスポートが必要なようです。module.exports = ...;と同等export default ...です。exports.foo = ...と同等export var foo = ...です。
Felix Kling、2015

10
importWebpack 2 / Rollup(およびES6ツリーのシェイクを可能にする他の任意のバンドラー)と共に使用されるBabelは最終的にNodeのCommonJSにトランスパイリングしますが、同等のコードNodeのクランチよりも大幅に小さいファイルで終了する可能性があることは注目に値しますES6がインポート/エクスポートの静的分析を可能にするという事実のために、require正確に使用する。これは(まだ)Nodeに影響を与えませんが、コードが最終的に単一のブラウザーバンドルとして完成する場合は、確かに可能です。
Lee Benson

5
動的インポートを行う必要がある場合を除いて
チュリアン2017

3
ES6モジュールは最新のV8に含まれており、フラグの背後にある他のブラウザーにも到着しています。参照してください。medium.com
channel

180

検討する必要のある使用法/機能がいくつかあります。

必要とする:

  • ロードされたモジュール名が事前定義されていない/ staticである動的ロード、または「特定のコードフローに応じて」「本当に必要」な場合にのみモジュールを条件付きでロードすることができます。
  • ロードは同期です。つまりrequire、複数のがある場合、それらは1つずつ読み込まれて処理されます。

ES6インポート:

  • 名前付きインポートを使用して、必要な部分のみを選択的にロードできます。メモリを節約できます。
  • インポートは非​​同期にすることができ(現在のES6モジュールローダーでは実際には非同期です)、パフォーマンスが少し向上します。

また、Requireモジュールシステムは標準ベースではありません。ES6モジュールが存在するようになった今、標準になる可能性はほとんどありません。将来的には、さまざまな実装でES6モジュールがネイティブにサポートされ、パフォーマンスの点で有利になります。


16
ES6のインポートが非同期だと思うのはなぜですか?
Felix Kling

5
@FelixKling-さまざまな観察の組み合わせ。JSPM(ES6 Module Loader ...)を使用しています。インポートによってグローバル名前空間が変更された場合、他のインポート内では影響が見られないことに気付きました(非同期で発生するためです。これは変換されたコードでも確認できます)。また、それが動作であるため(1つのインポートは他に影響を与えません)、そうしない理由はないので、実装に依存する可能性があります
Amit

35
あなたは非常に重要な何かを言及します:モジュールローダー。ES6にはインポートとエクスポートの構文が用意されていますが、モジュールのロード方法は定義されていません。重要な部分は、宣言が静的に分析可能であるため、コードを実行せずに依存関係を判別できることです。これにより、モジュールローダーはモジュールを同期または非同期でロードできます。ただし、ES6モジュール自体は同期または非同期ではありません。
Felix Kling、2015

5
OPで@FelixKling ES6モジュールローダーにタグが付けられたので、それが回答に関連していると思います。また、観察に基づくと、非同期は現在の動作であり、将来の可能性(任意の実装)であるので、考慮する必要のあるポイントです。あなたはそれが間違っていると思いますか?
2015

10
モジュールシステム/構文をモジュールローダーと混同しないことが重要だと思います。たとえば、ノード用に開発している場合は、requireとにかくES6モジュールをコンパイルする可能性があるため、ノードのモジュールシステムとローダーをとにかく使用しています。
Felix Kling、2015

41

主な利点は構文です:

  • より宣言的/コンパクトな構文
  • ES6モジュールは基本的にUMD(Universal Module Definition)を廃止します-本質的にCommonJSとAMD(サーバーとブラウザ)の間の分裂を取り除きます。

ES6モジュールでパフォーマンスの利点が見られる可能性はほとんどありません。ブラウザでES6機能が完全にサポートされている場合でも、モジュールをバンドルするために追加のライブラリが必要です。


4
ブラウザーがES6モジュールを完全にサポートしている場合でも、なぜバンドラーが必要なのかを明確にしていただけませんか?
E. Sundin

1
謝罪、より意味のあるように編集。モジュールのインポート/エクスポート機能は、どのブラウザーにもネイティブに実装されていないことを意味しました。トランスパイラーはまだ必要です。
snozza

16
私には少し矛盾しているようです。完全なサポートがある場合、バンドラーの目的は何ですか?ES6仕様に欠けているものはありますか?完全にサポートされている環境では利用できない、バンドラーが実際に何をするのでしょうか?
E. Sundin

1
@snozzaが言ったように...「インポート/エクスポートモジュール機能は、どのブラウザにも単純に実装されていません。トランスパイラは依然として必要です」
robertmain

2
余分なライブラリはもう必要ありません。v8.5.0(1年以上前にリリース)以降、Babelなしでnode --experimemntal-modules index.mjs使用できますimportnpmパッケージをネイティブESModuleとして公開することもできます(そうする必要がありますrequire。以前の方法との下位互換性があります。多くのブラウザーは、動的インポートもネイティブでサポートしています。
Dan Dascalescu 2018

38

どちらを使用してもパフォーマンス上のメリットはありますか?

現在の答えはノーです。現在のブラウザエンジンはどれもimport/exportES6標準から実装されていないためです。

いくつかの比較チャートhttp://kangax.github.io/compat-table/es6/はこれを考慮に入れていないため、Chromeのほぼすべてのグリーンが表示される場合は注意してください。importES6のキーワードは考慮されていません。

つまり、V8を含む現在のブラウザエンジンは、JavaScriptディレクティブを介してメインJavaScriptファイルから新しいJavaScriptファイルをインポートできません。

(V8がES6仕様に従ってそれを実装するまでまだ数バグまたは年先の可能性があります。)

このドキュメントは私たちが必要とするものであり、このドキュメントは私たちが従わなければならないものです。

また、ES6標準では、(ヘッダー).hファイルがあるプログラミング言語Cのように、モジュールを読み取る前にモジュールの依存関係が存在する必要があると述べています。

これは適切で十分にテストされた構造であり、ES6標準を作成した専門家はそれを念頭に置いていたと思います。

これにより、Webpackまたは他のパッケージバンドルが、いくつかの特別な場合にバンドルを最適化し、バンドルからの不要な依存関係を減らすことができます。しかし、完全な依存関係がある場合、これは決して起こりません。

import/exportネイティブサポートが公開されるまでにはしばらく時間がかかり、requireキーワードはどこにも行きません。

なにrequire

これはnode.jsモジュールをロードする方法です。(https://github.com/nodejs/node

ノードはシステムレベルのメソッドを使用してファイルを読み取ります。を使用するときは、基本的にそれに依存しrequireます。(エンドシステム、Linux、Mac、Windowsに依存する)JavaScriptファイル/モジュールをロードするrequireようなシステムコールで終了しuv_fs_openます。

これが正しいことを確認するには、Babel.jsを使用すると、importキーワードがに変換されrequireます。

ここに画像の説明を入力してください


2
実際、パフォーマンス改善できる領域が1つあります。それはバンドルサイズです。importWebpack 2 / Rollupビルドプロセスで使用すると、未使用のモジュール/コードを「ツリーシェイク」することにより、最終的なバンドルに巻き込まれる可能性があるファイルサイズを潜在的に減らすことができます。小さいファイルサイズ=ダウンロードの高速化=クライアントでの初期化/実行の高速化。
Lee Benson

2
推論は、地球上の現在のブラウザがimport キーワードをネイティブに許可していないことでした。または、これは、JavaScriptファイルから別のJavaScriptファイルをインポートできないことを意味します。これが、これら2つのパフォーマンスの利点を比較できない理由です。しかしもちろん、Webpack1 / 2やBrowserifyなどのツールは圧縮を処理できます。彼らは首から首までです:gist.github.com/substack/68f8d502be42d5cd4942
prosti

4
「木の揺れ」を見落としている。あなたの要点リンクのどこにも、ツリーの揺れが議論されていません。ためES6モジュールを使用することを可能にimportし、export特定のコードパスをインポート静的宣言され、一方、require動的とすることができるので、使用されていないコードにバンドル。パフォーマンス上の利点は、indirect-- WebPACKの2及び/又はロールアップができ、潜在的に高速ダウンロードにある小さいバンドルサイズをもたらし、したがって、(ブラウザの)エンドユーザにきびきび見えます。これは、すべてのコードがES6モジュールで記述されている場合にのみ機能するため、インポートを静的に分析できます。
Lee Benson

2
回答@LeeBensonを更新しました。ブラウザーエンジンのネイティブサポートを検討した場合、まだ比較することはできません。Webpackを使用した便利な3つのシェイクオプションは、CommonJSモジュールを設定する前でも実現できます。これは、実際のアプリケーションのほとんどで、使用するモジュールがわかっているためです。
prosti 16

1
あなたの答えは完全に有効ですが、私たちは2つの異なる特性を比較していると思います。すべて import/exportがに変換されrequire、付与されます。しかし、このステップの前に何が起こるかは、「パフォーマンス」強化と見なすことができます。例:lodashがES6で記述されている場合import { omit } from lodash、究極のバンドルには「除外」のみが含まれ、他のユーティリティは含まれませんが、シンプルバンドルはrequire('lodash')すべてをインポートします。これにより、バンドルサイズが大きくなり、ダウンロードに時間がかかるため、パフォーマンスが低下します。もちろん、これはブラウザのコンテキストでのみ有効です。
Lee Benson、

31

ES6モジュールを使用すると、「ツリーの揺れ」に役立ちます。つまり、Webpack 2、Rollup(または他のバンドラー)を有効にして、使用/インポートされていないコードパスを識別し、結果のバンドルに含めないようにします。これにより、不要なコードを排除することでファイルサイズを大幅に削減できますが、CommonJSにはデフォルトでバンドルされています。これは、Webpackなどが必要かどうかを知る方法がないためです。

これは、コードパスの静的分析を使用して行われます。

たとえば、次のように使用します。

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

... package.anotherPartバンドラーに不要なヒントを提供します(インポートされない場合、使用できません-正しいですか?)。バンドルする必要はありません。

Webpack 2でこれを有効にするには、トランスパイラーがCommonJSモジュールを吐き出さないようにする必要があります。あなたが使用している場合はes2015プラグインをバベルと、あなたはあなたにそれを無効にすることができます.babelrcので、のように:

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

ロールアップなどは動作が異なる場合があります。興味がある場合はドキュメントを参照してください。



25

非同期読み込みまたは遅延読み込みの場合、import ()はるかに強力です。非同期の方法でコンポーネントが必要な場合を確認してから、を使用importするconst変数のように、非同期の方法でそれを使用しますawait

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

または、使用したい場合require()は、

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

物事はimport()実際には非同期です。でneehar venugopalで述べたようにReactConf、あなたはクライアント側のアーキテクチャのコンポーネントを動的に反応する負荷にそれを使用することができます。

また、ルーティングに関しては、はるかに優れています。これは、ユーザーが特定のコンポーネントに特定のWebサイトに接続したときに、ネットワークログに必要な部分をダウンロードさせる特別なものです。例えば、ダッシュボードがダッシュボードのすべてのコンポーネントをダウンロードする前にログインページ。現在必要なのはログインコンポーネントなので、ダウンロードされるのはそれだけです。

同じことがexport当てはまりexportます。ES6 はCommonJSとまったく同じmodule.exportsです。

-node.jsプロジェクトを開発している場合は、厳密に使用する必要があります。これは、を使用する場合require()と同様に、ノードが例外エラーをスローinvalid token 'import'するためimportです。したがって、ノードはインポート文をサポートしていません。

更新-Dan Dascalescuによって提案されたように:v8.5.0(2017年9月リリース)以降、Babelなしでnode --experimental-modules index.mjs使用できますimportnpmパッケージをネイティブESModuleとして公開することもできます(そうする必要がありますrequire。 以前の方法との下位互換性があります。

非同期インポートを使用する場所の詳細については、こちらをご覧ください-https://www.youtube.com/watch?v = bb6RCrDaxhw


1
では、requireは同期して待機しますか?
バクラザン

1
事実と言える!
Zaveriに会う

15

知っておくべき最も重要なことは、ES6モジュールは確かに公式の標準ですが、CommonJS(Node.js)モジュールはそうではないということです。

2019年、ES6モジュールは84%のブラウザーでサポートされます。Node.jsはそれらを--experimental-modulesフラグの後ろに配置しますが、esmと呼ばれる便利なノードパッケージもあり、これにより統合がスムーズになります。

これらのモジュールシステム間で遭遇する可能性が高いもう1つの問題は、コードの場所です。Node.jsは、ソースがnode_modulesディレクトリに保持されていると想定していますが、ほとんどのES6モジュールはフラットなディレクトリ構造でデプロイされています。これらの調整は簡単ではありませんが、package.jsonインストール前後のスクリプトを使用してファイルをハッキングすることで実行できます。ここに同形モジュールの例とそれがどのように機能するかを説明する記事があります。


8

私は個人的にインポートを使用しています。インポートを使用して、必要なメソッド、メンバーをインポートできるからです。

import {foo, bar} from "dep";

ファイル名: dep.js

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

クレジットはポールシャンに行く。詳細情報


1
偉大な選択肢!あなたもしている後方互換性で、ネイティブESModuleとしてあなたのNPMパッケージを公開古いためにrequire方法はありますか?
Dan Dascalescu

6
requireでも同じことができます!
スイス

4
const {a,b} = require('module.js'); 同様に動作します...エクスポートaした場合b
BananaAcid

module.exports = { a: ()={}, b: 22 }-@BananaAcidレスポンスの後半
Seth McClaine、

7

現在、ES6インポートの時点で、エクスポートは常にCommonJSコンパイルされているため、どちらを使用してもメリットません。ES6の使用をお勧めしますが、ブラウザからのネイティブサポートがリリースされたときに有利になるはずです。その理由は、CommonJSではすべてのファイルを必要とする一方で、1つのファイルからパーシャルをインポートできるためです。

ES6→ import, export default, export

CommonJS→ require, module.exports, exports.foo

以下はそれらの一般的な使用法です。

ES6エクスポートのデフォルト

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

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

ES6複数エクスポートおよび複数インポート

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

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

なぜ(おそらく最適化-遅延読み込み?)それはそのように機能しているのimportかわかりませんが、インポートしたモジュールを使用しないとコードを解析できない可能性があることに気付きました。
これは、場合によっては予期されない動作になる可能性があります。

サンプルの依存関係として、嫌われているFooクラスを取り上げます。

foo.ts

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

例えば:

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"
})();

一方:

index.ts

import Foo from './foo'
typeof Foo; // any use case
// prints "Foo loaded"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.