ブラウザ、Node.jsの両方で使えるJavaScriptの書き方
(2014/12/31追記)この記事は古いです。@kaiinuiさんの記事『最近の行儀のよい JavaScript の書き方』がよりモダンな書き方なのでそちらを参照するべきでしょう。この記事は記録のために残します。
最近MEANアプリケーションを書いています。サーバー側はNode.js + Express、ブラウザ上ではAngularJS、永続化にはMongoDBというやつです。
これらは全てJavaScript単一言語なので、これまでサーバーサイド言語とJavaScript間では不可能だったコード、ロジックの共有が可能です。ただし一工夫が必要です。
共通化に一工夫が必要になる理由
Node.jsでは他JSファイルを読み込む際var Foo = require('Foo');
といった宣言が必要になります。一方でクライアントサイド(以下「ブラウザ上」という)ではこういった概念はなく、主にHTMLの<script>
タグなどで順に読み込むだけです。
このときブラウザ上ではrequire()
が解釈できません。そのため、Node.jsを意識して書いたJavaScriptファイルは本来ブラウザ上では動きません。また、同様にNode.jsではexports
という機構もありますが、これもブラウザ上では解釈できません。
この差を吸収するために一工夫が必要になるのです。
exportsの共通化
1行目と3行目が特殊な雰囲気です。hello
やmymodule
の名称は自由です。ただしmymodule
はブラウザ上でグローバル空間に定義される変数名となるので、汚染への配慮が必要です。
(function (exports) {
exports.hello = function(){return 'Hello world';};
})(typeof exports === 'undefined' ? this.mymodule = {} : exports);
サーバーサイド記述例
パスは例なので適切なものを使用してください。
var mymodule = require('./mymodule');
mymodule.hello();
クライアントサイド記述例
<script src="path/to/mymodule.js"></script>
<script src="path/to/client-side.js"></script>
alert(mymodule.hello());
共有ソースファイルをどこに置くかは悩ましいですが、今回のMEANアプリケーションではapp/
とlib/
がある同階層にcommon/
ディレクトリを作成、その中に保存し、app/scripts/common/
からシンボリック・リンクを貼る方法をとりました。(まだ試行錯誤中です)
require()の共通化
exports
の共通化については調べればすぐに出たんですが、なかなか分からなかったのがrequire()
の共通化でした。色々試したところ以下の書き方が使えそうでした。
declare var mymodule;
var mymodule = (typeof require === 'undefined') ? mymodule : require('./mymodule');
exports
での書き方を応用したものです。TypeScriptで書いているのでアンビエント宣言declare var mymodule;
をつけています。JavaScriptなら2行目のみで大丈夫。これでNode.jsではrequire()
を実行し、ブラウザ上ではrequireを飛ばしてグローバル変数mymodule
を使うことができます。
HTML記述例
<script src="path/to/mymodule.js"></script>
<script src="path/to/my-other-module.js"></script>
<script src="path/to/client-side.js"></script>
出来るっちゃあ出来るけど
重ねてですが変数名mymodule
はグローバルなので、一意性を保証できる長めの名前が安心です。そもそもサーバーサイドとクライアントサイドでソースを無闇に共有しすぎる設計も考えものです。ご利用は計画的に。
ということで、じゃんじゃんJavaScript (TypeScript) を書いていきましょー。
Discussion