📝

ブラウザ、Node.jsの両方で使えるJavaScriptの書き方

2020/10/27に公開

(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行目が特殊な雰囲気です。hellomymoduleの名称は自由です。ただしmymoduleはブラウザ上でグローバル空間に定義される変数名となるので、汚染への配慮が必要です。

mymodule.js
(function (exports) {
    exports.hello = function(){return 'Hello world';};
})(typeof exports === 'undefined' ? this.mymodule = {} : exports);

サーバーサイド記述例

パスは例なので適切なものを使用してください。

server-side.js
var mymodule = require('./mymodule');
mymodule.hello();

クライアントサイド記述例

client-side.html
<script src="path/to/mymodule.js"></script>
<script src="path/to/client-side.js"></script>
client-side.js
alert(mymodule.hello());

共有ソースファイルをどこに置くかは悩ましいですが、今回のMEANアプリケーションではapp/lib/がある同階層にcommon/ディレクトリを作成、その中に保存し、app/scripts/common/からシンボリック・リンクを貼る方法をとりました。(まだ試行錯誤中です)

require()の共通化

exportsの共通化については調べればすぐに出たんですが、なかなか分からなかったのがrequire()の共通化でした。色々試したところ以下の書き方が使えそうでした。

my-other-module.ts
declare var mymodule;
var mymodule = (typeof require === 'undefined') ? mymodule : require('./mymodule');

exportsでの書き方を応用したものです。TypeScriptで書いているのでアンビエント宣言declare var mymodule;をつけています。JavaScriptなら2行目のみで大丈夫。これでNode.jsではrequire()を実行し、ブラウザ上ではrequireを飛ばしてグローバル変数mymoduleを使うことができます。

HTML記述例

client-side.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