gulp

gulp.js+webpack+stylusでフロントエンド開発環境をつくる

サーバーサイドやスマートフォンアプリの開発ばかりしていて、jsなどのWebフロントエンド開発から遠ざかっていたら、とりまく環境がえらく複雑になっていたので勉強し直してみました。

以下、自分用メモを兼ねて書いておきます。

前置き

Webのフロントエンドというと、html+css+JavaScriptという構成になります。

しかしながら、最近の開発手法としては、これらを直接記述するのではなく、代替言語などを使って記述したものをビルドして成果物を出力するという方法が主流になってきているようです。

今回やりたいこと

  • htmlはjadeを使って記述
  • JavaScriptのモジュール依存解決にはwebpackを使う
  • cssはStylusを使って記述
  • ファイルの変更を監視して自動的にビルドしたい
  • ついでにビルド完了時にブラウザを自動的にリロードしたい

これらのことが、ツールを組み合わせることで実現できてしまいます。

Node.jsをインストールする

まず、最初の準備としてNode.jsをインストールします。

公式からインストールしてもいいのですが、今回はnodebrewで入れます。

$ curl -L git.io/nodebrew | perl - setup

ホームディレクトリの.nodebrew以下にインストールされるので、以下を.bashrcなどのシェルの設定ファイルに追加します(シェルによっては書き方が違いますので要注意)。

export PATH=$HOME/.nodebrew/current/bin:$PATH

追記したら、ターミナルを起動しなおすか、sourceコマンドなどで環境変数をセットしなおすとnodebrewコマンドが使えるようになります。

$ nodebrew help

nodebrewの良い所は、Node.jsの複数のバージョンをインストールして切り替えることができるところです。Node.jsをインストールするには以下のようにコマンドを実行します。

$ nodebrew install-binary latest # latest version
$ nodebrew install-binary stable # stable version
$ nodebrew install-binary v0.10.x # v0.10 latest
$ nodebrew install-binary 0.10.29  # without `v`

今回は最新版で進めたいので、latestをインストールします。

インストールが完了したら、コマンドが利用できるかどうか確認しておきます。

$ node -v
v0.12.0
$ npm -v
2.5.1

次に作業ディレクトリへ移動して、Node.jsのパッケージを管理するpackage.jsonを作成します。

$ mkdir ~/Documents/devtest
$ cd ~/Documents/devtest
$ npm init

色々と質問が出てきますので、それに答えましょう(面倒ならそのままエンターでもいいです)。
終わると、package.jsonが生成されていると思います。

gulp.js

次にビルドツールであるgulp.jpをインストールします。

$ npm install -g gulp
$ npm install -D gulp

gulp.jsは、gulpfile.jsというファイルを定義して実行する仕組みになっています。
このファイルのみに集約すると管理が大変になるので、gulp/tasks内にタスクごとにファイルを分けて定義するようにします。

具体的には、gulp.task("タスク名",function)でタスクを定義する際に、タスク名.jsのファイルでgulp/tasks内に保存することでタスク名とファイル名を一致させ、管理をかんたんにすることができます。

.
├── gulp
│   └── tasks
└── package.json

このようなファイル構造にします。
gulpfile.js の方はというと、gulp/tasks以下のファイルを読み込む処理を書いておきます。

var requireDir = require('require-dir');
requireDir('./gulp/tasks', { recurse: true });

require-dir に依存しているので、npmでインストールします。

$ npm install -D require-dir

また、共通の設定ファイルとしてgulp/config.jsを作成しておきます。

var path = require('path');

var dest = './dest'; // 出力先ディレクトリ
var src = './src';  // ソースディレクトリ
var relativeSrcPath = path.relative('.', src); // 後々、つかいます

module.exports = {
  src: src,
  dest: dest,

  // 各タスクごとの設定をこの下に追加していく
}

上記では、変換元のファイルをsrcディレクトリ以下に、ビルド後の出力ファイルをdestディレクトリ以下に吐き出すようにしています。

jade

jadeというのは、jsのテンプレートライブラリの一種で、以下の様な書法でHTMLを定義することができます。

doctype html
html
  head
    title="タイトル"
  body#main
    p 本文が入ります。

これをjadeで変換すると以下のようなHTMLが生成されます。

<!DOCTYPE html>
<html>
  <head>
    <title>タイトル</title>
  </head>
  <body id="main">
    <p>本文が入ります。</p>
  </body>
</html>

HTMLと比較すると記述量が少ない(閉じタグ不要)のとidやclassの指定がcssと同じ書法になっているのが特徴です。閉じタグが不要である代わりに、Pythonのようにインデントが必須なのでそこは要注意です。

さて、これをgulpで実行できるようにするため、タスクの定義を行います。
gulp/tasks/jade.js を以下の内容で作成します。

