Browserifyの上級指定教科書『Browserify Handbook』を読みすすめる
動機
ハリーポッターの作品中には『幻の動物とその生息地』のようにホグワーツ指定の教科書があります。
Browerifyも同様に、Browserifyを学ぶにあたって必要な上級教科書『Browserify Handbook』があります。 上級といっても非常にシンプルにわかりやすく解説されているので、エンジニア1年生でも読み解くことはできるでしょう。
ただ、Browerify自体もあまり日本語記事がなく、この時期だとWebpackの台頭でもうほとんど使っている人がいないかもしれませんが、Webpackのさらなる理解のためにも、今回書き起こしておこうと思いました。
やはり、ドキュメントをしっかり読んでこそ理解できるのでHandbookなるものがあるのならばしっかり読み、基礎ができていないと怒られないようにします。
それでは解説を始めていこうと思います。
アジェンダ
今回の記事のアジェンダはこちらになります。
Browserify
まずはBrowserifyについて考えて行きましょう。
Browserifyとは
Browserifyとは、
Browserify lets you require('modules') in the browser by bundling up all of your dependencies.
と公式ページにもあるように、依存関係をまとめてくれるもので、またブラウザ上でもrequire
という書き方を可能にしてくれます。
またNode.jsで使っていたライブラリをフロントエンドでもrequire
として使うことができるようになるためサーバーサイドのNode.jsを開発しているときに使っていたnpm
を同じような感覚で使うことができます。
Browserifyの使い方
このセクションは、あくまでも基本的な使い方、仕組みを解説しているだけです。
Hello Worldでの実例
例えば、以下のようにuniq
ライブラリを使ったmain.js
があるとします。
var unique = require('uniq'); var data = [1, 2, 2, 3, 4, 5, 5, 5, 6]; console.log(unique(data));
uniq
をインストールしなければならないので以下のようにインストールをします。
npm install uniq
そして、あとは魔法を唱えるだけです。
実際には以下のようにmain.js
と、その依存パッケージであるuniq
をbundle.js
ファイルをしてまとめます。
browserify main.js -o bundle.js
なお、-o
を指定しない場合は標準出力されるため
browserify main.js > bundle.js
とすることも可能です。
するとHTML内では<body>
タグの最後に
<script src="bundle.js"></script>
として書くことができてuniq
について心配する必要がありません。
普通ならば
<script src="url/uniq"></script> <script src="main.js"></script>
と書かなければエラーとなったでしょう。そもそそブラウザではNode.jsで使うようなrequire
は使うことはできないので、以下のようなエラーがでます。
これが、例えば
var foo = require('./foo.js'); var bar = require('../lib/bar.js'); var gamma = require('gamma'); var elem = document.getElementById('result'); var x = foo(100) + bar('baz'); elem.textContent = gamma(x);
のようになっていたとしても、browserifyによってrequire
を解析してくれ、まとめてくれます。
軽くサンプルはわかったかなと思います。少し体系的に調査をしたいと思います。
どのようにbundleされるのか
例えば、以下のような依存関係のあるJavascriptファイルを作成してみましょう。
a.js
module.export.sayYourName = function (){ console.log("A") }
b.js
module.export.sayYourName = function (){ console.log("B") }
main.js
const a = require('./a.js') const b = require('./b.js') function sayYourNames() { a.sayYourName() b.sayYourName() } sayYourNames() // Note: 今だけ追加
これを実行すると
node main.js => A B
という結果になります。図示すると以下のような関係になっています。
さきほどの関数をexport
しておきましょう。
module.exports.sayYourNames = function (){ ... }
これを以下のように出力します。
browserify main.js > bundle.js
図では、このようになったというイメージができます。
これを<script>
で入れてみて、ChromeのDevToolのSourceタブでみてみます。
するとすると以下のようになっていました。
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ module.exports.sayYourName = function (){ console.log("A") } },{}],2:[function(require,module,exports){ module.exports.sayYourName = function (){ console.log("B") } },{}],3:[function(require,module,exports){ const a = require('./a') const b = require('./b') module.exports.sayYourNames = function(){ a.sayYourName() b.sayYourName() } },{"./a":1,"./b":2}]},{},[3]);
コンパクトに一つにまとめられているようです。
主要なオプション
--plugin, -p
: プラグインを指定して使用する--require, -r
:bundle.require()
で使うファイルを指定--outfile, -o
: 指定したファイルに出力をする--external, -x
: 他のBundleからファイルを参照する
外部Require
バンドルするファイルでrequire()
しているパッケージからもrequire()
として公開することができます。
例えば、前の例のa.js
、b.js
、それに依存するmain.js
を使用するとしましょう。ここで外部ライブラリchroma-js
を追加しました。
const a = require('./a') const b = require('./b') const chroma = require('chroma-js') module.exports.sayYourNames = function(){ a.sayYourName() b.sayYourName() }
そして、今回はこのmain.js
でchroma-js
を使いたいだけでなくて、HTMLの別の<script>
タグでも使用したいので、以下のように外部公開をします。
browserify index.js --require chroma-js > bundle.js
そして、以下のようにインラインスクリプト内でrequire()
しても使うことができます。
<body> <p>Hello, World</p> <script src="bundle.js"></script> <script> var chroma = require('chroma-js') </script> </body>
非常に簡単にNode.jsで使っていたライブラリを使うことができます。
Multi Bundle
ここまでは単一bundleファイルにまとめて利益を得ていました。
例えばAdminページとPublicページがあるときに、必要なJavascriptファイルは異なる可能性が高いです。
それなのに、一つにまとめてしまうと、あらゆるところで必要のないファイルを読み込んでしまい、それがコストとなり、Webページの読み込みが遅くなる原因にもなります。
なるべく、必要なJavascriptファイルは必要なページで読み込むようにします。図の例だと、単一ファイルで依存関係が見えないのでメリットがわかりづらいですが、要点は捉えていると思います。
あるファイルadmin.js
に以下のようなファイルがあります。
var robot = require('./robot.js'); console.log(robot('beep'));
そしてpublic.js
に次のようにします。
var robot = require('./robot.js'); console.log(robot('public'));
そして両方が依存しているrobot.js
は以下のようになっています。
module.exports = function (s){ return s.toUpperCase() + '!' };
そして、以下のように実行します。
// 「外部Require」のようにrobotを公開します $ browserify -r ./robot.js > static/bundle.common.js $ browserify -x ./robot.js admin.js > static/bundle.admin.js $ browserify -x ./robot.js public.js > static/bundle.public.js
二つのページには、一つはbeep
を必要とするファイルを以下のように
<script src="common.js"></script> <script src="beep.js"><script>
して、もうひとつはboop
を必要とするファイルを以下のようにして使うことができます。
<script src="common.js"></script> <script src="boop.js"><script>
これは二つのステップで構成されています。
-r
によって外部requireとして公開しつつもcommon.js
を作成する-x
で他のBundleファイルを参照しつつ、共通要素を取り除く
上級指定教科書『Browerify Handbook』
それでは本題のドキュメントを読んでいきます。
実際のドキュメントは以下のURLです。
すべてのセクションは取り上げませんが、いくつか有用なものを解説していこうと思います。
章わけをしていますが、雰囲気を出すためであり、ドキュメントはMarkdownで書かれています。
1章: Browserifyの基礎。Hello, worldと他の出力
また、基礎の確認になります。library.js
に以下のようなファイルがあったとします。
module.exports = function (name){ return 'Hello, world ' + name; };
そしてmain.js
を以下のようにします。
var library = require('library.js'); library('I am Keke');
すると以下のような出力があります。
Hello, world I am Keke
また、関数だけでなくmodule.exports
で何もかも出力ができます。先ほどのlibrary.js
を以下のように
module.exports = 'Hello directly'
とすることもできます。
もちろんexports
でも渡すことはできますが、これはBrowerifyに限った話ではないので割愛させていただきます。
2章: Auto Recompile
コードの変更を監視して、自動的にbundleをし直してくれるパッケージがあります。
watchify
: もっともスタンダードなパッケージbeefy
: Webサーバーを立てて自動でバンドルしてくれるwzrd
:beefy
に機能は似ているが、より軽量なパッケージ
他にもいくつかありますが、ここではこの3つだけの紹介にします。
3章: Browerify APIを直接使う
Browserify APIを使って、JavascriptからNode.jsで実行してしまうパターンです。
var browserify = require('browserify'); var b = browserify(); b.add('./browser/main.js'); b.bundle().pipe(process.stdout);
GulpやGruntで使用することができます。
4章: Non Javascriptアセットの変換
Broweserify transformを使って、いろんな非Javascriptファイルをbundleファイルの中に入れることができます。
もっとも簡単なのがbrfs
を使うことです。
browserify -t brfs main.js > bundle.js
をすると、ファイルをそのまま取得することができます。
var html = fs.readFileSync(__dirname + 'robot.html', 'utf8');
は
var html = "<p>Hello</p>"
と同じになります。bundleファイル内で取得することができます。
もちろん、CSSも取り込むこともできますし、まだまだいろんな種類のファイルをbundleの中に取り込めます。
var fs = require('fs'); var insertStyle = require('insert-css'); var css = fs.readFileSync(__dirname + '/style.css', 'utf8'); insertStyle(css);
5章: Bundlingを完全にマスターする
ここではBundlingについて、もうすこしだけ詳しく書いていこうと思います。
容量を抑える
npmでは、重複した依存関係はインストールするパッケージが増えるごとに自動的は排除されず、npm dedupe
によって行うことができます。それかnode_modules/
を一旦、削除して再度インストールするという手があります。
Browserifyは同じファイルを二度使うことはありませんが、バージョンが別だとうまくはいきません。
そのようなときは
browserify --list
と
browserify --deps
を行って重複に対して調査をしてみてください。
tinify
tinifyプラグインを用いてさらに容量を抑えることができます。
browserify hoge.js --plugin tinify > bundle.js
tinifyとはBrowserifyのpluginで、いくつもの最適化をしてくれるものです。
詳しい内容は上記のリポジトリでご覧ください。
特定のモジュールを空オブジェクトで置き換える: Ignore
ある特定のモジョールを使いたくない時は以下のように
browserify --ignore chroma-js
と指定すると無視をすることができる。
特定のファイルを取り除く: Exclude
例えばすでに他のライブラリに取り込まれている時、別のbundleではrequire()
ができないようにしたい時があるかもしれません。
図示すると以下のような状況下のときです。
例えばmain.js
でjQuery
を使っていたとします。
var $ = require('jquery'); $(window).click(function () { document.body.bgColor = 'red' });
そしてすでに
browserify -r jquery > bundle.jquery.js
で使っているときに再度、jqueryを入れたくないので
browserify main.js --exclude jquery > bundle.js
とすることによって重複を避けることができます。APIからは
b.exclude('hoge')
のように使用することができます。
6章: Transform
Javascriptの仕様や、CoffeeScriptなどを変換してくれるものです。他にもCSS飲めた言語を変換してくれたり多機能です。
変換できるものは、以下のリンクにありますが、膨大な数ほどあります。
例えば、ここでCoffeeScriptの簡単なスクリプトを書いてみましょう。
hello = () -> console.log("Hello from Coffee Script") hello()
そして以下のようにバンドルします。
$ npm install coffeeify coffeescript $ browserify -t coffeeify main.coffee > bundle.js
すると以下のように出力がされます。
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ var hello; hello = function() { return console.log("Hello from Coffee Script"); }; hello(); },{}]},{},[1]);
まとめ
今回の記事でBrowserifyへの理解が深まり、より簡単にアプリケーションを効率よく使うことができそうです。
次はWebpackについて記事をまとめたいと思います。
ありがとうございました。