LoginSignup
11
10

More than 3 years have passed since last update.

[gulp4] ejs, sassの開発環境を作ったので覚書き

Last updated at Posted at 2020-04-04

はじめに

gulp4で開発環境を作ったので、全体の流れの覚書きです。
gulpfileのコードは載っけていますが、gulp.jsの詳しい説明は載せてません。

gulpfile設定の時のつまづきメモはこちら。
[gulp4] gulpfileのつまずきアレコレ

作った環境

  • ejsをhtmlに変換、圧縮
  • sassをcssに変換、ベンダープレフィックス付与、圧縮、ソースマップ作成
  • JSを圧縮、ソースマップ作成、
  • 画像を圧縮
  • 更新が内容保存されたら、ブラウザを自動でリロードする

ちなみに、作成時の環境はこちらでした。
node v12.16.1
npm v6.13.4

導入の手順

  1. node.jsをインストール
  2. パッケージをインストール
  3. gulpfile.jsを設定する

手順1:node.jsをインストール

「作った環境」は、node.jsのパッケージをインストールすることで作れます。
その為にまずはnode.jsをインストールします。

選択肢①: homebrew,nodebrewでインストール

こちらの記事が参考になりそうです!
MacにNode.jsをインストール

ただ、私はnode.jsはインストール済みでしたが、"nodenv"というものを知り使ってみたかったので「選択肢②」を今回試しました。

選択肢②:nodenvでインストール

この機会に、プロジェクト毎にnode.jsのバージョンを簡単に切り替えられる"nodenv"を入れてみました。

こちらのサイトがわかりやすかったです!
anyenvとnodenvでNode.jsのバージョン管理をしよう!

ただ、私の場合、nodenvを使ってプロジェクト用のnode.jsをインストールする際にnodenv: default-packages file not foundというエラーが出てしまいました。

yarnのインストールと、yarnでdefault-packagesファイルを作る必要があったようです。
echo yarn >> $NODENV_ROOT/default-packagesを打ったらインストールできる様になりました。

このエラー時に参考にさせていただいたサイトはこちら。
nodenv install時にdefault-packages file not foundが出た時のメモ

手順2:パッケージをインストール

いよいよパッケージをインストールしていく訳なのですが、その前に、どんなものをインストールしたのかを記録するファイル package.json が必要になります。

手順2-1:package.jsonを生成する

作業ディレクトリに移動して、ターミナルに以下を入力すると、package.jsonが生成されます。

npm init -y

-yオプションをつけると、ファイル名やファイル説明などの設定項目を全て "yes" で処理したこととなり、デフォルト設定になります。
最初にファイル設定をしたい場合は npm init と入れて、各項目を埋めていけばOKです。
設定は、package.jsonのファイル内容を書き換えることでいつでも編集できます。

手順2-2:パッケージをインストールする

パッケージインストールのコマンドは以下の通りです。
ターミナル上で色々文字が動き出してちょっと怖いですが、アンインストールも簡単にできるので恐れず入力します。

//インストール
npm install --save-dev パッケージ名

//一括インストールしたい時
npm install --save-dev パッケージ名1 パッケージ名2 パッケージ名2

//アンインストール
npm uninstall --save-dev パッケージ名

今回使うパッケージは、全て開発環境でのみ使用するので、--save-devオプションをつけます。
他にも、npm install パッケージ名 --save-devnpm i -D パッケージ名といった書き方でもOKです。

メモ: "--save-dev" オプションについて

--save-dev-D オプションを付けると、package.json内の "devDependencies" 項目に、つけずに npm install パッケージ名 とすると "dependencies" 項目に登録されます。

"dependencies"とは、「依存関係」という意味です。
さっくりいうと、こういう感じ。

  • devDependencies:開発にのみ使用するパッケージ(gulpなど)
  • dependencies:本番環境でも使用するパッケージ (プラグインなど)

今回インストールするパッケージ一覧

先ほど紹介したコマンドを使用して個別にインストールするか、こちら↓をコピペ。

npm install --save-dev gulp gulp-sass gulp-postcss autoprefixer gulp-clean-css gulp-ejs gulp-htmlmin gulp-uglify del gulp-imagemin imagemin-mozjpeg imagemin-svgo imagemin-pngquant imagemin-gifsicle gulp-watch browser-sync

パッケージの役割は次の通りです。(クリックで各提供元ページに遷移します)
他にもgulpの公式サイトやググると、いろんな便利パッケージを探すことができます。