var gulp = require("gulp");
var jade = require("gulp-jade");
var plumber = require("gulp-plumber");
var config = require('../config').jade;

gulp.task("jade", function(){
  gulp.src(config.src)
      .pipe(plumber()) // エラーが発生しても処理を継続
      .pipe(jade(config.options))
      .pipe(gulp.dest(config.dest));
});

上記で用いているモジュールをインストールします。

$ npm install -D gulp-jade gulp-plumber

config.jsも追記しておきます。

  // jade
  jade: {
    src: [
      src + '/www/**/!(_)*.jade'
    ],
    dest: dest,
    options: {pretty: true}
  },

src/www 以下の、.jadeファイルに対して変換をかけるように定義してみました。

実際に、先ほどのサンプルのjadeをsrc/www/test.jadeという名前で保存して実験してみます。
保存できたら、以下のコマンドを実行します。

$ gulp jade
[00:50:05] Using gulpfile ~/Documents/devtest/gulpfile.js
[00:50:05] Starting 'jade'...
[00:50:05] Finished 'jade' after 15 ms

gulpコマンドの後にはタスク名を指定することで指定したタスクを実行することができます。

$ cat dest/test.html
<!DOCTYPE html>
<html>
  <head>
    <title>タイトル</title>
  </head>
  <body id="main">
    <p>本文が入ります。</p>
  </body>
</html>

うまく変換ができていることが分かります。

webpack

次に、jsをwebpackでビルドすることを考えます。jsはsrc/jsに記述し、ビルド後はdest/jsに保存されるようにしてみます。

まず、gulp/tasks/webpack.jsにタスクを定義します。

var gulp = require('gulp');
var gulpif = require('gulp-if');
var uglify = require('gulp-uglify');
var webpack = require('gulp-webpack');
var config = require('../config');

gulp.task('webpack', function () {
    gulp.src(config.webpack.entry)
        .pipe(webpack(config.webpack))
        .pipe(gulpif(config.js.uglify, uglify()))
        .pipe(gulp.dest(config.js.dest));
});

上で使用している未インストールのモジュールを入れます。

$ npm install -D gulp-if gulp-uglify gulp-webpack

jadeと同様にconfig.jsに追記します。

  // jsのビルド設定
  js: {
    src: src + '/js/**',
    dest: dest + '/js',
    uglify: true
  },

  // webpackの設定
  webpack: {
    entry: src + '/js/main.js',
    output: {
      filename: 'bundle.js'
    },
    resolve: {
      extensions: ['', '.js']
    },
  },

試しに簡単なjQueryを使ったスクリプトをビルドしてみます。
src/js/main.jsを作成します。

var $ = require('jquery');

$(function(){
  var test = $('body#main');
  console.log(test.html());
});

これでビルドを実行すると…

$ gulp webpack
[01:31:41] Using gulpfile ~/Documents/devtest/gulpfile.js
[01:31:41] Starting 'webpack'...
[01:31:41] Finished 'webpack' after 223 ms
[01:31:41] Version: webpack 1.5.3
    Asset  Size  Chunks             Chunk Names
bundle.js  1737       0  [emitted]  main

ERROR in ./src/js/main.js
Module not found: Error: Cannot resolve module 'jquery' in /Users/abe/Documents/devtest/src/js
 @ ./src/js/main.js 1:8-25

エラーになりました。npmでjqueryを入れておかないとだめなようです。

$ npm install -D jquery

これで再実行すると、無事にbundle.jsがdest/js内に生成されると思います。

Stylus

Stylusは、LESSやSASSなどと同じようなCSSメタ言語の一種です。LESSやSASSはCSSと似た書き方をしますが、Stylusは前述のjadeのようにインデントで定義していくような書法になっているのが特徴です。

今回は、src/styl以下にStylusのファイルを置くこととし、先頭が_で始まるファイルについては他からincludeしたりextendしたりするファイル(直接変換はしない)として処理することにします。

では、gulp/tasks/stylus.jsにタスクを定義します。

var gulp = require('gulp');
var gulpif = require('gulp-if');
var plumber = require('gulp-plumber');
var stylus = require('gulp-stylus');
var concat = require('gulp-concat');
var autoprefixer = require('gulp-autoprefixer');
var minify = require('gulp-minify-css');
var nib = require('nib'); // Stylusのmixin
var config = require('../config').stylus;

gulp.task('stylus', function () {
    gulp.src(config.src)
        .pipe(plumber())              // エラー出ても止まらないようにする
        .pipe(stylus({use: [nib()]}))               // 実際コンパイルしてるのはここ
        .pipe(concat(config.output))  // 1つのファイルに固める
        .pipe(autoprefixer(config.autoprefixer))  // vendor-prefixつける
        .pipe(gulpif(config.minify, minify()))    // 必要ならminifyする
        .pipe(gulp.dest(config.dest));            // 出力する
});

