2017年3月22日水曜日

Node.js モジュールの読み込み(require)・公開(exports)

Node.js学習中。事前知識なしに require とか module.exports だとか出てくるとなんだこれはと思ったが仕組みは単純なもの。

require exports のまとめ

·         require('ファイル名') でモジュールを実行

·         実行中のjsファイルから見て node_modules/ファイル名.js を読み込む事が多い。 ファイルパスの解決の手順はやや複雑なため後述。

·         require で同じファイルを複数回読み込んでも2回目は実行されない。 (2回目以降は module.exports を返すだけになる。)

·         require の戻り値は modules.exports に代入したオブジェクト。

·         modules.exports には初期値として空のオブジェクトが入っているので modules.exports.foo = "bar" のような記述でもOK

·         exports というエイリアスが存在

·         this.exports === modules.exports true

·         exports には modules.exports と同じオブジェクトが入っているだけに過ぎないので例えば exports = ""; とすると this.exports modules.exports は無関係となる。 参考: http://d.hatena.ne.jp/jovi0608/20111226/1324879536

·         モジュール毎にそれぞれ独立したスコープを持つ

·         モジュール間のやり取りは module.exports のオブジェクトを介して行う。

·         大域変数は global はどのモジュールからもアクセス可能

モジュールの読み込みのカンタンな例

main.js node_modules/sub.js があるとする。

1
2
3
4
5
6
7
8
9
10
11
/// node_modules/sub.js

// オブジェクトを外部公開するときは
// module.exports にオブジェクトを設定
module.exports = 999;

// fooの値はグローバルスコープ
foo = 123;

// barの値はこのファイル内のみで有効
var bar = 456;

 

1
2
3
4
5
6
7
8
9
/// main.js

// require sub.js module.exports
// 設定されたオブジェクトを取得できる
var sub = require('sub');
console.log(sub);        // 999
console.log(foo);        // 123
console.log(global.foo); // 123
console.log(typeof bar); // undefined

$ node main で実行するとbarだけは main.js からアクセスできないため undefined となる。

モジュールのパス解決の順番

ディレクトリ区切り付きのパスかどうかでパス解決手順は異なる。

ディレクトリ区切り付きのパスの場合

ディレクトリ区切り付きのパスとは たとえば requie("./../sub")  require("/sub") のようなパス。 拡張子は省略可能。

この場合は指定したパスにファイルがなければそこで探索終了して例外を送出する。

ディレクトリ区切り付きのパスではない場合

requie("sub") のようにファイル名だけを指定した場合は coreモジュール => module.paths => NODE_PATH の順番で探索。

1.       coreモジュールの探索。 どのようなモジュールがあるかは module.exports._buildinLibs で確認可能

2.       module.paths 配列に定義されたディレクトリを順番に探索。 デフォルトでは "./node_modules/" フォルダ内を探索し、 見つかるまで "親フォルダ/node_modules/" フォルダへと探索。 "/node_modules/" (ルートディレクトリ)の探索で終了。 ただし module.paths は書き換え可能なので 空配列をセットすると node_modules の探索は行なわれなくなる。

3.       環境変数 NODE_PATH がセットされている場合はそのディレクトリを探索 NODE_PATH に複数の値を設定する場合は":"(コロン)で区切る。

4.       例外送出

モジュールの複数回読み込み

requireで読み込んだモジュールは一度しか実行されず、 2回目以降は modules.exports を返すだけとなる。

1
2
/// node_modules/sub.js
console.log("foo");

 

1
2
3
4
5
6
7
8
/// main.js
var a = require('sub'); // 内部で "foo" が出力される
var b = require('sub'); // 出力は無い

// a b も実体は同じオブジェクト
a.bar = 123;
console.log(b.bar); // 123
console.log(a === b); // true

modules.exports exports

exports という modules.exports のエイリアスが存在するが、 この exports はただ modules.exports の初期値を保持しているだけ。

要するに var exports = modules.exports; がモジュールの実行前に行われていると考えておけばだいたい合ってる。 (実際は関数の引数だけど)

 

npm と Node.js 上で require を使ったモジュール読み込みの仕組みについてメモ

npm でインストールしたモジュールの行方

まず, npm でインストールしたモジュールの行方についてまとめます.

npm でのインストールには 2種類あります.

ローカルインストール

普通にインストールするとローカルインストールになります. 現在のカレントディレクトリの node_modules フォルダの中にインストールします.

npm install モジュール名  

どこにインストールされるのかちゃんと確認したい場合は bin コマンドを使います.

npm bin  

グローバルインストール

グローバルインストールは全てのプロジェクト共通で使いたいモジュールをインストールする際に使います. やり方は簡単でローカルインストールの方法に -g オプションを加えるだけです.

また, グローバルインストールした場合, そのディレクトリにパスが通ります. coffeescript mocha を直接コマンドとして実行でるようになるのはそのためです.

npm install -g モジュール名  

グローバルインストール用フォルダがどこにあるのかは -g オプションをつけて bin コマンドを実行すると確認できます.

npm bin -g  

ちなみに windows の場合グローバルインストール用フォルダにパスが通っていないので, NODE_PATH 環境変数にパスを登録してから使うようにしましょう.

windows 7 の場合, C:/Users/ユーザー名/AppData/Roaming/npm/node_modules 的な場所にあるかと思います.

require でモジュールをサーチする場所

require でモジュールをサーチするディレクトリについてまとめます.

require で探すモジュールのパスは global.module.paths の中に入っています.

node  
global.module.paths  

デフォルトではカレントディレクトリから下りながら全てのディレクトリの node_modules が登録されています.

[ 'e:/Users/hoge/Documents/work/node_modules',
  'e:/Users/hoge/Documents/node_modules',
  'e:/Users/hoge/node_modules',
  'e:/Users/node_modules',
  'e:/node_modules' ]

それに加えて NODE_PATH 環境変数の中にあるモジュールを探しに行きます.

Reference

参考にしたサイト