ゆず日記

戦う Vimmer 兼 Dvorakユーザ 兼 Kinesisユーザ 兼 おぺらー が戦わないブログ

Grunt で livereload 環境を作ったお話。

Grunt を使うようになって早数ヶ月。
ガリガリ弄るってことはないのですが、何気なく使う機会は多いのでちょっと纏め。

Gruntの要約

3行で纏めると、

  • node.js で動作するタスク実行ツール
  • livereload や jshint, minify, less, Sass 生成とかを自動実行できる
  • 設定は Gruntfile.js に記述する

諸々全部入りで書こうとすると、何がなんだか分からない & 自分も全部理解していない為、まずは livereload (ファイル変更時にブラウザが自動リロードしてくれる) 環境の構築だけ纏める。

準備

node.js, npmのインストール

node.js からインストール。
node.js の複数バージョン管理をするのであれば、nodebrew とか nvm とかから入れる。
npm (node.jsのパッケージ管理ツール)は、node.js ver.0.6.3(2011/11/25リリース) 以降は node.js 内にインクルードされているので node.js をインストールすれば入る。

grunt-cli をグローバルにインストールする

grunt の0.4.0(2013/02/18リリース)以降では、grunt-cli をグローバルにインストールする必要がある。
(それ以前は grunt をグローバルにインストールする必要があったらしい。)
ここググって過去の日本語記事をあたっても上手く行かずに詰まった...
結局公式ドキュメントを見て解決できたのだが、最初から公式ドキュメントを見るべきだと痛感。
Getting started - Grunt: The JavaScript Task Runner

$ npm install -g grunt-cli # nvm 使っていない人は sudo が必要

以降の作業は、各サイトやプロジェクト用のディレクトリの下でインストールしていく。

$ mkdir my-project-name
$ cd ./my-project-name

package.jsonの設置

まず node.js のパッケージを管理する為の package.json を設置する。
package.json に必要なパッケージリストとバージョンを書いておくと、簡単にパッケージインストールできるようになる。
Ruby でいうなれば、Gemfile。

以下の内容を package.json というファイル名でディレクトリ下に設置する。

{
  "name": "my-project-name",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-connect": "*",
    "grunt-contrib-watch": "*"
  }
}

livereloadの最小構成に必要なパッケージは3つだけ。

  • grunt: Grunt本体
  • grunt-contrib-connect: Grunt上で動くWebサーバ。
  • grunt-contrib-watch: ディレクトリ配下のファイルをWatchし、ファイル変更をトリガーに予め設定した処理を行うことができるGruntのプラグイン*1

上の jsonGetting started - Grunt: The JavaScript Task Runner のコピペ + プラグイン部分を改変したもので、grunt 以外のプラグインのバージョンは今回は "*" にして特に規定していない。

Gruntとgruntpluginsのインストール

上述の通り、package.json に記述されたプラグインは、npm install コマンドで node_modules ディレクトリに一括インストールできる

$ npm install --save-dev // package.json 側に devDependencies と書いてあれば --save-dev は無くてもいい。

package.json 側で devDependencies、または --save-dev オプション付きでインストールすると、開発向けの依存モジュールとしてインストールされる。本番との利用モジュールを区別できるということらしい。

Gruntfile.jsの設置

Grunt の設定ファイルである Gruntfile.js を、gruntjs/grunt-contrib-connect · GitHub を見ながら設定を書いていく。

ここはもう Gruntfile.js 直接見てもらった方が早い。
(説明も兼ねて若干冗長に書いています。)

// 'use strict';

module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    // grunt-contrib-connectの設定(Webサーバの設定)
    connect: {
      site: { // オプション未設定の為、空オブジェクト
      }
    },

    // grunt-contrib-watchの設定(ウォッチ対象の設定)
    watch: {
      html_files: {
        files: '**/*.html' // ウォッチ対象として、ディレクトリ配下の*.htmlを指定
      },
      options: {
        livereload: true // 変更があればリロードするよ
      }
    }
  });

  // Load tasks(grunt実行時に読み込むプラグイン)
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-watch');

  // Default tasks(grunt実行時に実行するタスク)
  grunt.registerTask('default', ['connect', 'watch']);
};

grunt, livereloadの実行と、ブラウザ側の livereload プラグインのインストール

テキトーなindex.htmlを書いて同じ階層に設置し、

./my-project-name
  Gruntfile.js
  node_modules/
  package.json
  index.html

gruntコマンドを実行する

~/my-project-name $ grunt
Running "connect:site" (connect) task
Started connect web server on localhost:8000.

Running "watch" task
Waiting...

この状態で localhost:8000 にアクセスすると index.html をブラウジングできるようになる。

