Webサイトのコードは複雑化する一途で、繰り返しの作業が生じるのがごく当たり前になっています。しかし、世の中にはより優れた、効果的な開発プロセスがあるはずです。
このチュートリアルでは、Gulpを使ってWordPressのテーマ作成を自動化する方法と、ワークフローの自動化でテーマ作成のプロセスを改善する方法を紹介します。
なぜワークフローの自動化が必要なのか
ワークフローを最適化することは、開発プロセスにおいて非常に有益で、取り組む価値があります。その理由をいくつか挙げます。
- カスタムツールで、繰り返しの多い、つまらないタスクをすべて省ける
- ほかの重要なコアとなる開発作業に時間をかけられる
- すべてのアセットを縮小、最適化することでWebサイトのパフォーマンスを最適化できる
必要なこと
Gulpの紹介
GulpはJavaScriptのタスクランナーで、CSSの圧縮、Sassのコンパイル、画像の最適化、ブラウザーの更新など、時間がかかるタスクを自動化できます。
Gulpのツールを使えば、イベントの発生に従って、さまざまなアクションを自動化できます。たとえば、次のようなことができたらと想像してみてください。
- Sassファイルを保存するたびに、GulpがSassをコンパイルし、縮小化したCSSファイルを出力する
- フォルダーに新しい画像を追加すると、Gulpがこの画像を最適化し、新しい専用フォルダーに画像を移動する
- PHPまたはSassファイルを保存すると、Gulpが自動的にブラウザーをリロードする
Gulpのセットアップ
システムにGulpをグローバルインストールします。テーマ内にパッケージとしてインストールする方法については後述します。
Node.jsがインストールされていると仮定して、コマンドラインツールを開き、npmを使って次のようにGulpをインストールします。
npm install gulp -g
ここで、gulp -v(Gulpのバージョンのコマンド)を実行して、Gulpが正しくインストールされているかテストします。次のように出力されるはずです。
➜ ~ gulp -v
[09:33:59] CLI version 3.9.1
テーマのセットアップ
この記事では、Underscoresを基本のテーマとして使います。ダウンロードするには、underscores.meに進み、「gulp-wordpress」のように名前を付けて新しいテーマを作成します。WordPressのテーマディレクトリに直接ダウンロードして、そのあとダッシュボードで有効にします。
コマンドラインからテーマを追加したgulp-wordpressに直接進みます。以下は私の場合の例です。
cd ~/www/wordpress/wp-content/themes/gulp-wordpress
npm initコマンドを実行し、少しだけ簡単な操作をしてpackage.jsonファイルを作成します。このファイルにはテーマや後ほどインストールするパッケージの情報が入ります。
ここまで完了すると、次のような開始ファイルがあるはずです。
{
"name": "gulp-wordpress",
"version": "1.0.0",
"description": "WordPress Theme Development Automation with Gulp",
"author": "Name"
}
次に、開発に必要なGulpをインストールします。
npm install gulp --save-dev
Gulpパッケージのソースファイルを含むnode_modulesディレクトリはこのとき作成され、package.jsonファイルは依存関係のあるパッケージとしてGulpを含むように更新されています。
{
"name": "gulp-wordpress",
"version": "1.0.0",
"description": "WordPress Theme Development Automation with Gulp",
"author": "Author Name",
"devDependencies": {
"gulp": "^3.9.1"
}
}
gulp-autoprefixerのようなGulpのタスクの中にはES6のPromiseのサポートが必要なものもあるので、その場合はes6-promiseのポリフィルをインストールし、次のように、gulpfile.jsの先頭でES6のPromiseを呼び出します。
npm install es6-promise --save-dev
Gulp設定の最後のステップは、空の設定ファイルgulpfile.jsの作成です。JavaScriptやSassなどのGulpタスクを定義するのに使います。
gulpfile.jsの開始ファイルは次のようになります。
require('es6-promise').polyfill();
var gulp = require('gulp');
// default task
gulp.task('default');
まとめると次のようになります。
- ファイルの先頭でes6-promiseのポリフィルを呼び出し、Gulpにインポート
- defaultタスクを作成
Gulpが正常に作動し、すべてが完了したことを確認するために、コマンドラインでgulpを動かし、gulpfile.jsファイルに作成されたdefaultタスクを実行します。以下のように出力されるはずです。
[09:48:23] Using gulpfile ~/www/wordpress/wp-content/themes/gulp-wordpress/gulpfile.js
[16:33:13] Starting 'default'...
[16:33:13] Finished 'default' after 58 μs
Gulpのタスクで開発をスピードアップ
この時点で新しいタスクのテーマの準備ができたので、テーマ開発をスピードアップできる共通タスクを説明します。
CSS(Sass)で作業する
CSSを書くのにSassを使っているなら、自動化に必要なことが2つあります。1つはSassをCSSにコンパイルすること、そしてautoprefixerを使ってCSSにベンダープレフィックスを追加することです。Sassを例に説明しますが、もしLessなどを使いたければ、専用のGulpのプラグインもあります。
最初に、gulp-sassとgulp-autoprefixerをインストールします。
npm install gulp-sass gulp-autoprefixer --save-dev
次にSassディレクトリを基本的な構造で作成します。
├── sass
│ └── style.scss
style.scssファイルはメインとなるスタート地点です。自由にSassの構造を作成し、ほかのコンポーネント、モジュール、関数を好きなようにファイル内にインポートできます。
はじめの数行はWordPressに必要なスタイルシートのヘッダーです。
/*
Theme Name: Gulp WordPress
Theme URI: http://example.com/
Author: Author Name
Author URI: http://example.com/
Description: Description
Version: 1.0.0
License:
License URI:
Text Domain: gulp-wordpress
*/
body {
color: #333;
background-color: #fff;
}
次のステップでsassタスクを作成し、次の2つを実行します。
- コンパイルして、Sassのautoprefixerでベンダープレフィックスを追加
- 最終的にWordPressのCSSファイルとなるstyle.cssを構築
require('es6-promise').polyfill();
var gulp = require('gulp');
var sass = require('gulp-sass');
var autoprefixer = require('gulp-autoprefixer');
gulp.task('sass', function() {
return gulp.src('./sass/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(gulp.dest('./'))
});
gulp.task('default', ['sass']);
ここで、コマンドラインから直接gulp sassタスクを実行します。style.scssファイルをコンパイルし、テーマルートに新しいstyle.cssファイルを構築します。
sassを実行するもう1つの方法は、上に書いたようにタスク名を2番目のパラメーターとしてdefaultタスクに渡す方法です。gulpが起動すると、sassタスクが実行されます。
WordPressでの良い実践方法は、最終のCSSファイル内のCSSの目次をスタイルシートのヘッダーのすぐあとに作成し、コードやセクション関連のインポートの前で、目次のあとにCSSのコメントを追加するものです。
コメントは
/*----- 1.0 Normalize -----*/
のようにCSSの基本スタイルにし、
//----- 1.0 Normalize -----
などSassスタイルにならないよう注意してください。コメントは最終のCSSファイルに必要なので、SassスタイルだとSassコンパイラで隠されてしまいます。またコメントはスタイルシートのヘッダーと目次で使用することを覚えておいてください。
以下は目次と外部のSassファイルへのインポートを含むstyle.scssファイルの例です。
/*
Stylesheet Header ...
*/
/*--------------------
>>> TABLE OF CONTENTS:
----------------------
1.0 Normalize
2.0 Typography
3.0 Icons
4.0 Components
--------------------*/
/*----- 1.0 Normalize -----*/
@import 'normalize';
/*----- 2.0 Typography -----*/
@import 'typography';
rtl.cssファイルが自動生成されるように、gulp-rtlcssプラグインを使ってLTR (左から右)のCSSをRTL(右から左)に自動変換できます。1つのファイルにSassを書き、Gulpはstyle.cssとrtl.cssの2つのCSSファイルを生成します。
gulp-rtlcssの背景にある考え方はfloat、text-align、テキストの方向、そのほか左から右へのプロパティなどCSSプロパティをすべて変換することです。
2番目のプラグインはgulp-renameで、ファイル名をrtl.cssに自動的に変更します。
npm install gulp-rtlcss gulp-rename --save-dev
次にgulpfile.jsファイルの先頭に、新しくインストールしたプラグインを入れ、変換するためにstyle.cssが生成されたあとでrtlcss()を使用するsassタスクを修正します。
このステップでは、プラグインはフロートや左から右へのテキスト方向などのCSSプロパティをすべて変換して、ファイル名をrtl.cssに変更し、ファイルをテーマのルートディレクトリに出力します。
var rtlcss = require('gulp-rtlcss');
var rename = require('gulp-rename');
gulp.task('sass', function() {
return gulp.src('./sass/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(gulp.dest('./')) // Output LTR stylesheets (style.css)
.pipe(rtlcss()) // Convert to RTL
.pipe(rename({ basename: 'rtl' })) // Rename to rtl.css
.pipe(gulp.dest('./')); // Output RTL stylesheets (rtl.css)
});
gulp sassを実行すると、style.cssとrtl.cssファイルが生成されます。
Sassの構造について、またGulpでSassを使う方法についてもっと知りたい人は、以下を参考にしてください。
- Architecture for a Sass Project(Sassプロジェクトのアーキテクチャー)
- A Simple Gulp’y Workflow For Sass(簡単なSass用Gulpのワークフロー)
ファイルを監視する
Sassファイルを変更するたびにgulp sassを実行するかわりに、新しいタスクを呼び出して自動化します。
watchタスクはファイルに加えた変更を確認するのに使用し、このタスクはファイルが変更されるたびに、もう1つのアクションを実行します。たとえば、Sassファイルを保存すると、sassタスクが自動的に起動します。
gulpfile.jsファイルの内部に、新しいwatchタスクを追加して/sassディレクトリの変更を監視し、sassタスクを実行します。次にwatchタスクとともにdefaultタスクを更新します。
gulp.task('watch', function() {
gulp.watch('./sass/**/*.scss', ['sass']);
});
gulp.task('default', ['sass', 'watch']);
ここで、コマンドラインでgulpを動かしてsassタスクを実行すると、そのあとでwatchタスクが動き続けます。
エラーハンドリング
コードを書いていると、ときどき、作業の途中で定義していないSassの変数を書いてしまうことがあります。ファイルを見ている間は、特定のタスクがその変数をコンパイルできないのでGulpは中断することになり、作業を続けるにはGulpを再度動かさなければならないので面倒です。
こうしたエラーに対応してくれるのがgulp-plumberプラグインで、エラーでGulpが中断するのを防げます。
エラーハンドリングの改良には、エラーメッセージをカスタマイズするgulp-utilというユーティリティ機能パッケージをインストールして、エラーが起こるとブザー音が鳴るよう設定し、さらにどこにエラーがあるかわかるようエラーメッセージに色をつけます。
npm install gulp-plumber gulp-util --save-dev
var plumber = require('gulp-plumber');
var gutil = require('gulp-util');
var onError = function (err) {
console.log('An error occurred:', gutil.colors.magenta(err.message));
gutil.beep();
this.emit('end');
};
gulp.task('sass', function() {
return gulp.src('./sass/*.scss')
.pipe(plumber({ errorHandler: onError }))
//
//
});
上のコードで実行していることは、以下の2つです。
- onError機能を追加して、エラーメッセージをログに記録し、ブザー音を設定
- plumber機能を使うためにsassタスクを更新し、onError機能をerrorHandlerオブジェクトのプロパティに渡す
この設定で、何のエラーが起こっているのか、どのファイルに問題があるのかを確実に把握できます。またGulpが止まるのを防げるのが重要なポイントです!
以下は未定義のSass変数$colorの例です。
JavaScript
JavaScript用には異なるツールがあり、JavaScript開発のワークフローをスピードアップし改良できます。たとえば、次のようなことができます。
- たくさんのJavaScriptファイルを1つのファイルに連結
- コードエラーをJSHintで確認
- コードを縮小して、ファイルサイズをかなり小さくする
これらのプラグインは次のようにインストールします。
プラグインのインストールが終わったら、gulpfile.jsファイル内に新しくインストールしたプラグインを読み込み、新しいjsタスクを追加します。
var concat = require('gulp-concat');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
gulp.task('js', function() {
return gulp.src(['./js/*.js'])
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(concat('app.js'))
.pipe(rename({suffix: '.min'}))
.pipe(uglify())
.pipe(gulp.dest('./js'))
});
このタスクは./jsディレクトリ内の.jsで終わるファイルであればどのファイルでもOKです。jshintでコードエラーを確認してから、コードエラーをapp.jsに連結し、縮小した出力が必要なので、ファイル名をapp.min.jsに変更します。そのあとコードを縮小し、最後にファイルを./jsディレクトリに出力します。
この時点で、テーマのルートディレクトリに.jshintrcという構成ファイルを作成します。.jshintrcはシンプルなJSONファイルで、JSHintオプションを有効にするか否かを以下のように指定します。
{
"undef": true,
"unused": true,
"browser": true
}
- undef:宣言されなかった変数を使うときに警告
- unused:変数を定義して一度も使用されていないときに警告
- browser:最新ブラウザーで登場したnavigatorやdocumentなどのグローバルオブジェクトを定義
JSHintには用途に合わせて使えるよう、たくさんのオプションが用意されています。
コマンドライン(またはターミナル)からgulp jsを実行します。後ほどテーマで使うapp.min.jsという新しいファイルが生成されます。
デフォルトで、_underscoreのテーマには/jsディレクトリの下にcustomizer.js、navigation.js、skip-link-focus-fix.jsファイルがあります。
特定のファイルを入れる必要があるときだけ、次のようにgulp.src配列に追加できます。
gulp.task('js', function() {
return gulp.src([
'./js/navigation.js',
'./js/skip-link-focus-fix.js'
])
});
このコードはnavigation.jsとskip-link-focus-fix.jsの2つのファイルを実行します。ほかの新しいファイルを追加する必要があれば、この配列に追加できます。
またwatchタスクを更新して、JavaSciptファイルの変更を監視できます。defaultタスクを更新すると、Sassのようにgulpを起動するときにjsアクションを自動的に実行できます。
gulp.task('watch', function() {
gulp.watch('./sass/**/*.scss', ['sass']);
gulp.watch('./js/*.js', ['js']);
});
gulp.task('default', ['sass', 'js', 'watch']);
functions.phpというテーマファイル内に、以下のように生成されたapp.min.jsファイルをエンキューします。
wp_enqueue_script( 'gulp-wordpress-javascript', get_template_directory_uri() . '/js/app.min.js', array(), '20151215', true );
エンキューしたほかのJavaScriptファイルはすでに連結、縮小されて1つのファイルになっているので削除できます。現在のHTTPでは、たくさんのファイルより1つのファイルにすることが、Webサイトのパフォーマンスのスピードアップと改善につながります。
functions.phpファイル内のエンキューするスクリプト機能は次のような形で終わります。
/**
* Enqueue scripts and styles.
*/
function gulp_wordpress_scripts() {
wp_enqueue_style( 'gulp-wordpress-style', get_stylesheet_uri() );
wp_enqueue_script( 'gulp-wordpress-javascript', get_template_directory_uri() . '/js/app.min.js', array(), '20151215', true );
}
add_action( 'wp_enqueue_scripts', 'gulp_wordpress_scripts' );
画像
いよいよGulpで画像の最適化をします。画像の最適化によって、テーマ内で使っているすべての写真の表示速度が自動的に最適化されます。この自動化をより簡単にするには、Gulpタスクをセットアップして、画像のディレクトリを監視します。画像を画像のディレクトリにドラッグするとGulpは画像を最適化して、準備済みの最適化した画像が入る別のフォルダーに移します。
2つのフォルダーを作成します。
- /images/src:オリジナル画像が入っているソースフォルダー
- /images/dest:最適化した画像が入るフォルダー
gulp-imageminをインストールしてPNG、 JPEG、 GIF、SVG形式の画像を縮小します。
npm install gulp-imagemin --save-dev
新しいタスク(images)を作成します。imagesタスクはソースフォルダー(/images/src)にある画像を監視して最適化し、最適化フォルダー(/images/dest)に移動します。
var imagemin = require('gulp-imagemin');
gulp.task('images', function() {
return gulp.src('./images/src/*')
.pipe(plumber({errorHandler: onError}))
.pipe(imagemin({optimizationLevel: 7, progressive: true}))
.pipe(gulp.dest('./images/dist'));
});
また、新しい画像を/images/srcフォルダーにドラッグするたびにimagesタスクが実行されるので、/images/srcフォルダーを監視します。さらにdefaultタスクを更新して、imagesアクションを実行します。
gulp.task('watch', function() {
//
gulp.watch('images/src/*', ['images']);
});
gulp.task('default', ['sass', 'js', 'images', 'watch']);
BrowserSyncでブラウザーをリフレッシュする
コードがPHPであれ、Sassであれ、JavaScriptであれ、コード変更後にはブラウザーをリロードしたいものです。また、複数のブラウザーやモバイルデバイスで試すことでしょう。試す場合、いちいちページごとにリロードボタンを押さなければなりませんが、BrowserSyncを使えばリロードを効果的かつ自動的にできます。
依存関係のあるパッケージとしてBrowserSyncをインストールしておく必要があります。
npm install browser-sync --save-dev
次にgulpfile.jsファイル内にBrowserSyncを読み込み、BrowserSyncにも適用されるようにwatchタスクを更新します。
var browserSync = require('browser-sync').create();
var reload = browserSync.reload;
gulp.task('watch', function() {
browserSync.init({
files: ['./**/*.php'],
proxy: 'http://localhost:8888/wordpress/',
});
gulp.watch('./sass/**/*.scss', ['sass', reload]);
gulp.watch('./js/*.js', ['js', reload]);
gulp.watch('images/src/*', ['images', reload]);
});
ローカルの開発用URLのプロキシオプションを更新しなければならないことに注意してください。たとえば、ローカルURLがlocalhost:8888/wordpressなら、プロキシの値もそれに合わせて更新します。
BrowserSyncが作業中にファイルを監視してくれるので、たとえば私はPHPファイルの変更を監視するタスクを設定しました。
また、sass、js、imagesのwatchタスクを更新して、ファイルの変更や画像フォルダーの中身が変わったときにページをリロードするようしました。
これでgulpを実行できます。ブラウザーで新しいタブが自動的にローカルホストURLを開き、コンソールは次のようになっています。
[BS] Access URLs:
--------------------------------------------
Local: http://localhost:3000/wordpress/
External: http://192.168.1.2:3000/wordpress/
--------------------------------------------
UI: http://localhost:3001
UI External: http://192.168.1.2:3001
--------------------------------------------
同じネットワークに接続されたデバイスなら外部URLを使用でき、変更のたびにBrowserSyncがすべてのブラウザーをリロードします。
UI External URLはBrowsersyncのコントロールパネルで使用され、コントロールパネルでは同期オプションの変更やデバイス管理ができます。
最後に
ここまで説明してきたように、自動化ツールを使った作業は非常に有益で、開発プロセスのスピードを上げられます。たくさんのGulpのプラグインがありますので、必要に応じてぜひ利用してみてください。自動化して時間を節約できるものがあれば、ぜひ、やってみましょう!