backbone-boilerplateではじめるHTML5アプリケーション開発 その3

backbone-boilerplateが提供するビルド

f:id:qualitas:20140521042907p:plain

backbone-boilerplateの特徴の1つとして、ビルドプロセスがしっかり整備されていることがあげられます。今回は、backbone-boilerplateが用意しているビルドの内容について、見てみます。

backbone-boilerplateのビルドの内容を理解することで、Javascriptアプリケーションのビルドの方式について、ひととおり理解することができると思います。

backbone-boilerplateのビルドでやっていること

backbone-boilerplateはビルドにgruntを利用しており、デフォルトで、以下のタスクを実行するように設定されています。

Gruntfile.js

  // When running the default Grunt command, just lint the code.
  grunt.registerTask("default", [
    "clean",
    "jshint",
    "processhtml",
    "copy",
    "requirejs",
    "styles",
    "cssmin",
    //"compress",
  ]);
jshintタスク

JSHintを利用してappフォルダ配下のJavascriptソースの静的解析を実施します。
grunt からJSHintを実行するためのプラグイン grunt-contrib-jshint を利用しています。
gruntjs/grunt-contrib-jshint · GitHub


boilerplate内のjsファイルに対してjshintを行っても何もエラーはでませんが、ためしに、github-viewer/app/components/user/index.js に以下のようなコードを追記して grunt jshint を実行すると、

  var i = 0

以下のように、セミコロンがないことを指摘されます。

Running "jshint:0" (jshint) task
Linting app/components/user/index.js ...ERROR
[L3:C12] W033: Missing semicolon.
  var i = 0

Warning: Task "jshint:0" failed. Use --force to continue.

Aborted due to warnings.

実際の開発では、 .jshintrc を定義してJSHintのオプションを設定する必要があると思います。

processhtmlタスク

これは、htmlファイルの内容をリリース向けに変更するための処理を行うためのタスクです。
grunt-processhtml を利用して実行しています。

具体的には、index.html の内容を変更しています。
開発時にローカルPCから参照されるindex.html (例: github-viewer/index.html)は、以下のような内容になっています。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width,initial-scale=1">

  <title>GitHub Viewer</title>

  <!-- Application styles. -->
  <!-- build:[href] /styles.min.css -->
  <link rel="stylesheet" href="/app/styles/index.css">
  <!-- /build -->
</head>

<body>
  <!-- Application container. -->
  <main role="main" id="main"></main>

  <!-- Application source. -->
  <!-- build:[src] /source.min.js -->
  <script data-main="/app/main" src="/vendor/bower/requirejs/require.js"></script>
  <!-- /build -->
</body>
</html>

これは、リリースビルド後の dist/index.html だと、以下のように変更されています。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width,initial-scale=1">

  <title>GitHub Viewer</title>

  <!-- Application styles. -->
  <link rel="stylesheet" href="/styles.min.css">
</head>

<body>
  <!-- Application container. -->
  <main role="main" id="main"></main>

  <!-- Application source. -->
  <script data-main="/app/main" src="/source.min.js"></script>
</body>
</html>

/app/styles/index.css が、 /styles.min.css に変更されています。
また、 /vendor/bower/requirejs/require.js が、 /source.min.js になっています。
processhtmlが、index.htmlのコメントを参照して、ビルド時に変換してくれています。

  <!-- build:[href] /styles.min.css -->
  <link rel="stylesheet" href="/app/styles/index.css">

...

  <!-- build:[src] /source.min.js -->
  <script data-main="/app/main" src="/vendor/bower/requirejs/require.js"></script>

開発時とリリース時でindex.htmlの内容を変更したいことはよくあると思います。
場合によっては、index.htmlを2重管理したりとか。。
processhtmlを使えば、そういう問題に対応できます。

requirejsタスク

RequireJSを利用して分割したjsファイルを、1つのjsファイルに結合します。ソース内の改行や空白文字の除去(ソース圧縮)も実施されます。
これは、r.js を利用して行われます。また、AMD loaderとしてalmondが指定されています。

    // This task uses James Burke's excellent r.js AMD builder to take all
    // modules and concatenate them into a single file.
    requirejs: {
      release: {
        options: {
          mainConfigFile: "app/config.js",
          generateSourceMaps: true,
          include: ["main"],
          insertRequire: ["main"],
          out: "dist/source.min.js",
          optimize: "uglify2",

          // Since we bootstrap with nested `require` calls this option allows
          // R.js to find them.
          findNestedDependencies: true,

          // Include a minimal AMD implementation shim.
          name: "almond",

          // Setting the base url to the distribution directory allows the
          // Uglify minification process to correctly map paths for Source
          // Maps.
          baseUrl: "dist/app",

          // Wrap everything in an IIFE.
          wrap: true,

          // Do not preserve any license comments when working with source
          // maps.  These options are incompatible.
          preserveLicenseComments: false
        }
      }
    },

Lo-Dash Template Loader を利用してテンプレートファイルを呼び出しをしている箇所については、そのテンプレートファイルもjsファイル内に結合されます。

これらの処理は、backbone-boilerplateで用意されている、grunt-bbb-requirejs を利用して行われます。
grunt-bbb-requirejsは、grunt-contrib-requirejs と同じ目的をもつプラグインで、実際、grunt-contrib-requirejsからフォークされて開発されたプラグインですが、ソースは完全に異なっていて、処理内容にはいくつか違いがあるようです。

なお、Source Mapsファイル(source.min.js.map)も生成してくれます。
リリースビルドされたアプリを実行していても、Chromeデベロッパツールでは最適化前のソースの内容を確認することができます。

stylesタスク

style.css内の @import で指定された別のcssファイルの内容を、style.cssに結合します。

/*-- Bootstrap. -------------------------------------------------------------*/

@import "../../vendor/bower/bootstrap/dist/css/bootstrap.css";

/*-- Application stylesheets. -----------------------------------------------*/

@import "app.styl";

ビルド後、以下のように、bootstrap.cssの内容が埋め込まれた形になります。

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {display: block;}
audio,
canvas,
video {display: inline-block;}
audio:not([controls]) {display: none; height: 0;}

...省略....

この処理は、grunt-bbb-styles で実施されています。

cssminタスク

style.css の内容を圧縮(改行、空白文字取り除き)して、style.min.css を作成します。


以上が、backbone-boilerplateのデフォルトのビルドで実行されるタスクです。
実際の開発では、他のタスクも必要になってくると思います(jsdocとか)。
Javascriptアプリケーションで必要となりそうな最低限のビルドが用意されており、仮にbackbone-boilerplateを利用しない場合でも、これらのビルドの考えた方は参考になると思います。