gulp :タスクランナー
gulp-rename :ファイル名変更
gulp-sass :sassをcssに変換
node-sass-package-importer :.css拡張子のファイル(sanitize.cssなど)をscssファイルで読み込み可にする
gulp-postcss :autoprefixerを使う為に必要
autoprefixer :ベンダープレフィックスを自動付与する
gulp-clean-css :cssを圧縮
gulp-ejs :ejsをhtmlに変換
gulp-htmlmin :htmlを圧縮
gulp-babel :ES6をES5に変換する(IE11が非対応のES6に対応するため)
gulp-uglify :jsを圧縮
del :ファイルを削除する
gulp-imagemin :画像圧縮
imagemin-mozjpeg :jpg用エンコーダー
imagemin-svgo :svg用エンコーダー
imagemin-pngquant :png用エンコーダー
imagemin-gifsicle :gif用エンコーダー
gulp-watch :タスクを監視する
gulp-plumber :エラー検知
gulp-notify :検知したエラーをデスクトップに通知
browser-sync :ファイル保存時にブラウザを自動でリロード

インストールされたら、package.jsonの"devDependencies"項目に、パッケージ名とバージョンがずらっと並んでるはず。

手順3:gulpfile.jsを設定する

インストールしたパッケージは、gulpfile.jsを設定することで使えるようになります。

手順3-1: gulpfileを記載する

gulpの公式サイトや、パッケージ提供元の説明を見ながら記載していきます。

出来上がったのはこんな感じ。

gulpfile.js
const gulp = require('gulp'); //gulp
const rename = require('gulp-rename'); //名前変更
const sass = require('gulp-sass'); //sassコンパイル
const packageImporter = require('node-sass-package-importer'); //cssパッケージのimport用
const postcss = require('gulp-postcss'); //autoprefixerを使うのに必要
const autoprefixer = require('autoprefixer'); //prefixをつける
const cleanCss = require('gulp-clean-css'); //css圧縮
const ejs = require('gulp-ejs'); //ejsコンパイル
const htmlmin = require('gulp-htmlmin'); //html圧縮と整理
const babel = require('gulp-babel'); //jsトランスパイル
const uglify = require('gulp-uglify'); //js圧縮
const del = require('del'); //フォルダやファイルを削除
const imagemin = require('gulp-imagemin'); //画像圧縮
const mozjpeg = require('imagemin-mozjpeg') //jpgエンコード
const imageminSvgo = require('imagemin-svgo'); //svgエンコード
const imageminOptipng = require('imagemin-pngquant'); //pngエンコード
const imageminGifsicle = require('imagemin-gifsicle'); //gifエンコード
const gulpWatch = require('gulp-watch'); //ファイル監視と実行
const plumber = require('gulp-plumber'); //エラーチェック
const notify = require('gulp-notify'); //エラーをデスクトップに通知
const browserSync = require('browser-sync').create(); //ブラウザシンク


//取得元、出力先のパス
const pathOrigin = {
  dest: './dest',
  dev: './src'
}
const paths = {
  html: {
    src: pathOrigin.dev + '/views/*.ejs',
    tempSrc: pathOrigin.dev + '/views/**/_*.ejs',
    dest:  pathOrigin.dest,
    map: '/map',
  },
  styles: {
    src: pathOrigin.dev + '/scss/*.scss',
    dest: pathOrigin.dest + '/css',
    tempSrc: pathOrigin.dev + '/scss/**/_*.scss',
    map: '/map',
    clearmap: pathOrigin.dest + '/css/map',
  },
  scripts: {
    src: pathOrigin.dev + '/js/*.js',
    dest: pathOrigin.dest + '/js',
    map: '/map',
    clearmap: pathOrigin.dest + '/js/map/',
  },
  images: {
    src: pathOrigin.dev + '/assets/*.{jpg,jpeg,png,svg,gif}',
    dest: pathOrigin.dest + '/assets',
  }
}
const srcpathAll = [paths.html.src, paths.html.tempSrc, paths.styles.src, paths.styles.tempSrc, paths.scripts.src, paths.images.src];


//画質コントロール
const imageQuality = {
  jpg: 60,
  png: [0.3, 0.5], //minimum, maximum
  svg: 60,
  gif: 1, //1 or 3, 1 is default
}

// sass
function stylesFunc() {
  return gulp
    .src(paths.styles.src, {sourcemaps: true, since: gulp.lastRun(stylesFunc)})
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(postcss([ autoprefixer({
      grid: 'autoplace', //gridレイアウトのプレフィックスを有効にする
    }) ]))
    .pipe(cleanCss())
    .pipe(gulp.dest(paths.styles.dest, { sourcemaps: paths.styles.map }));
}

// EJS
function htmlFunc() {
  return gulp
    .src([paths.html.src, '!' + paths.html.tempSrc], {since: gulp.lastRun(htmlFunc) })
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(ejs())
    .pipe(rename({extname:'.html'}))
    .pipe(htmlmin({
      collapseWhitespace : true, //余白を削除
      removeComments : true, //コメントを削除
      removeRedundantAttributes: true //default値の属性を削除
    }))
    .pipe(gulp.dest(paths.html.dest))
}

