Browserify使いはじめてしばらくたって、自分なりのコツがつかめてきた気がするのでまとめてみます。
まず今回のコンテキストとしては画面遷移をそれなりに含み、同じモジュールを各画面で使う前提。
SPAだとちょっと考え方が変わるかもしれません。
【対策以前】全部をbundleすると太る
Browserify覚えてしばらくは、ページ毎の実行部分も含めてbundle.jsにひとまとめしてたけど、各ページ共通部分があるにもかかわらず別ファイルになってしまい、ブラウザキャッシュも生かせず、ファイルサイズも肥大化して微妙だなぁと感じていました。ビルドも毎回でgulpタスクも重くなりがちでした。
- 共通部分も別々のファイル
- ブラウザキャッシュ活かせない
- ファイルサイズ肥大化
- ビルド重め
ファイル配置例
.
├── node_modules
│ ├── knockout
│ └── underscore
├── public
│ ├── index.html
│ └── static
│ └── js
│ ├── bundle1.js
│ └── bundle2.js // bundle1と同じものが多く含まれている!
└── src
├── MyModule.js // 自作モジュール
├── page1.js // ページ固有のロジック
└── page2.js // ページ固有のロジック
src/app.js
// src/page*.js
(function(){
var ko = require('knockout');
var _ = require('underscore');
var myModule = reqire('./src/MyModule.js');
// ページ固有の処理
})()
browserifyコマンド
// app.jsそのものをbundle
browserify src/page1.js > public/static/js/bundle1.js
// 別ページなら別bundle
browserify src/page2.js > public/static/js/bundle2.js
【対策】共有部分だけbundle
browserifyはbundleしたモジュール類を他のファイルからrequireで呼びだせるので、共通モジュールをbundleしたファイルと、各ページ固有ロジックを記述したファイルにわけることにしました。
そうすることで共有ファイルはブラウザキャッシュを活かせるし、各ページ固有のファイルサイズは小さくなります。requireするものが全部共有ファイルに入っているならば、ビルドも必要ありません。
- 共通部分だけを一つのbundleファイルにする
- 他のscriptからrequireで呼び出せる
- ブラウザキャッシュ活かせる
- ファイルサイズ縮小
- ビルド頻度・重さ減少
ファイル配置例
.
├── node_modules
│ ├── knockout
│ └── underscore
├── public
│ ├── index.html
│ └── static
│ └── js
│ ├── bundle.js
│ ├── page1.js
│ └── page2.js
└── src
└── MyModule.js
今回の例だとapp.jsでrequireするファイルはすべてbundle.jsに入っているので、app.jsをbrowserifyにかける必要はありません。そのままpublic/staic/js
に配置します。
browserifyコマンド
外部からrequireできるbundleを作る方法です。自作モジュール(src/MyModule.js
)にモジュール名を割り当ててあげることで、モジュール名で呼び出せるようになるのがポイントです。
browserify -r knockout -r underscore -r ./src/MyModule.js:MyModule > public/static/js/bundle.js
同じことをgulpタスクで書くと以下。やはり{expose: 'MyModule'}
で自作モジュールに名前をつけてあげるのがポイントです。
gulp taskバージョン
var browserify = require('browserify');
var source = require('vinyl-source-stream');
gulp.task('build', function() {
return browserify()
.require('underscore')
.require('knockout')
.require('./src/MyModule.js', {expose: 'MyModule'})
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest('./public/static/js/'));
});
public/static/js/page*.js
// src/app.js
(function(){
var ko = require('knockout'); // requireが使える
var _ = require('underscore'); // bundle.jsから読んでいる
var MyModule = reqire('MyModule'); //設定した名前で呼び出せる
// ページ固有の処理
})()
htmlで呼び出し
<script src="static/js/bundle.js"></script>
<script src="static/js/page*.js"></script>
別ファイルからrequireする方法はBrowserifyのDocumentの割と最初のほうに書いてあるので、最初からちゃんと読み込んで使っていればこうしてたかな、という感じもしますが最近やっと気がついたので。
もっといい運用あるよとか、ご意見ありましたらお願いします。