併せて、ブラウザ側にも livereload 用の拡張をインストールする。
Chromeの場合には、Chrome Web Store - LiveReload
Firefoxなど他のブラウザにもあるので、適宜インストールする。

ブラウザ拡張の livereload を有効にした状態で、この時に index.html を編集すると、自動的にリロードしてくれるようになる。

お疲れ様でした。

以下、Gruntfiles.jsの補足

grunt-contrib-connectの設定

Webサーバの設定を記述している。
options以降にポートやホスト名, プロトコルを指定可能。

connect: {
  site: {
    options: {
      hostname: '*',
      port: 9000
    }
  }
}

hostnameはdefaultで'localhost'、もしlocalhost以外でもアクセスしたい場合には hostname: '*' と設定すると別環境からもIPアドレス指定でアクセス可能になる、ステキ。


ちなみに、複数サイトを別ポートで同時起動可能。

connect: {
  site1: {
    options: {
      port: 9000,
      base: 'www-roots/site1'
    }
  },
  site2: {
    options: {
      port: 9001,
      base: 'www-roots/site2'
    }
  }
}

gruntjs/grunt-contrib-connect · GitHub

// grunt-contrib-watchの設定

前述の通り、ファイル変更をトリガーとするプラグインであるgrunt-contrib-watchの設定部分になるが、このプラグインにlivereloadもbuilt-inされている為、ここにlivereloadの設定を記述している。

livereload(というかoptions)の書き方は色々あり、

watch: {
  static_files: {
    files: ['**/*.html', '**/*.css']
    options: {
      livereload: true // static_filesで指定したfilesに対してのみlivereloadオプションを付与
    }
  },
  js_files: {
    files: '**/*.js'
  }
}

みたいに、watchの対象グループをhtml, cssに絞り、jsは対象外にする書き方や、

watch: {
  static_files: {
    files: ['**/*.html', '**/*.css']
  },
  js_files: {
    files: '**/*.js'
  },
  options: {
    livereload: true, // watch対象すべてに対してlivereloadオプションを付与
    spawn: false
  }
}

のようにwatch対象全てをlivereloadする書き方も出来る。

defaultでtrueのspawnオプションは、子プロセスでタスクを実行するかどうか指定するオプション。ファイル数が多いなどで重い場合には、spawn: falseを指定すると解消される場合がある。


ちなみにいろんなブログみた限り全然言及されてなかったけれど、filesは配列で複数指定できる

files: '**/*.html'  //ディレクトリ以下のすべての.htmlファイル
files: '**/*' //ディレクトリ以下のすべてのファイル
files: ['**/*.html', '**/*.css'] // ディレクトリ以下のすべてのhtml, cssファイル

gruntjs/grunt-contrib-watch · GitHub


"Fatal error: EMFILE: Too many opened files."とか言われる時。

ファイル数が多い場合にerrorが出る。OSのulimitの値を大きくすることで解消できる

$ ulimit -n // 現在のプロセス数を確認
256

$ ulimit -n 1024 // プロセス数を1024に変更

なお、都度変更するのが手間な場合には、launchctlで規定値を変更できる

$ launchctl limit // 現在のmaxfilesを確認
        cpu         unlimited      unlimited
        filesize    unlimited      unlimited
        data        unlimited      unlimited
        stack       8388608        67104768
        core        0              unlimited
        rss         unlimited      unlimited
        memlock     unlimited      unlimited
        maxproc     709            1064
        maxfiles    256           256

$ launchctl limit maxfiles 2048 2048 // default maxfilesを2048に変更

osx snow leopard - How to change default ulimit values in Mac OS X 10.6? - Super User

grunt-contrib-livereload プラグインは?

livereload 用にそのものズバリな grunt-contrib-livereload というプラグインがあるが、現在では gruntjs/grunt-contrib-livereload · GitHub を見ての通り grunt-contrib-watch プラグインに内包されている為不要。

ほか

jshint, minify, less, Sass自動生成 などについても纏めようと思ったけど長過ぎたのでここまで。
取り敢えず grunt 使えば出来ます。

終わりに

grunt, livereloadを導入にあたって、下記サイトを参考にさせていただきました。
GruntでLivereload(自動更新)をやってみた! | SAITEI no CHINJUU!!!(最低の珍獣)
grunt を軽く触ってついでに livereload させてみた - 憧れ駆動開発

自分の理解力が乏しい所為もあって、ただ見ながらやるだけでは理解できなかったのですが、
このエントリの為に公式ドキュメントを見たり、検証しながら書くことで大分理解できたと思う。
やっぱり定期的にアウトプットしないダメですね…。

しかし5月に教えてもらって5月に入れたのに、ブログに纏められたのが10月…。もっとペースあげねばw

*1:grunt-contrib-* をgruntpluginと呼ぶらしい。