// JS
function scriptFunc() {
  return gulp
    .src(paths.scripts.src, { sourcemaps: true })
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(babel({
      presets: ['@babel/env']
    }))
    .pipe(uglify())
    .pipe (gulp.dest(paths.scripts.dest, { sourcemaps: paths.styles.map, since: gulp.lastRun(scriptFunc)}));
}

//画像処理
function imageFunc() {
  return gulp
  .src([paths.images.src], {since: gulp.lastRun(imageFunc)})
    .pipe(imagemin([
      mozjpeg({quality: imageQuality.jpg}),
      imagemin.optipng({optimizationLevel: imageQuality.png}),
      imagemin.svgo({removeViewBox: false}),
      imagemin.gifsicle({optimizationLevel: imageQuality.gif}),
    ]))
    .pipe(gulp.dest(paths.images.dest));
}

// サーバーを立ち上げる
function destServer(done) {
  browserSync.init({
      server: {
          baseDir: pathOrigin.dest,
          index  : "index.html"
      },
      reloadOnRestart: true,
  });
  done();
}

// ブラウザのリロード
function browserReload(done) {
  browserSync.reload();
  done();
  console.log(('reload done'));
}

function removeFile(path, name) {
  return del(path.replace(/src\/views/, 'dest').replace('ejs', 'html'));
};

// タスクを監視
function watchFiles() {

  //ファイルやディレクトリが削除されたらdest内の同盟ファイルを削除、それ以外ならstylesFuncを実行
  gulp.watch(paths.styles.src)
    .on('all', (event, path) => {
      if (event === 'unlink' || event === 'unlinkDir') {
        return del(path.replace(/src\/scss/, paths.css.dest).replace('scss', 'css'));
      }
      return stylesFunc();
  });
  gulp.watch(paths.styles.tempSrc, gulp.series(stylesFunc));

  gulp.watch(paths.html.src)
    .on('all', (event, path) => {
      if (event === 'unlink' || event === 'unlinkDir') {  
        console.log(path);
        return del(path.replace(/src\/views/, paths.html.dest).replace('ejs', 'html'));
      }
      return htmlFunc();
    });
  gulp.watch(paths.html.tempSrc, gulp.series(htmlFunc));

    gulp.watch(paths.scripts.src)
      .on('all', (event, path) => {
        if (event === 'unlink' || event === 'unlinkDir') {
          return del(path.replace(/src\/js/, paths.scripts.dest));
        }
        return scriptFunc();
    });

    gulp.watch(paths.images.src)
      .on('all', (event, path) => {
        if (event === 'unlink' || event === 'unlinkDir') {
          return del(path.replace(/src\/assets/, paths.images.dest));
        }
        return imageFunc();
    });
    gulp.watch(srcpathAll, gulp.series(browserReload));
}


// mapfile削除
function clearMap() {
  return del([paths.styles.clearmap]);
}


// タスクの実行
exports.default = gulp.series(gulp.parallel(stylesFunc, htmlFunc, scriptFunc, imageFunc), gulp.series(destServer, watchFiles));
exports.clearmap = clearMap;

メモ: ディレクトリ構造

このようにしていますが、変更したい場合はconst = pathsに記載のパスを書き換えてください。
スクリーンショット 2020-04-02 17.52.28.png

手順3-2:package.jsonにAutoprefixerの対応ブラウザ範囲を追記

package.jsonのdevDependencies{・・・},の後に、ベンダープレフィックスを対応するブラウザバージョンを指定します。
スクリーンショット 2020-04-02 21.38.53.png

package.json
  "browserslist": [
    "last 2 versions",
    "ie >= 11",
    "Android >= 4",
    "ios_saf >= 8"
  ]

手順3-3:タスクを実行する

ターミナルに次のコマンドを入力することで、タスクが実行されます。
ソースマップは、本番環境には不要なので削除用のタスクを作りました。

//デフォルトタスクの実行
gulp

//ソースマップ削除を実行
gulp clearmap

gulpfile設定にあたり参考にさせていただいたサイト

gulp公式(英語)
gulp4に移行するためにタスクを書き換えてみた
gulp4の設定方法 - SassやAutoprefixer、ejs、画像の圧縮などを自動化する

おわりに

もし間違いや、「こっちの方がいいよ!」というのがあれば是非教えてください。
参考にさせていただいたサイトや記事の運営・執筆者さま、ありがとうございました。


2020/5/4追記:以下のパッケージを追加しました。
node-sass-package-importer gulp-babel gulp-plumber
gulp-notify

11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10