JavaScriptのimportとrequireの違いって?

JavaScriptで開発をしている時に、自分で作成したファイルやnodeモジュールなどを読み込む時にimportrequireを使う時があると思います。

そういえばこの二つの違いってなんだろうと疑問に思ったのでその違いを探っていきたいと思います。

結論を先に述べると、requireがnodeの書き方でimportがES2015の記述方法です。 因みに前者がCommonJSと呼ばれるモジュールシステム、後者がES Modulesです。

importは全ブラウザでサポートしているわけではないので、babelなどで変換する必要があります。

requireは当たり前のことですが、nodeがない環境では使用することができません。

ここで疑問が生まれます。 フロントエンドで開発をしていくとなるとnodeを使った開発が主流になると思います。node側でどのようにES Modulesを解釈しているのでしょうか?

この両者を区別するアルゴリズムが存在します。 CommonJSES Modulesによって処理内容を切り替えているようです。 こちらが参考にさせていただいた記事になります。

yosuke-furukawa.hatenablog.com

ではES Modulesの記述方法を見ていきましょう。

ES Modules

default exports

default exportsはexport default (渡したい変数や関数)の形式で記述します。以下のコードを見てください。

export default function(name) {
  console.log(`My name is, ${name}!`);
}

default imports

import getName from './greet.js';
getName('Tom');

このとき、greet.jsから渡された関数は、import先の変数getNameに代入されています。 export文では文字列や数値などの値、class、オブジェクトや配列などのリテラルも指定して公開できます。

named exports

こちらは上記のdefault exportsと違って1つのモジュールから個別にexportしたいものに変数名をつける必要があります

export const name = "tanaka";

export const fruits = ["Apple", "Orange", "Banana"];

exports const num = 40;

それぞれ変数名をつけてexportします。

named imports

上で定義したモジュールを読み込む際にはimport先でimport { (変数名) } from (パス名)の形式で、分割代入を記述するときのよう変数名を単一あるいは複数列挙します。

import { name, fruits } from './sub.js';

もしimport先から公開されている値のすべてを読み込みたいのであれば、任意の名前空間内に配置できます。

import * as module from './sub.js';
console.log(module.name); // "tanaka"

また変数名を読み込み側で変更することもできます。

import { name as myName } from './sub.js';

sub.jsで定義されている変数nameMynameと変更しています。

import元と同一ディレクトリのモジュールを読み込む場合には、./と記述しなくてはいけません。

それ以外の読み込みについては、HTMLからほかのリソースを指定する場合のパス指定のルールと一緒です。/から書き始めることで、ルートパスでの指定もできます。

// 同一階層のmenu.js を取得
import menu from './menu.js';

// 上の階層のscroll.js を取得
import scroll from '../scroll.js';

// ルートパスのroot.js を取得
import root from '/root.js';

また拡張子についても気をつける点があります。 webpackやBrowserifyを使っていると、.jsの拡張子を省いていてもモジュールを取得してくれることに慣れてしまっているかもしれませんね。

次回は、CommonJSについての書き方やルールについて解説していきたいと思います。