npm とか bower とか一体何なんだよ!Javascript 界隈の文脈を理解しよう

  • 1962
    いいね
  • 4
    コメント
この記事は最終更新日から1年以上が経過しています。

背景

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 フォルダを作って、こんなコードを準備してみます。

src/add.js
// たし算モジュールを定義
module.exports = function (a, b) {
  return a + b;
};
src/main.js
// たし算モジュールを読み込み
var add = require('./add');

var txt = document.createTextNode(add(114, 514));
document.body.appendChild(txt);

モジュールの定義と読み込みをやっているあたりに CommonJS を感じましょう。このままではブラウザで実行できません。

あわせて、表示用の html を準備します。

index.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 が生成されます。

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/ フォルダの中にどんどん入っていくのですが、これをどうやってロードしたらいいのかわからなかったのでやめました。

参考