npmとbrowserifyを使ったクライアントサイドのウェブアプリ開発
YoutubeとVimeoの検索結果のRSSをまとめてOPMLで取得できるサイトを作った | Web scratch で公開した
Tech Video RSS Searcher はbrowserifyを使って作ったので、その辺の開発フローについての記事です。
browserify って何?
browserify はNode.jsスタイルで書かれたモジュール(CommonJS)を
ブラウザで利用できるように変換するコマンドラインツール(Nodeモジュール)となっています。
又、node.jsのCore Modulesのshimが用意されていて、
npmで公開されているnode.js向けのモジュールも一緒に変換してブラウザで動かすことが出来るようになっています。(普通に require
で読みこめば勝手に変換されます)
原理的に無理だったり全てのモジュールが動くわけじゃないですが、
その辺の互換レイヤーについては以下を見るといいかと思います。
むしろ駄菓子屋さんで | browserify概説 で紹介されてるようなv1の頃のアーキテクチャとは違う部分も多いので、昔見たことある人は別物だと思って見るといいかもしれません。
- announcing browserify v2 v2の時の変更
追記:
Browserify作者によるsubstack/browserify-handbookというハンドブックが公開されています。
より体系的な内容がまとまっているため読むことをオススメします。
例えば、以下のようなシンプルなCommonJSで書かれたものを見てみます。
var add = require("./add");
add(1, 2); // => 3
module.exports = function (x, y) {
return x + y;
};
これをbrowserifyでビルドしてみると以下のように変換されてます。
browserify simple/index.js -o simple/bundle.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,!);if(i)return i(o,!);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = function (x, y) {
return x + y;
};
},{}],2:[function(require,module,exports){
var add = require("./add");
add(1, 2); // => 3
},{"./add":1}]},{},[2])
変換したbundle.jsは、他にローダライブラリ等に依存はしてないのでそのまま読み込むだけで扱えます。
browserify について興味をもった人は以下の記事を読んでみるといいかもしれません。
- Introduction to Browserify • Blake Embrey
- browserifyの入門的な記事ですが、
-t
の変換レイヤーやオプションについても書かれていてまとまっています
- browserifyの入門的な記事ですが、
- Client-Side JavaScript Management, Browserify vs Component
- ちょっと古めですが、同じCommonJSを扱うcomponentとの比較について書かれています。
- JavaScript Dependencies, Modules & Browserify
- JavaScriptの色々なモジュールの書き方とbrowserifyについて書かれています。
- 他のモジュールから変換して扱う方法(P52)などよくまとまってます。
- RequireBin
- JSFiddleみたいにbrowserifyを使ったコードを試せるサービス
この記事はモジュール管理の比較記事ではないので、以下のような内容について書いていきます。
- bowerでインストールしたものをbrowserifyで使う
- npmで公開したものをbrowserifyで使う
- browserifyでのビルドサイクル(beefy or gulp)
- browserifyでビルドしたもののデバッグ
browserifyでのクライアントサイド開発
実際に作ったものは azu/tech-video-rss-searcher に公開してあります。
CommonJS以外のモジュールをbrowserifyで使う
今回、npm以外のモジュールとしてはbower経由でRactive.jsを使っただけでしたが、そのままでは<script>
で読み込んで使うことを想定した作りになっているため、nodeスタイルで書いたコードから使いにくいです。(require("ractive")
で使えない)
そこで、browserifyでは -t
オプションでnodeスタイルで書かれたコードを変換する際に、別の変換を組み合わせられるtransformsという仕組みが用意されています。
今回はbowerでインストールしたものを変換したかったのでdebowerifyという変換モジュールを使います。
browserifyで変換するときに、-t debowerify
と指定する事でnodeスタイルから扱えるように変換されてbundle.jsにまとめられます。
$ browserify -t debowerify app/app.js -o public/budle.js
例
リポジトリのbower.jsonを見ると ractive
をインストールしています。
これをモジュールとして扱う場合は、以下のようにbower.jsonのパッケージ名に合わせたものを require
することで扱えます。
var Ractive = require("ractive");
debowerifyによって、bowerでインストールしたものがCommonJSから扱えるように変換されbundleに含まれます。
bower以外にもAMDを変換するdeamdifyや、ES6モジュールを変換するes6ify、ブラウザ向けに書かれた window.
に対して名前空間を持つようなライブラリを変換するdeglobalify / browserify-shim 等があります。
list of source transforms に変換モジュールがまとめられています、意外と充実しているためブラウザ向けに公開されている大体のライブラリはbrowserifyに持ってきて使う仕組みがあると思います。
また、これらの変換モジュールは、基本的にJavaScript ASTを変更することで行われるため、
後述するデバッグで使うSourcemapを破壊せずに変換出来ます。
簡単にいうと、browserifyで変換 + 変換モジュールでの変換 をしても元のライブラリソースとのマッピングは壊れないので、変換したものが普通にデバッグ出来るということです。
最近はjQuery等もnpmでインストールできますが、ブラウザ向けのライブラリがnpmでも公開されてるものはそこまで多くないので、ライブラリを使おうと思ったら恐らくこういう変換モジュールをかませる必要がでてくると思います。
npmで公開してるものをbrowserifyで使う
次はbrowserifyの機能としてよく紹介されてると思いますが、
npm経由でインストールしたnode向けに書かれたモジュールもbrowserifyでは変換することができます。
ブラウザに存在しない機能(OSのファイルシステムや外部プロセスを呼ぶ等)やshimがないモジュールは扱うことが出来ませんが、他は大体動作すると思います。
NPM Everywhere by Azer Koçulu等でも書かれていますが、npmという既にあるエコシステムに乗れる事がbrowserifyの利点の一つだと思います。
既に公開されてるnodeモジュールを使うこともそうですが、
npmに関するツールは充実しているので、作ってるウェブアプリのロジックをライブラリとして切り出してnpmで公開して使うような事がやりやすいです。
例
今回のアプリでは、OPML形式で出力したかったので、
そのOPML形式に変換する部分だけをopml-generator.jsというnodeモジュールを作って使っています。
opml-generator.jsはただのNode.jsで動く文字列処理をするようなライブラリなので、他のNodeプロジェクトでも使えますし、今回のアプリもdependenciesとして読み込みbrowserifyで変換して利用しています。
browserifyでのビルドサイクル(beefy or gulp)
browserifyのデメリット?としてブラウザで実行するにはビルドが必要です。
browserify
コマンドで変更する度に手動で変換するのは面倒なので、Gruntやgulp等のビルドツールを利用すると思います。
(単純にファイル監視+ビルドだけならwatchifyも利用できます)
今回は、beefyを使った方法とビルドツールのgulpを使った方法の両方を使えるようにしてみました(どっちがいいのかわからなかったので両方出来るようにしたという話)
beefy
beefy は ファイルの変更監視 + browserifyのビルド + ローカルサーバ (+ LiveReload) を行うコマンドラインツールです。
例えば、browserifyコマンドで以下のようにビルドする時、
browserify -t debowerify app/app.js -o public/app.js
これをbeefyで自動化するには、以下のようにコマンドを実行します。
beefy app/app.js:public/app.js 8989 -- -t debowerify
ポート8989でローカルサーバが立つので、 http://localhost:8989/
にアクセスする事で自動でビルドされるようになります。
ローカルサーバを立てると後述するデバッグにも役立つので、その点beefyはローカルサーバの部分も自動でできるのでお手軽です。(そもそもXHRとかはhttpじゃないと上手く動かない場合があるので、こうした方がいい)
gulp
追記: gulpでBrowserifyを使う場合は次の記事を参照して下さい。
- Gulp + Browserify: The Everything Post | Viget
- gulp/docs/recipes/fast-browserify-builds-with-watchify.md at master · gulpjs/gulp
Browserifyは元々streamに対応しているため、gulp pluginを別途用意する必要がなく、browserify
をモジュールとしてそのまま利用できます。
以下のやり方は非推奨です。
gulpからbrowserifyを使うpluginとしてgulp-browserifyがあるので、これを利用します。
詳細はgulpfile.jsをみてもらうとわかりますが、 connect
を使ったローカルサーバとgulpを使ったファイル監視をして gulp-browserify
でビルドという感じです。
気をつける点としてはgulp-browserifyで変換する際にストリームで変換されるので、そのままでsourcemapのファイル名がfake-hogeのようになってデバッグできなくなります。
standalone: “app.js” というオプションでbrowserifyのentry-point(mainとなるファイル)を渡すことで正しくマッピングされると思います。
@7to3 あ、ほんとですね。{ read: false } の場合standalone無くても大丈夫ですね。
ありがとうございます。— azu (@azu_re) January 21, 2014
追記: gulp.srcに { read: false }
を指定している場合は standalone
オプション必要ありませんでした。
(hughsk/vinyl-source-streamというpluginを組み合わせた方がスマートかもしれません)standalone
という謎オプションを使わなくていいので
これでbeefyと大体似たようなファイル監視+ローカルサーバ+ビルドができると思います。
browserifyでビルドしたもののデバッグ
先ほどから何回か出てきていますが、browserifyでは --debug -d
オプションを付けることでsource mapも一緒に吐き出してくれます。(beefy、gulp-browserifyにもそれぞれオプションで指定出来ます)
これを使えば、browserifyでビルドしたものもデバッグすることが出来ます。
おー、beefyでローカルサーバ立ててWebStormでそのlocalhostに対してデバッグ実行させれば、
browserify + debowerify に対してもWebStormからブレークポイント貼って見られる pic.twitter.com/ZxdnAWuLHw
— azu (@azu_re) January 17, 2014
source map
source mapに対応してるFirefoxやChromeのようなブラウザなら、browserifyでデバッグビルドしたものを表示した場合、
以下のように元となるNode.jsのshimや変換元のモジュールのソース等が見られブレークポイント等も使えるので普通にデバッグが行えます。
WebStormでも同じようにJavaScriptデバッガーを使ってデバッグする事が出来ます。
Run ConfigurationからJavaScript Debugで先ほど立てたローカルサーバへアクセスするようにするだけです。
これにより、普通にJavaScriptを書く時と殆ど感じでデバッグが出来ます。
Tech Video RSS Searcher ではbeefyを使って、上記のようにWebStormからデバッグしていましたが、特に問題なくブレークポイントを貼ってチェックしたりできて変換フェーズがあるといってもそこまで問題はない感じでした。
おわりに
この記事では browserify をつかったウェブアプリの開発フローについて書きました。
browserifyのドキュメントはあんまり充実してない感じ(ドキュメントというよりはチュートリアル的な方向)がするので、少し手を出しにくいかもしれませんが、Nodeの資産やツールも適応しやすく意外と現実的に開発できるような環境だと思います。
ブラウザでのモジュールといえばECMAScript 6 modulesですが、こちらもES6 Module Transpilerなどを使っていますぐ使うことも出来ます。
実際にjQuery Evergreenやlodash-es6、backburner.jsなどES6 modulesを実際に使って書かれてるライブラリ等もあります。(プロダクトはさすがに見たこと無い)
ES6 modulesも現状ではビルドして使う感じになるという点では同じですが、browserifyの場合は既にNode.jsで使われてるCommonJSというスタイルを使うので、エディタやツールのサポートがあるという状態になってるのはいいことだと思います。
ES6 modulesで増える構文はcontextual keywordsだったと思いますが、その構文にちゃんと対応してるエディタはまだなかったと思います。(WebStorm 8で入るかもしれないですが)
また今回は全然触れてませんがプロダクション的な用途を考えるならファイルサイズも気になる場合があるかもしれません。
shim は利用した機能の分だけが自動でビルド時に含まれてるようになってるので、素で書いた場合に比べて(デバッグビルドをしなければ)大幅に増えるという感じではないですが、npm経由で入れたモジュールの依存モジュールも引っ張って変換されるので、下手にnpmで入れるといきなりファイルサイズが増える場合があるかもしれません。
ビルドにかかる時間の省略やキャッシュ的にも、依存する外部ライブラリは開発するソースとは別々のbundle.jsとして生成するなどの方法(--noparse
というオプションもある)を取れるかもしれません。(その辺についてはやった人が書くといいよ!)
今度こそおわりに
browserify はとっつきにくいイメージがありますが、触ると楽しいです。
以上です。
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。