小さく始める isomorphic module pattern

More than 3 years have passed since last update.


isomorphic

せっかく window や node/io の標準モジュールに依存していないロジックであれば、

ブラウザでも node/io で動くようにしておくと色々嬉しい。が軽視されている感がある。

俗に isomorphic な JavaScript と呼ばれている。

それを npm と bower で公開するのであれば、問題はモジュールシステムだ。


最小の isomorphic module pattern

一番シンプルで負荷の少ない方法。

まず、ライブラリを以下のように書く。

// lib.js

function Lib() {
// 変数は外に出さない
}

Lib.prototype.foo = function(){
return "foo";
};

this.Lib = Lib; // point

それを以下のように使う。

// main.js

var Lib = Lib || require('lib').Lib;

var lib = new Lib();
lib.foo(); // foo

これで node.js では普通に動く。

同じコードは HTML を以下のように書けば、ビルドなんかせず動く。

<script src="lib.js"></script>

<script src="main.js"></script>

this は module.exports or window だということを利用している。

注意点としては、 Lib 関数の外にあるものは、 node ではモジュールローカルだが、ブラウザでは window に付いてしまう点とカバー範囲が window + commonjs という点。

実例: [https://github.com/Jxck/obtain-unicode/blob/master/obtain-unicode.js]


UMD (Universal Module Definition)

上の例で足りない場合は、 UMD が検討できる。

おそらく名前としてはこちらの方が有名だろう。

以下にパターンが蓄積されている。

github umdjs/umd

いくつかあるが returnExports.jscommonjsStrict.js あたりを使えば大抵足りる。(自分が書いたコードが自身が別のモジュールに依存しているかどうかによって色々変わる)

// if the module has no dependencies, the above pattern can be simplified to

(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof exports === 'object') {
// commonjs
module.exports = factory();
} else {
// Browser globals
root.Lib = factory();
}
})(this, function () {
var bar = "bar"; // こういうのも隠される

function Lib() {
}

Lib.prototype.foo = function(){
return "foo";
};

return Lib; // point
});

特に AMD への対応もしたい場合や、モジュールローカル変数が window についてしまうのを防ぐための名前空間が欲しい場合などに使われる。

実例: [https://github.com/teppeis/htmlspecialchars/blob/master/index.js]


Browserify / WebPack

依存が複雑になると解決が面倒になる場合がある。するとツールで解決する。

Browserify は Node 標準モジュールも isomorphic にしてしまい、 WebPack は JS 以外の CSS なども含めて解決できるツールだが、両方ともその機能の一部を単なるモジュール解決ツールとして使われている。

どちらもやっているのは、ファイルの連結時に先の UMD の亜種でくるんでいるだけではある。

複数のディレクトリに分けたり、複数のモジュール間で複雑に依存が絡んでいる場合などに使うと良いと思う。

使わないで済む規模なら、個人的には避けたい。が ES6 modules がまだまだ先になりそうな現状、前述のパターンで辛い場合は使わざるを得ない。

実例: [https://github.com/twada/power-assert/blob/master/build/power-assert.js]


ES6 modules

ES6 では待望の標準モジュールシステムが入りそうである。

// lib.js

function Lib(){};

export Lib = Lib;

// main.js

import { Lib } from 'lib';

var lib = new Lib();

実装されれば、これだけでブラウザでも node/io でも動く予定。

ES6 modules final

人類の理想であり、救いであり、今はまだ夢である。

全ての混沌を解決してくれることを願い、首を長くして待つ。