Grunt + Webpack + Bowerを使ってCoffeeScriptで作ったBackboneアプリケーションをビルドする

  • 11
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

まえがき

  • nodeとかbowerとかgruntとかwebpackは適当に入れてください
  • JSビギナーなのでおかしなことが書いてあるかもしれません(ツッコミ歓迎)

元々CoffeeScriptで書いたものをGruntでコンパイルしつつ開発していましたが、ファイルを分割する必要があり、分割するにあたってそれぞれの依存関係を解決する必要がありました(速度出てたのでブラウザコンパイラは使わず)。当初、RequireJSを使って分割作業を始めたのですが、r.jsを使ったコンパイルにハマって作業が滞り、「最近はwebpackなんてのもあるらしいよ」という元同僚の一声に乗っかりwebpack始めてみました。

システム全体は、Djangoで書かれたウェブアプリケーション、Ajax APIを使用するクライアントサイドアプリケーションという構成になります。

記述言語、コンパイル、結合、コピー、変更監視は次のようなツール構成で実現しています。

タスク ツール 備考
言語 CoffeeScript / JS
JSの結合 grunt-webpack (webpack)
変更監視 grunt-contrib-watch webpackにも変更監視あるけど使いません
ファイルコピー grunt-contrib-copy

ディレクトリレイアウト

クライアントサイドアプリケーションのソースコードはwww/src以下に、bowerでインストールするライブラリ群はwww/components以下に配置しています。www/resources以下が実際の配布物という想定。

.
├── .bowerrc
├── Gruntfile.coffee
├── bower.json
├── package.json
└── www
    ├── components         # bower
    ├── resources
    │   ├── css
    │   └── js
    ├── src
    │   ├── loader.coffee  # アプリケーション実行開始時に読み込む
    │   ├── router.coffee  # Backbone.Router
    │   ├── models
    │   ├── utils
    │   └── views
    └── webpack.config.js  # 今回使わないwebpack

webpackの環境整備

node modules

必要なnodeモジュールをインストールします。

npm install coffee-script -g
npm install coffee-loader
npm install grunt -g
npm install webpack -g
npm install bower -g

以下は参考のpackage.jsonです。

package.json
{
  "name": "testapp",
  "version": "0.0.1",
  "engines": [
    "~0.11.14"
  ],
  "repository": {
    "type": "git",
    "url": "git://example.com"
  },
  "author": "Mitsukuni Sato",
  "devDependencies": {
    "coffee-loader": "^0.7.2",
    "coffee-script": "~1.8.0",
    "grunt": "^0.4.5",
    "grunt-contrib-clean": "^0.6.0",
    "grunt-contrib-coffee": "^0.12.0",
    "grunt-contrib-cssmin": "^0.10.0",
    "grunt-contrib-uglify": "^0.6.0",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-legacy-util": "^0.2.0",
    "grunt-notify": "^0.4.1",
    "grunt-webpack": "^1.0.8",
    "underscore.string": "^2.3.3",
    "webpack-dev-server": "^1.6.6"
  },
  "dependencies": {
    "bower": "^1.3.12"
  }
}

bower

Backboneに必要なモジュールはbowerからインストールします。.bowerrc, bower.jsonを書いてbowerからインストールし。

.bowerrc
{
  "directory": "www/components/",
  "analytics": false,
  "timeout": 30000,
  "registry": {
    "search": [
      "https://bower.herokuapp.com"
    ]
  }
}
bower.json
{
  "name": "testapp",
  "version": "0.0.1",
  "homepage": "https://example.com",
  "authors": [
    "Mitsukuni Sato <mitsukuni.sato@example.com>"
  ],
  "license": "MIT",
  "private": true,
  "ignore": [
    "**/.*",
    "node_modules",
    "www/components/",
    "test",
    "tests"
  ],
  "dependencies": {
    "jquery": "~2.1.1",
    "underscore": "~1.7.0",
    "backbone": "~1.1.2"
  }
}
bower install

