Help us understand the problem. What is going on with this article?

# CommonJS と ES6の import/export で迷うなら

More than 3 years have passed since last update.

はじめに

いつもはes6のモジュール管理方式であるimport/exportを使って、npmで手に入れたライブラリや自分で作成したモジュールをロードしているが、たまに思った挙動にならないことがある。また、export defaultしたモジュールをテストしようとしてkarmaでrequire()するとエラーになったりした。そういう場合は、とりあえずmodule.exportsを使ってみるとうまくいったりすることが多いのだが、なぜなのかはあまり考えていなかった。

ということで、実際にはどうなっているのか、またモジュールシステムとは何なのかという点で基本的なことから理解を深めて、es6のimport/exportとの違いを知り、より正しく実装できるようになれば良いと思っている。

結論としては

方針としては基本的にはES6形式で記述するが、CommonJS形式の読み書きが必要なケースは、次の対比表を使って理解する。

対比表

CommonJS Export/Import 備考
exports.method = method export { method }
module.exports = function() {...} export default function() {...}
export.default = function() {...} export default function() {...} CommonJS的には、いちプロパティの default に対して関数を設定するのと同じこと
var a = require('module') import a from 'module'
var a = require('module').a import { a } from 'module'
var a = require('module').default import a from 'module' export defaultでエクスポートされたモジュールの場合

もう少し詳しく

Modules

Node.js にはシンプルなモジュールローディングシステムがある。ファイルとモジュールとは、一対一の関係がある。例えば次のようなsampleA.jsは、sampleAモジュールとみなすことができる。

sampleA.js
const PI = Math.PI;
exports.area = (r) => PI * r * r;
exports.circumferenct = (r) => 2 * PI * r;

exportsという特別なオブジェクトのプロパティとして、エクスポートしたい関数を追加すれば良い。

module.exports

もし、関数そのもの(例えばコンストラクタ)をエクスポートしたかったり、オブジェクトをエクスポートしたい場合は、module.exportsキーワードを用いる。なお、モジュールシステム自体は、require('module')にて実装されている。

The module object

module変数は、現在のモジュールを表現しているオブジェクトを参照している。便宜上、module.exportsexports経由でもアクセス可能と なっているが、続く項にあるようにexportsオブジェクト自体の参照を更新すると問題が生じる。

module.exports

module.exportsオブジェクトはモジュールシステムによって作られる。我慢できないことはないが、ライブラリ作成時にあるクラスのインスタンスをモジュールとしてエクスポートしたいニーズは多くある(newする、の意)。そうするためにはmodule.exportsに望みのオブジェクトを代入する。この時exports変数にオブジェクトを代入しても、エクスポートしたことにならないので注意する

exports alias

exports変数はモジュールの内部で利用可能であり、module.exportsの参照としてそのライフサイクルが始まる。もし新しい値を代入すれば前の値はバインドされることはないし、何もエクスポートされなくなる。ガイドとしては、もしこの関係性が奇妙に映るようならexportsは無視してmodule.exportsを使え、ということだ。

Export(ES6)

エキスポート文は、関数やオブジェクトやプリミティブ型の値をモジュールやファイルからエクスポートするときに使う。

Named exports

Named exports は、幾つかの値をエクスポートするときに便利で、インポートする側は、参照する際に同じ名前を使うことができる。

my-module.js
// module "my-module.js"
export function cube(x) {
  return x * x * x;
}
const foo = Math.PI + Math.SQRT2;
export { foo };

別のスクリプトから、次のように読み込むことができる。

index.js
import { cube, foo } from 'my-module';
console.log(cube(3)); // 27
console.log(foo);    // 4.555806215962888

Default export(only one per script)

default export においては、1モジュールにつき一つの初期エクスポートが存在している。これには、関数やクラス、オブジェクトなど何でも設定できる。

my-module.js
// module "my-module.js"
export default function cube(x) {
  return x * x * x;
}
index.js
import cube from 'my-module';
console.log(cube(3)); // 27

export default / require()

この組み合わせでエクスポート・インポートする場合は、次のように考える。

index.js
var a = require('./module').default

index.jsにて、importするならこう。

index.js
import a from './module'

aに対して、defaultを明示しているかどうかの違いがある。

この部分の取り扱いの違いが、karmaでテストする際のエラー(冒頭に記述した問題点)の原因であり、export defaultと、module.exports =は等価なものだという思い込みが諸悪の根源だった。

参考URL

Node.js v6.2.2 Documentation
JavaScriptとECMAScript、CommonJS/AMD/Browserify/RequireJS/Webpack、TypeScriptに関する覚え書き

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away