Browserifyの使い方について調べてみた
最近Browserifyって単語を良く見るようになりました。
Browserifyをざっくり説明すると、
- ブラウザ上でもNode.js用モジュールを使えるようにする
- ブラウザでもrequire()を使ったモジュール管理を使えるようにする
という特徴があるようです。
browser(ブラウザ)+ fy(〜する)という単語からもNode.jsをブラウザ化するってニュアンスでしょうか。
require()といえばRequireJSも提供してますよね。
Browserify使うことでRequireJSを使わなくてもモジュール管理ができるようになりそうです。
Browserifyの使い方
まずは公式サイトに載ってるコードを試してみます。
browserify
これはどうやらNode.js向けのモジュールをブラウザでも使えるようにするチュートリアルのようです。
まず作業ディレクトリを作成します。
$ cd path/to/workspace $ mkdir browserify # browserifyを試す用のディレクトリ $ cd browserify
このディレクトリ直下に main.js を作成します。
// @file main.js var foo = require('./foo'); var gamma = require('gamma'); var n = gamma(foo(5) * 3); var txt = document.createTextNode(n); document.body.appendChild(txt);
foo.jsをmain.jsと同じ階層に作成します。
// @file foo.js module.exports = function (n) { // 外部から呼べるようにexportsする return n * 11; };
Node.jsな書き方ですね。
main.jsではgammaというモジュールも読み込んでいるのでインストールします。
$ npm install gamma .. gamma@0.1.0 node_modules/gamma
このままではmain.jsは動きません。
browserifyを使ってファイルを結合する必要があります。
browserifyコマンドが使えるようにグローバルにインストールします。
$ npm install -g browserify # グローバルにインストールする
-o オプションをつけてファイルの結合をします。
このタイミングで依存関係があるファイルを検出し、
ブラウザでも実行できる形に生成するようです。
$ browserify main.js -o bundle.js # bundle.jsという名前のファイルに書き出す
生成された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,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].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=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ // @file foo.js module.exports = function (n) { // 外部から呼べるようにexportsする return n * 11; }; },{}],2:[function(require,module,exports){ // @file main.js var foo = require('./foo'); var gamma = require('gamma'); var n = gamma(foo(5) * 3); var txt = document.createTextNode(n); document.body.appendChild(txt); },{"./foo":1,"gamma":3}],3:[function(require,module,exports){ // transliterated from the python snippet here: // http://en.wikipedia.org/wiki/Lanczos_approximation var g = 7; var p = [ 0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7 ]; var g_ln = 607/128; var p_ln = [ 0.99999999999999709182, 57.156235665862923517, -59.597960355475491248, 14.136097974741747174, -0.49191381609762019978, 0.33994649984811888699e-4, 0.46523628927048575665e-4, -0.98374475304879564677e-4, 0.15808870322491248884e-3, -0.21026444172410488319e-3, 0.21743961811521264320e-3, -0.16431810653676389022e-3, 0.84418223983852743293e-4, -0.26190838401581408670e-4, 0.36899182659531622704e-5 ]; // Spouge approximation (suitable for large arguments) function lngamma(z) { if(z < 0) return Number('0/0'); var x = p_ln[0]; for(var i = p_ln.length - 1; i > 0; --i) x += p_ln[i] / (z + i); var t = z + g_ln + 0.5; return .5*Math.log(2*Math.PI)+(z+.5)*Math.log(t)-t+Math.log(x)-Math.log(z); } module.exports = function gamma (z) { if (z < 0.5) { return Math.PI / (Math.sin(Math.PI * z) * gamma(1 - z)); } else if(z > 100) return Math.exp(lngamma(z)); else { z -= 1; var x = p[0]; for (var i = 1; i < g + 2; i++) { x += p[i] / (z + i); } var t = z + g + 0.5; return Math.sqrt(2 * Math.PI) * Math.pow(t, z + 0.5) * Math.exp(-t) * x ; } }; module.exports.log = lngamma; },{}]},{},[2])
生成されたbundle.jsをHTMLのscriptタグで読み込むことで
ブラウザでもNode.jsのモジュールが使えることになります。
Browserifyでモジュール管理する
実際はNode.jsのモジュールをブラウザ化するよりも、
フロントのJSをモジュール毎に管理する用途で使われることの方が多いようです。
Browserifyを使った開発をするとなると、browserifyコマンドを実行した後に
生成されるコードでなければ実行できないことになります。
でもいちいちコードを修正してターミナルからbrowserifyコマンドを叩くなんてしたくありません。
そういう面倒くさいことは人間がやるんじゃなくてGrunt.jsにさせましょう。
Grunt.jsはJenkinsのフロント版みたいなやつです。
Grunt: The JavaScript Task Runner
ちなみにGrunt.jsの競合として最近gulp.jsが出てきました。
gulp.js - the streaming build system
後発らしく、Grunt.js使ってて不満な部分を解消していますが、
まだGruntよりも対応するプラグインが少ないのが現状。
いずれはGruntに取って代わる気がする。
今回はGrunt.jsを使っていきます。
まだGrunt.jsが入っていなければインストールします。
$ npm init # package.jsonを作っておく。 .. # (適当にエンターを押す) $ npm install -g grunt-cli # グローバルにインストール. gruntコマンドが使えるようになる .. $ npm install grunt --save-dev # 本体をインストール .. $ grunt --version # バージョン確認 grunt-cli v0.1.13 grunt v0.4.3
他にもGrunt用のプラグインをいくつかインストールしておきます。
$ npm install grunt-browserify grunt-contrib-watch --save-dev
アプリケーションのルートディレクトリにGruntfile.jsを作成して、設定を記述します。
// @file Gruntfile.js module.exports = function (grunt) { var pkg = grunt.file.readJSON('package.json'); grunt.initConfig({ browserify : { // タスク名. $ grunt browserify で実行できる dist : { src : 'src/main.js', // エントリーポイントとなるファイル dest : 'build.js' // 出力するファイル名 } }, watch : { // タスク名. $ grunt watch で実行できる scripts : { files : ['src/**/*.js'], // 監視対象のファイル tasks : ['browserify'] // 変更があったら呼ばれるタスク } } }); // grunt関連のプラグインはpackage.jsonに記述されたものを読み込む Object.keys(pkg.devDependencies).forEach(function (devDependency) { if (devDependency.match(/^grunt\-/)) { grunt.loadNpmTasks(devDependency); } }); // $ grunt で実行するタスクに watch を指定 grunt.registerTask('default', ['watch']); };
Gruntfile.jsをよく見るとわかりますが、srcディレクトリとdistディレクトリを作成しました。
srcにはmain.jsとfoo.jsが入ってます。
この状態でgruntコマンドを叩くと
$ grunt Running "watch" task Waiting...
と、ファイルの監視が始まります。
では新しくsrc/hello.jsというファイルを追加してみます。
// @file hello.js /** * 引数に与えられた文字列の先頭にHello, を追加する * @param {String} str * @return {Strint} */ function hello(str) { return 'Hello, ' + str; } module.exports = hello;
src/main.jsに修正を加えます。
// @file main.js var foo = require('./foo'); var hello = require('./hello'); // 追記 var gamma = require('gamma'); var n = gamma(foo(5) * 3); var txt = document.createTextNode(hello(n)); // 追記 document.body.appendChild(txt);
index.htmlを用意します。
<!DOCTYPE html> <html> <head> <title>browserify test</title> </head> <body> <!-- build.js はbodyの最後で読み込む --> <script src="dist/build.js"></script> </body> </html>
index.htmlをブラウザで開くと
Hello, 3.287218585534316e+293
という謎の文字列が表示されることが確認できます。
まとめ
- browserifyを使うとNode.jsで使うモジュールをフロントの開発でも使うことができる
- module.exports または exports で参照を外出しすることで、他のモジュールからはrequire()で参照を得られる
- gruntやgulpも同時に使わないと開発が捗らない
おわりに
コードの規模が大きくなると、browserifyによって生成されるコードが大きくなるんじゃないかという懸念がありますが、たぶんビルドの設定で何とかなるんでしょう。
ここらへんはもう少し調査が必要。
あと深く調べられたわけじゃないので入門記事みたいになった。
Grunt.jsをブログで扱うのが何気に初めてでしたが、もっといろいろできるのでまた登場する予定。