Grunt

Gruntfileを書いてwebpackをビルドできるようにします。

Gruntfile
"use strict"

# webpackのモジュールを呼び出しておく
path = require("path")
webpack = require('webpack')

tasks = ["webpack"]


module.exports = (grunt) ->
  pkg = grunt.file.readJSON("package.json")

  grunt.initConfig

    pkg: pkg

    # webpackのオプションをここに書く。
    # webpack.config.jsと併用する場合、どちらを優先しておくか決めておくのが良さそう。
    webpack:
      app:
        entry: "./www/src/loader.coffee"  # コンパイル開始時に読み込むエントリファイル
        output:
          filename: "./www/resources/js/bundle.js"  # 出力先ファイルパス
        module:
          loaders: [
            { test: /\.coffee$/, loader: 'coffee-loader' }  # 拡張子coffeeはcoffee-loaderを使う
          ]
        resolve:
          root: [path.join(__dirname, "www/components")]  # bowerのディレクトリを定義
        plugins: [
          new webpack.ResolverPlugin(
            new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
          ),
          new webpack.ProvidePlugin({  # グローバル空間に読み込むモジュール?よく分かってません
            $: "jquery",
            jQuery: "jquery",
            _: "underscore",
            Backbone: "backbone"
          })
        ]
        stats:
          colors: true
          modules: true
          reasons: true
        progress: false
        failOnError: false
        watch: false  # webpackで変更監視しません
        keepalive: false

    watch:
      files: [
        "www/src/**/*.coffee",
        "www/components/**/*.js",
      ]
      tasks: tasks

  taskName = undefined

  for taskName of pkg.devDependencies
    grunt.loadNpmTasks taskName  if taskName.substring(0, 6) is "grunt-"

  grunt.registerTask "default", tasks

Backboneアプリケーション

loader.coffee

webpackのコンパイル時に初めに呼び出されるファイル。

loader.coffee
require './router.coffee'

window.App =
  Models: {}
  Views: {}
  Utils: {}

router.coffee

Backbone.Routerの定義。実質このファイルの処理からアプリケーションが開始される。

router.coffee
require './views/top.coffee'

jQuery ->
  class App.Router extends Backbone.Router
    routes:
      '': 'top'
      'pictures/:id': 'pictures'

    top: ->
      console.log('top', arguments)
      new App.Views.TopView()

    pictures: ->
      console.log('pictures', arguments)

  new App.Router()
  Backbone.history.start({pushState: true})

views/top.coffee

サイト直下にアクセスした場合のビューを定義します。Apps.Models.PhotoModelに依存するものとします(てきとうです)。

views/top.coffee
# 必要なモデルを呼び出し
require '../models/top.coffee'


jQuery ->
  # Backboneオブジェクトは定義済みなのでrequire必要なし
  class App.Views.TopView extends Backbone.View
    initialize: ->
      @

models/top.coffee

Apps.Models.PhotoModelの定義(てきとうです)。

models/top.coffee
jQuery ->
  class App.Models.PhotoModel extends Backbone.Model
    idAttribute: 'id'
    urlRoot: '/ajax/photos/'

    parse: (response)->
      return response.photo

コンパイル

手動でコンパイル

webpack.config.jsを用意していないのでgruntからコンパイルします。

grunt webpack

Running "webpack:app" (webpack) task
Version: webpack 1.4.13
                       Asset    Size  Chunks             Chunk Names
./www/resources/js/bundle.js  460308       0  [emitted]  main
   [0] ./www/src/loader.coffee 86 {0} [built]
   [1] ./www/src/router.coffee 2093 {0} [built]
(省略)

Done, without errors.

変更を監視してコンパイル

grunt-contrib-watchをインストールしてCoffeeScriptの変更を監視し、変更時に自動的にコンパイルします。前述のGruntfile.coffeeにwatchタスクが定義されているので、grunt watchを実行するだけで監視状態になります。

grunt watch

参考