背景
Javascript で Web アプリを作ろうとすると、よくわからないことだらけで超混乱します。
- npm と bower の違いは?
- npm はサーバーサイド用、bower はクライアントサイド用らしいよ
- えっ、でもなんで bower のインストールに npm が必要なの?
- サーバーサイドは Rails で書きたいから npm 要らないと思うんだけど・・・
- ていうかサーバーサイドJSとか node.js って何?
- よく見る browserify って何?
こういった疑問が沸き上がるのは、各ツールが生まれた文脈がよくわかっていないからです。いろいろ調べてやっとちょっとわかってきたのでメモします。間違いがあったらご指摘ください。
「CommonJS」誕生 - Javascript は汎用プログラミング言語へ
その昔、Javascript 大好きおじさんは言いました。
Javascript 最高!ブラウザで使うだけではもったいない!汎用言語にしよう!
しかし、そうは言っても Javascript には足りないものがたくさんありました。その最たるものがモジュール機能です。C言語なら #include、ruby なら require、golang なら import。どんな言語でも用意されている、他のファイルを読み込んで使えるようにする機能が、なんと Javascript には用意されていませんでした。
そこで2009年ごろ、まったく新しい汎用言語としての Javascript の仕様を策定する動きが発足しました。こうして生まれたのが CommonJS です。
CommonJS はあくまで仕様であり、CommonJS というライブラリが存在するわけではありません。Common Lisp みたいなものです。CommonJS の仕様に則って実装された処理系のひとつが、かの有名な node.js です。Common Lisp に対する CLisp みたいなものです。こうして Javascript は、めでたく ruby のような汎用スクリプト言語の仲間入りを果たしました。実際、node.js は ruby と同じように node コマンドでスクリプトを実行できます。
$ node helloworld.js
追記(2015/10/01)
現在 node.js は独自の進化を遂げ、もはや CommonJS に準拠していません(参考1)(参考2)。node.js のことは「CommonJS みたいなやつ」だと思っておいてください。
CommonJS 界のパッケージ管理ツール「npm」誕生
実のところ、CommonJS の仕様策定はまだ全然終わっていません。それでも前述のモジュール機能はすぐに策定されたようで、もう使うことができます。そうなれば必然的に生まれてくるのが、gem や pip のようなパッケージ管理ツールです。
node.js の世界では、npm というパッケージ管理ツールを使います。npm のリポジトリに、node.js という言語で書かれた便利ツール達がどんどん追加されていきます。ここで、node.js はもはやブラウザという檻から外れた汎用言語なので、作られるツールも「コマンドラインで使って開発を楽にするツール」という趣が強いです。例えば、
- http-server : 簡易webサーバー。
- grunt : タスクランナー。
- bower : パッケージ管理ツール。
ん?
ブラウザ用JS のためのパッケージ管理ツール「bower」
いま名前のあがった bower は、node.js ではなく、ブラウザで動く昔ながらの Javascript のためのパッケージ管理ツールです。パッケージの例としては、
- JQuery
- AngularJS
- パララックス(かっこいいスクロール)を実装できる君
など。昔ながらの Javascript といえどもパッケージに依存性はあるし、インストールも簡単になるので、パッケージ管理ツールが存在することによるメリットは十分にあるのです。
そして、bower 自体は node.js で実装されています。これが、bower を使うために npm をインストールしないといけなかった理由です。実際のサーバーサイドの開発に ruby を使おうが php を使おうが、bower という node.js で書かれたプログラムを実行するために、node.js という処理系が必要なのです。
なおややこしいことに、AngularJS など一部のメジャーなパッケージは(たとえそれがブラウザ上で動くライブラリであろうと)bower でも npm でも配布されています。2014年の11月に、npm のブログ上で「npm が統一リポジトリになりますよ」宣言が公開されたそうですが、あんまりちゃんと片付いてはいないようです。
「Browserify」誕生 - 天界と地上を繋ぐ魔法
繰り返しになりますが、node.js はブラウザという枠を外れて独自に進化した言語です。逆に言うと、node.js として書かれた Javascript はそのままではブラウザで動きません。ところが、node.js の持つモジュール機能や npm 上にある無数の便利ツールにすっかり慣れてしまった node.jser 達は、もはや素の Javascript には戻れない体になってしまいました。
そこで生み出されたのが browserify です。browserify を使うと、まるでC言語を機械語にコンパイルするように、node.js スタイルで書かれたコードをブラウザ上で動くものに変換 することができます。普段は node.js スタイルの高級言語を使ってコーディングして、作業の最後にブラウザで動く低級言語に変換すればよいのです。
実践
実践で復習しましょう。上記のことがらを踏まえて、
- node.js のインストール
- npm で browserify をインストール
- CommonJS スタイルでコーディング
- browserify でブラウザ向けにビルドして表示
をやってみます。今度こそ理解できるはず!
node.js のインストール
やり方はいろいろあると思いますが、Mac なら homebrew を使うのが簡単です。処理系をインストールするわけですから、homebrew を使うのは自然なことです。
$ brew install node.js
npm で browserify をインストール
node.js をインストールすると、もう npm も使えるようになっています。最初に初期設定を行います。
$ mkdir npm_test
$ cd npm_test
$ npm init
(いろいろ聞かれるけど全部エンターで OK)
終わったら browserify をインストールしてみましょう。
npm install -g browserify
ここで、-g オプションはパッケージのグローバルインストールを意味します。browserify はいつでも使う便利ツールなのでシステム全体にインストールします。開発中のプロジェクト固有のパッケージをインストールするときは、-g オプションを外せば、プロジェクトフォルダの node_module フォルダ配下にダウンロードされます。
browserify という node.js 製の便利ツールをインストールするので、npm を使いました。これも自然に理解できると思います。
CommonJS スタイルでコーディング
npm_test 配下に src フォルダを作って、こんなコードを準備してみます。
// たし算モジュールを定義
module.exports = function (a, b) {
return a + b;
};
// たし算モジュールを読み込み
var add = require('./add');
var txt = document.createTextNode(add(114, 514));
document.body.appendChild(txt);
モジュールの定義と読み込みをやっているあたりに CommonJS を感じましょう。このままではブラウザで実行できません。
あわせて、表示用の html を準備します。
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<script src="dest/build.js"></script>
</body>
</html>
最後に、browserify でビルドした JS を出力するフォルダだけ作っておきましょう。
$ mkdir dest
現在のファイル構成はこんな感じです。
npm_test
├── dest
├── index.html
├── package.json
└── src
├── add.js
└── main.js
browserify でブラウザ向けにビルドして表示
いよいよビルドしましょう。
$ browserify src/main.js -o dest/build.js
上記を実行すると、dest フォルダに build.js が生成されます。
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = function (a, b) {
return a + b;
};
},{}],2:[function(require,module,exports){
var add = require('./add');
var txt = document.createTextNode(add(114, 514));
document.body.appendChild(txt);
},{"./add":1}]},{},[2]);
元コードで書いた require が解決されて、禍々しいコードが吐き出されています。index.html を開けば、めでたく足し算の結果が表示されているはずです。
おわりに
ここまで読んで、冒頭に書いた疑問に答えられるようになっていれば幸いです。
- npm と bower の違いは?
* npm はサーバーサイド用、bower はクライアントサイド用らしいよ
* えっ、でもなんで bower のインストールに npm が必要なの?
* サーバーサイドは Rails で書きたいから npm 要らないと思うんだけど・・・
- ていうかサーバーサイドJSとか node.js って何?
- よく見る browserify って何?
その他
browserify を毎回実行するのがめんどくさい件について
gulp でできるらしいです。gulp とか grunt とか一体何なんだよ!
browserify の -t オプションについて
場合によっては、CommonJS → ふつうのJS 以外にも変換が必要なことがあります。例えば CoffeeScript の変換など。これらに関しても一気通貫で変換できるよう、browserify には transform module なるプラグインが存在します。あらかじめインストールのうえ、-t オプションで指定すると一緒に変換してくれるようです。
$ npm install reactify
$ browserify -t reactify src/aaa.jsx > dest/aaa.js
結局 bower を使ってない件について
bower でインストールしたパッケージは components/ フォルダの中にどんどん入っていくのですが、これをどうやってロードしたらいいのかわからなかったのでやめました。