VSCodeでGulpを実行しJavaScriptとSassをビルドしてBrowserSyncでライブプレビューする方法

**[TOWN Advent Calendar 2018](https://qiita.com/advent-calendar/2018/town)** 1日目の記事です。

弊社は9割開発者の弊社においてなので、デザイナーがやっていること・出来ることを知らない人も多いと思います。

ならばデザイナーの自分から一発目をかましたろと思い、エンジニアとも共通の話題にしやすい自分のフロントコーディング環境について紹介します。フロント始める人の参考になれば。

※以下はGulp3の内容です。[Gulp4に対応した内容を新たに書きました。](https://qiita.com/KazuyoshiGoto/items/3059c99330cdc19e97ad)


## どんな環境？

* **Node.js**インストール済みの環境で
* エディタに**VSCode**使って
* タスクランナーに**Gulp**を利用して
* **Sass**をCSSにコンパイルし
* **BrowserSync**でライブプレビューしています。

こんな環境で書いてます。

## 前提＆用語

### VSCode

みんな大好き**Visual Studio Code**.

VSCodeに限らずAtomなどターミナルを内包したエディタなら何でも良いです。Gulpはターミナルで実行するので、別にターミナルを起動するより楽なためです。

### Node.js & npm

サーバサイドで動作するJavaScriptです。
ググれば質の高い情報がゴロゴロあるのでNode.jsについて説明は省きます。

npmはNode.jsで動作するアプリケーションのパッケージ管理サービスです。
Node.jsをインストールしてnpmが使える状態が前提になります。

### Gulp

**Gulp**とはNode.jsベースで動作するタスクランナーです。

タスクランナーとはフロントエンドコーディングで頻発する様々な細かいタスクを自動化してくれるナイスガイです。例えば以下のようなことができます。

* sass、babelなどのコンパイラ実行
* 各種コードのLint
* コードの圧縮、整形
* 画像変換
* ライブプレビュー
* その他色々

タスクランナーにはいくつかの種類があり、自分は**Grunt**から使い始めましたがGulpの方が設定しやすく移行しました。

### Sass

**Sass**とはCSSを簡単に書くためのメタ言語です。

SassにはCSSコーディングの時間を短縮できる様々な機能があります。最も分かりやすい例だと、CSSで頻発する以下のようなネスト構造を簡単に書くことができる点です。

```css
.hoge {width:100px}
.hoge p {margin:20px}
.hoge a {color:#f00}
```
↓Sassの書き方

```scss
.hoge {
  width:100px;
  p {margin:20px}
  a {color:#f00}
}
```

Sassの他の機能にも興味があればググってください。

Sass記法のままではブラウザでは読まれないため、GulpでSassをCSSにコンパイルします。

少し補足ですが、Sassで使われるファイルは`.scss`ファイルになります。`.sass`形式もありますが少し記法が異なり、素のCSSと共存できる`.scss`の方がメジャーなためこちらを使います。


## ディレクトリ構成

以下のディレクトリ構成を前提にします。

```
project(名前自由)
├dist/
│ ├css/
│ └js/
└src/
  ├js/
  └scss/
```
* `dist` : 公開用フォルダ、基本いじらない
* `src` : 編集用フォルダ、こちらをいじる

src以下のファイルを編集したらGulpでdist以下に出力する流れです。


では環境を作っていきます。

## プロジェクトの初期化

VSCodeで `project` フォルダを開きます。ターミナルの起点が `project` になるので、ターミナルで以下のように打ちます。

```
npm init -y
```
これでプロジェクトに `package.json` というファイルが作られます。

`package.json` にはプロジェクト情報やインストールしたnpmのパッケージ情報が記録されます。内容は今はまだ気にしなくて良いです。

## Gulpと各種モジュールのインストール

次にGulpをインストールします。

```
npm i gulp -D
```
これでプロジェクトにgulpがインストールされました。

Gulp単体では何もできないので用途に応じてモジュールを入れていきます。

```console
npm i gulp-notify -D        //Gulpのエラー通知をしてくれるモジュール
npm i gulp-plumber -D       //Gulpで実行エラーが起きてもプロセスを落とさず続けてくれるモジュール
npm i gulp-sass -D          //GulpでSassを扱えるようになるモジュール
npm i gulp-autoprefixer -D  //CSSに自動でベンダープレフィックスを入れてくれるモジュール
npm i gulp-uglify -D        //JavaScriptを難読化＆Minifyしてくれるモジュール
npm i browser-sync -D       //ライブプレビューできるようになるモジュール
```

インストールしたモジュールは `project` フォルダ直下の `node_modules` フォルダにインストールされています。

ここで一旦 `node_modules` フォルダをまるごと削除してから以下のコマンドを打ってください。

```
npm install
```

モジュールが再インストールされ `node_modules` フォルダが元に戻ったと思います。

インストール時に付けた `-D` のオプションで `package.json` にインストール情報が記録されていたため `install` コマンド一つで全てのモジュールが再インストールできます。 `package.json` さえ残していればnpmで構築した環境をいつでもどこでも復元できます。

## gulpfile.js の作成

`gulpfile.js` とはGulpに実行させる内容をまとめた設定ファイルです。
`project` フォルダ直下に `gulpfile.js` を作り以下のように打ちます。

```gulpfile.js
var gulp         = require('gulp');
var notify       = require("gulp-notify");
var plumber      = require("gulp-plumber");
var sass         = require('gulp-sass');
var autoprefixer = require('gulp-autoprefixer');
var uglify       = require('gulp-uglify');
var browserSync  = require("browser-sync");
```

まず利用するモジュールを読み込みます。

それではタスク別に`gulpfile.js`へ追記していきます。

## パスの記述

パスの記述を多用するため変数に入れておきます。

```gulpfile.js
//パス設定
var paths = {
  'html'   : './dist/',
  'scss'   : './src/scss/',
  'css'    : './dist/css/',
  'jsSrc'  : './src/js/',
  'jsDist' : './dist/js/'
}
```

単に自分の書き方のクセなのでコード内で逐一パスをハードコーディングしても問題ありませんが、このように設定しておいた方が楽だと思います。

## Sassのタスク記述

Sassを使えるようにするためのタスクを追記します。

```gulpfile.js
//Sass
var sassOptions = {
  outputStyle: 'compressed'//圧縮設定 nested, expanded, compact, compressed
}
gulp.task('sass', function () {
  gulp.src(paths.scss + '**/*.scss')
    .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") }))
    .pipe(sass(sassOptions))
    .pipe(autoprefixer())
    .pipe(gulp.dest(paths.css))
});
```

* `/src/scss/`以下で`*.scss`ファイルが更新されたら
* Gulpエラーチェックを通して
* Sass変換を通して
* 自動ベンダープレフィックスを通して
* `/dist/css/`フォルダに書き出す

といった流れです。
pipeでモジュールを繋げていくだけなので何をしているのか読みやすいですね。

なお処理の頭で`sassOptions`にSass変換時のオプションを入れています。自分的にはMinifyするだけで十分なので、さらに凝ったオプションを試したい方はググってください。

## JavaScript圧縮のタスク記述

JSを圧縮するためのタスクを追記します。

```gulpfile.js
//JS圧縮
gulp.task('js', function () {
  gulp.src(paths.jsSrc + '**/*.js')
    .pipe(plumber())
    .pipe(uglify())
    .pipe(gulp.dest(paths.jsDist));
});
```

このJS圧縮もなかなか有能で、コードの短縮、Minify、難読化を同時にやってくれます。

## BrowserSyncのタスク記述

ライブプレビューできるようにするタスクを追記します。

```gulpfile.js
//BrowserSync
gulp.task('browser-sync', () => {
  browserSync({
    server: {
      baseDir: paths.html
    }
  });
  gulp.watch(paths.html + "**/*.html", ['reload']);
  gulp.watch(paths.css + "**/*.css", ['reload']);
  gulp.watch(paths.jsDist + "**/*.js", ['reload']);
});
gulp.task('reload', () => {
  browserSync.reload();
});
```

`src`フォルダのファイルを監視し、更新されたらブラウザがリフレッシュされる仕組みです。

## タスク自動実行の記述

ここまで作ったタスクを自動的に実行するためのタスクを作ります。

これまでのコードでSassやJS圧縮用の`sass` `js`というタスクを作っていましたが、これらを実行するには

```
gulp sass
gulp js
```

と、Sassをコンパイルしたい時、JSを圧縮したい時に毎度打つ必要があり猛烈に面倒です。

そこで以下のコードを最後に書きます。

```gulpfile.js
//watch
gulp.task('watch', function () {
  gulp.watch(paths.scss + '**/*.scss', ['scss']);
  gulp.watch(paths.jsSrc + '**/*.js', ['js']);
});

gulp.task('default', ['browser-sync', 'watch']);
```

`watch` と `default` のタスクを追加します。

コンソールで以下のように打つことで `default` のタスクが実行されます。

```
gulp
```

`default` は同時に`browser-sync` と `watch` も実行するので、今回やりたい事をこれ一つで実行できるようになりました。

以上で`gulpfile.js`全体は以下のようになります。

```gulpfile.js
var gulp         = require('gulp');
var notify       = require("gulp-notify");
var plumber      = require("gulp-plumber");
var sass         = require('gulp-sass');
var autoprefixer = require('gulp-autoprefixer');
var uglify       = require('gulp-uglify');
var browserSync  = require("browser-sync");

//パス設定
var paths = {
  'html'   : './dist/',
  'scss'   : './src/sass/',
  'css'    : './dist/css/',
  'jsSrc'  : './src/js/',
  'jsDist' : './dist/js/'
}

//Sass
var sassOptions = {
  outputStyle: 'compressed'
}
gulp.task('scss', function () {
  gulp.src(paths.scss + '**/*.scss')
    .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") }))
    .pipe(sass(sassOptions))
    .pipe(autoprefixer())
    .pipe(gulp.dest(paths.css))
});

//JS圧縮
gulp.task('js', function () {
  gulp.src(paths.jsSrc + '**/*.js')
    .pipe(plumber())
    .pipe(uglify())
    .pipe(gulp.dest(paths.jsDist));
});

//BrowserSync
gulp.task('browser-sync', () => {
  browserSync({
    server: {
      baseDir: paths.html
    }
  });
  gulp.watch(paths.html + "**/*.html", ['reload']);
  gulp.watch(paths.css + "**/*.css", ['reload']);
  gulp.watch(paths.jsDist + "**/*.js", ['reload']);
});
gulp.task('reload', () => {
  browserSync.reload();
});

//BrowserSync
gulp.task('browser-sync', () => {
  browserSync({
    server: {
      baseDir: paths.html
    }
  });
  gulp.watch(paths.html + "**/*.html", ['reload']);
  gulp.watch(paths.css + "**/*.css", ['reload']);
  gulp.watch(paths.jsDist + "**/*.js", ['reload']);
});
gulp.task('reload', () => {
  browserSync.reload();
});

//watch
gulp.task('watch', function () {
  gulp.watch(paths.scss + '**/*.scss', ['scss']);
  gulp.watch(paths.jsSrc + '**/*.js', ['js']);
});

gulp.task('default', ['browser-sync', 'watch']);
```

## Gulpの実行

ターミナルで以下のように打ちます。

```
gulp
```

すでに書いた通りですが、これで`default`に指定したタスクが実行され、Gulpが監視状態に入ります。Browser Syncも起動し、ブラウザの新規タブで`dist`フォルダ直下のindexが表示されます。

あとは`src`フォルダ以下のscssファイルやjsファイルを編集することで`dist`以下にモリモリ出力され、Browser Syncで起動したブラウザのタブでライブプレビューが動くという寸法です。

---

近頃はそもそもフロントのコードを書くこと自体が忌避されがちですが、何だかんだ細かい調整は必要だしGulp対応しているフロントエンドのフレームワークも多いので、Gulpで楽しくコード書いていきましょう。