Gulp + Webpack + Babel + Vue.js で作るWordPressテーマ開発環境

# 動機

- WrodPress（REST API）改修案件でトランスパイルされたJSと格闘
- JQueryそろそろもういいんじゃねぇの？ → Vue.js使ってみよ
- 運用フェーズでのJavascriptの見通し

JQueryの、このライブラリ使いたい！どうしても！（そうしたら工数圧縮できるのに...）
って人のために Vue.js + JQuery の共存も考慮しています。

#フロントエンド開発環境界隈

`browserify` から `Webpack` の台頭があったようで
正直どっちでもいいけど多数派を選択したいところ

## 概念

*2017年くらい*

```
Gulp > Webpack > babel > Vue
——————————————————
 - stream   (webpack-stream) 
		-loader     (babel-loader) (vue-loader)
                                  + present
```

*2015年くらい*

```
Gulp > browserify > babel > Vue 
——————————————————
 - stream    (vinyl-source-stream)
                - ~ify          (babelify)  (vueify)
                                  + present
```

Gulp + Webpack + Babelの概念に関してはこちらが秀逸
参考：[gulpとbabelとwebpackというフロント開発３銃士](https://qiita.com/bakira/items/3c4e2d10aae085767817)

Gulp先輩 > Webpack先輩 > Babel君という理解でいいかと思います。

チュートリアルに従って、`npm i -D`していくとなんだかわけの分からないものを何も考えずにインストールしていることが多いので、モジュール類も含めて概念をまとめてみました。

Gulp先輩の下に `webpack` や `browserify` を従えるときは、 `-stream`モジュールが必要で、
`webpack` や `browserify` の下に `babel` を従えるときは、 `〜-loader` や `〜 ~ify` モジュールが必要という理解でいきます。

タスクを渡すときはこのモジュールを介して渡していきます。
Gulpモジュールの親玉に `plumber` が居るって感じでしょうか。

### そのほかの便利モジュール

- vue-template-compiler ( `.vue` を使えるようにする)
- eslint (コードチェック) : eslint-for-vue
 
### いいわけ

※2018年初頭に「babel / vue 」キーワードでGoogleで検索して把握した概要です。
※Sassはそもそもcssをjsに統合する必要性がわからなかったので、node-sassに任せておきます。
※執筆時点でvueの構文（便利さ）は理解していません。
※全体をVue,jsで組むなら、vue-cliが始めやすいらしいですよ。


## 各種設定ファイル

### ディレクトリ構成

大事なのは設定ファイルの位置とコンパイラを動かして、出力されたディストリビューションファイルの位置関係かと思います。

別記事で[DockerをつかったWordPressテーマ開発環境を整えた](https://qiita.com/KoichiSugimoto/items/fc993e2d52dbed39fc79)ので、それに従っています。VCCWや mampp / xampp 環境の場合は出力ファイルの位置が変わるかと思うので設定ファイルのパスを読み替えてください。


```text:Dir構成
package.json　←　いまからつくる
gulpfile.js　←　いまからつくる
--- ここから Docker ---
public (Wordpressディレクトリ)
db-data / wordpress-dump-xxxxxxx.sql
docker-compose.yml
```

# 初期設定

### package.json (node)

最初につくるときは `npm init`で、空の `package.json` ができあがります。

```
$ npm init
```

```text:Dir構成
package.json　←　出来た
--- ここから Docker ---
public (Wordpressディレクトリ)
db-data / wordpress-dump-xxxxxxx.sql
docker-compose.yml
```

`npm i -D XXX` していくとプロジェクトごとのpackage.jsonが仕上がっていきます。
(`i` は `install`、 `-D` は `--save-dev` の略)

```
$ npm i -D gulp gulp-autoprefixer gulp-clean-css gulp-notify gulp-plumber gulp-sass
$ npm i -D webpack webpack-stream
$ npm i -D babel-core babel-loader babel-preset-env
$ npm i --save vue
$ npm i -D vue-loader vue-template-compiler
```
わかりやすく行分けしていますが、もちろんワンライナーでもインストールできます。
gulpのもろもろは個人的な趣味です。

```
npm WARN vue-loader@13.7.0 requires a peer of css-loader@* but none is installed. You must install peer dependencies yourself.
```

`vue-loader`いれると`css-loader`も入れろって出てきます。
素直にいれとこう

```
$ npm i -D css-loader
```

`package.json` があるディレクトリで`npm install`または`npm i`を実行すると`node_module`がインストールされます。
チームで開発するときは、プロジェクトディレクトリのソースを共有して、`git pull`して`npm install`してくださいねって言うだけでいい。
（GulpなどはCLIで実行することになるので、グローバルインストール`npm i -g gulp`が必要ですね）

Vueテンプレート（.Vue）style-cssを含むをコンパイルできるようにする

```
$ npm i -D vue-style-loader
```

出来上がった`package.json`がこんな感じ

```json:package.json
{
  "name": "example",
  "version": "1.0.0",
  "description": "######",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-env": "^1.6.1",
    "css-loader": "^0.28.9",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^4.1.0",
    "gulp-clean-css": "^3.9.2",
    "gulp-notify": "^3.2.0",
    "gulp-plumber": "^1.2.0",
    "gulp-sass": "^3.1.0",
    "jquery": "^3.3.1",
    "vue-loader": "^13.7.0",
    "vue-template-compiler": "^2.5.13",
    "webpack": "^3.10.0",
    "webpack-stream": "^4.0.0",
    "vue-style-loader": "^4.1.2"
  },
  "dependencies": {
    "vue": "^2.5.13"
  }
}

```

### gulpfile.js

varじゃなくて、constって書けるんですね！
webpackは素通りしてきたので、webpack.configは書かない（gulpfileにまとめる）方向でひとつお願いします。

```text:Dir構成
package.json　←　Done
gulpfile.js　←　いまからつくる
--- ここから Docker ---
public (Wordpressディレクトリ)
db-data / wordpress-dump-xxxxxxx.sql
docker-compose.yml
```

注） `example` はテーマ（テーマディレクトリ）名とします。
注） `paths` は各々読み替えて！

```js:gulpfille.js
const gulp = require('gulp'),
	plumber = require("gulp-plumber"),
	sass = require('gulp-sass'),
	cleancss = require('gulp-clean-css'),
	autoprefixer = require('gulp-autoprefixer'),
	notify = require('gulp-notify'),
	webpack = require('webpack'), // Let's use webpack.DefinePlugin
	webpackStream = require('webpack-stream'), // Let's use webpack3
	VueLoaderPlugin = require('vue-loader'); // vue-loader

const paths = {
	"url":"localhost:8802",
	"css": "./public/wp/wp-content/themes/example/assets/css/",
	"scss": "./public/wp/wp-content/themes/example/assets/scss/**/*.scss",
	"entry": "./public/wp/wp-content/themes/example/assets/js/src/main.js",
	"js": "./public/wp/wp-content/themes/example/assets/js/",
}

//Sass
gulp.task('sass',function(){
    return gulp.src(paths.scss)
    .pipe(plumber())
    .pipe(sass({outputStyle: 'expanded'}))
	.pipe(cleancss({debug: true}, function(details) {
            console.log(details.name + ': ' + details.stats.originalSize + ' > ' +  + details.stats.minifiedSize);
    }))
	.pipe(autoprefixer({
		browsers: ["last 2 versions", "ie >= 9", "Android >= 4","ios_saf >= 8"],
		cascade: false
	}))
    .pipe(gulp.dest(paths.css))
	.pipe(notify("Sass Compile Success!"));
});

//Webpack JS bundle
gulp.task('bundle', function(){
	return gulp.src( [ paths.entry ] )
		.pipe(plumber())
		.pipe(webpackStream({
			// watch: true, // webpack の watch ではなく gulp の watch を使う
			output: {
				filename: 'build/bundle.js',
			},
			module: {
				rules: [
					{
					  test: /\.vue$/,
					   loader: 'vue-loader',
					   options: {
					       options: {
					            presets: [
					                'es2015',
					                     {
					                     targets: {
					                         browsers: ['last 2 versions', 'safari 7', 'edge 14'],
					                     },
					                },
					            ],
					        },
					    // other vue-loader options go here
					    },
					},
					{
						test: /\.js$/,
						resourceQuery: paths.js + 'src/',
						loader: 'babel-loader',
						options: {
					            presets: [
					                'es2015',
					                     {
					                     targets: {
					                         browsers: ['last 2 versions', 'safari 7', 'edge 14'],
					                     },
					                },
					            ],
						}
					},
					{
					    test: /\.css$/,
					    oneOf: [
					        {
					            resourceQuery: /module/,
					            use: [
					                'vue-style-loader',
					                {
					                    loader: 'css-loader',
					                    options: {
					                        modules: true,
					                        localIdentName: '[local]_[hash:base64:5]',
					                    },
					                },
 					            ],
					        },
					        // this matches plain `<style>` or `<style scoped>`
					        {
					            use: ['vue-style-loader', 'css-loader'],
					        },
					    ],
					},
				],
			},
			resolve: {
				extensions: ['.js', '.vue'],
				modules: [
					"node_modules"
				],
				alias: {
				}
			},
			//環境によってビルドする内容を分ける
			plugins: [
				new webpack.DefinePlugin({
					'process.env': {
						NODE_ENV: '"production"'
					}
				}),
				new webpack.optimize.UglifyJsPlugin(),
				new webpack.optimize.DedupePlugin(),  // ライブラリ間で依存しているモジュールが重複している場合、二重に読み込まないようにする
				new webpack.optimize.AggressiveMergingPlugin(),  //ファイルを細かく分析し、まとめられるところはできるだけまとめてコードを圧縮する
				// JQuery / JQueryライブラリのための定義（Jquery絶対使わないってプロジェクトのときは消去）
				new webpack.ProvidePlugin({
					jQuery: "jquery",
					$: "jquery",
					jquery: "jquery"
				}),
				// vueコンポーネント
				new VueLoaderPlugin(),
			]
		}, null, function(err, stats) {
			/* Use stats to do more things if needed */
			if (stats.compilation.errors.length) {
				notify.onError({
					title: 'Webpack error',
					message: stats.compilation.errors[0].error,
					sound: 'Frog',
				});
			}
		}))
		.pipe(gulp.dest( paths.js ))
		.pipe(notify("Bundle JS Success!"));
})

gulp.task('default', ['sass','bundle'], function () {
	gulp.watch(paths.scss, ['sass']);
	gulp.watch( [paths.entry], ['bundle']);
});

```

チームで開発するときは、`gulp` してくださいねって言うだけでいい。

## WordPress でVue-JSを使う

WordPressテーマディレクトリの `function.php` に以下を追加
header直書きよりこちらがおすすめ

```php:function.php
wp_enqueue_script( 'vue-js', 'https://cdn.jsdelivr.net/npm/vue', [], 'latest', true );
wp_enqueue_script( 'example-bundle', get_theme_file_uri( '/assets/js/build/bundle.js' ), array( 'jquery’, 'vue-js' ), '1.0', true );
```

# 今後の展望

- `.env` でプロジェクトごとの設定を管理

# 最近書いた記事

[Dockerで作るWordPressテーマローカル開発環境](https://qiita.com/KoichiSugimoto/items/fc993e2d52dbed39fc79)
[Gulp + Webpack(+Babel)でシンプルJQuery構成](https://qiita.com/KoichiSugimoto/items/283d967169546c3f2dce)
[Gulp + yarn シンプルJQuery構成](https://qiita.com/KoichiSugimoto/items/2136dfb1e9679868abba)

# 参考にさせていただいたサイト（多謝）
[gulpとbabelとwebpackというフロント開発３銃士](https://qiita.com/bakira/items/3c4e2d10aae085767817#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%AE%E5%AF%BE%E5%BF%9C%E7%8A%B6%E6%B3%81)
[Vue.js 単一ファイルコンポーネントを使って開発環境を強化する](http://chibinowa.net/note/vuejs/vue-5.html)
[Vue.js 開発環境を手作りしてみた手順（自分メモ）](http://ngzm.hateblo.jp/entry/2017/10/04/195329)
[GulpでVue+SASSの開発環境を構築する](http://www.webopixel.net/develop/1221.html)

# 修正履歴

2018.02.05 - `vue-template-compiler` `vue-style-loader` をインストール、Vueコンポーネント（.vue）とその中のcssをコンパイルできるように修正