npmでモジュールをインストールします。

$ npm install -D gulp-stylus gulp-concat gulp-autoprefixer gulp-minify-css nib

config.jsに追記します。

  // stylus
  stylus: {
    src: [
      src + '/styl/**/!(_)*'
    ],
    dest: dest + '/css/',
    output: 'main.css',  // 出力ファイル名
    autoprefixer: {
      browsers: ['last 2 versions']
    },
    minify: false
  },

試しに、以下のStylusをsrc/styl/main.stylに保存して変換してみます。

fonts = helvetica, arial, sans-serif

body
  padding 50px
  font 14px/1.4 fonts

ビルドを実行して…

$ gulp stylus

結果を見てみると…

$ cat dest/css/main.css
body {
  padding: 50px;
  font: 14px/1.4 helvetica, arial, sans-serif;
}

きちんと変換できていますね!

静的ファイルのコピー

www以下に静的なHTMLや画像などを配置した場合にコピーする必要があるので、それを定義しておきます。

gulp/tasks/copy.jsを作成します。

var gulp = require('gulp');
var config = require('../config').copy;

gulp.task('copy', function () {
    gulp.src(config.src)
        .pipe(gulp.dest(config.dest));
});

config.jsは

  // copy
  copy: {
    src: [
      src + '/www/**/*.!(jade)'
    ],
    dest: dest
  },

として、*.jade以外のファイルをコピーするようにしてみました。

buildタスクを作る

複数のタスクをまとめて実行できる、buildタスクを作っておきます。

gulp/task/build.jsを作ります。

var gulp = require('gulp');

gulp.task('build', ['webpack', 'stylus', 'jade', 'copy']);

これで、webpack, stylus, jade, copyの4つのタスクを自動的に実行することができます。

ファイルの変更を監視して自動的にビルド

ファイルの変更時に自動的にビルドを行うようにしておけば、わざわざその度にコマンドを入力する必要がなくなるので便利です。

gulp/task/watch.jsに定義してみます。

var gulp = require('gulp');
var watch = require('gulp-watch');
var config = require('../config').watch;

gulp.task('watch', function () {
    // js
    watch(config.js, function () {
        gulp.start(['webpack']);
    });

    // styl
    watch(config.styl, function () {
        gulp.start(['stylus']);
    });

    // jade
    watch(config.jade, function() {
        gulp.start(['jade']);
    });

    // www
    watch(config.www, function () {
        gulp.start(['copy']);
    });
});

gulp-watchをインストールします。

$ npm install -D gulp-watch

config.jsに監視するパスを指定します。

  // watch
  watch: {
    js: relativeSrcPath + '/js/**',
    styl: relativeSrcPath + '/styl/**',
    jade: relativeSrcPath + '/www/**',
    www: relativeSrcPath + '/www/**'
  },

これで、gulp watchを実行するとファイル監視状態が開始されます。

ビルド完了時にブラウザを自動的にリロード

LiveReloadという機能を使うと、開発用の簡易サーバと連動して自動的にリロードできるようになります。

下準備として、Chromeの拡張機能「LiveReload」をインストールしておきます。

gulp/tasks/webserver.jsを定義します。

var gulp = require('gulp');
var webserver = require('gulp-webserver');
var config = require('../config');

gulp.task('webserver',function(){
    gulp.src(config.dest)
        .pipe(webserver({
            livereload: true,
            port: 8001,
            fallback: 'index.html',
            open: true
        }));
});

npmでモジュールをインストールします。

$ npm install -D gulp-webserver

これで、gulp webserverを実行するとサーバが起動し、LiveReloadも有効になります。

デフォルトタスクを定義

gulpを何も指定せず実行した時に、ビルド→ファイル監視→サーバ起動と自動で実行してくれると理想的です。

defaultタスクを、gulp/tasks/default.jsに記述します。

var gulp = require('gulp');

gulp.task('default', ['build', 'watch', 'webserver']);

これで、うまくいくはずです。

参考サイト

今回は分からないことばかりで、以下のサイトを参考にさせてもらいました。ありがとうございます!

gulp.jsを使ってフロントエンドのビルドをする【webpack, stylus】 - yutaponのブログ [http://yutapon.hatenablog.com/]

gulpでlivereloadしたい時の書き方 - Qiita [http://qiita.com/]

タスクランナーgulp入門 - to-R [http://blog.webcreativepark.net/]

LINEで送る
Pocket

gulp.js+webpack+stylusでフロントエンド開発環境をつくる」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です