Laravel mix 始めました

# 導入
これは [エキサイト Advent Calender 2018](https://qiita.com/advent-calendar/2018/excite) の記事です。

弊社でもPHPのフレームワークとしてLaravelを使用することが多くなってきましたが、
まだ何故かLaravel Mixについては本格的に使用しているプロジェクトは少ないように思います。

そこで、今更ながらLaravel Mixの導入を紹介したいと思います。

以下、試したときの主なバージョン
(社内環境ではなく、僕のローカルVMですがなんか微妙に古い。。。)

```bash
$ php -v
PHP 7.1.8 (cli) (built: Aug  9 2017 13:20:06) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.1.8, Copyright (c) 1999-2017, by Zend Technologies

$ node -v
v8.11.4

$ npm -v
5.6.0

$ npm ls --depth=1 | grep laravel-mix
laravel-mix@2.1.14

$ composer info | grep laravel
laravel/framework                     v5.5.44  The Laravel Framework.
```



# Laravel Mix とは？
https://laravel.com/docs/5.7/mix
https://laravel-mix.com/
> An elegant wrapper around Webpack for the 80% use case.

と公式サイトにある通り、
**80%のユースケースカバーしたWebpackの良い感じのラッパー**
です。

そのうたい文句の通り、ややとっつきにくい印象のある[Webpack](https://webpack.js.org/)の設定をほぼ意識せず

- jsのモジュール分割
- babelによるトランスパイル
- vueコンポーネントの利用
- scssのコンパイル
- js&cssのMinify


等の機能が簡単に使えます。

# 使い始めるには？
LaravelのプロジェクトでなくてもLaravel Mix単独でも使えますが、
Laravelの場合はデフォルトで必要最低限のnpmパッケージが設定されたpackage.jsonが付いてくるので
これを使うとすんなりインストールできます。

*[composer](https://getcomposer.org/)と[npm](https://www.npmjs.com/)は使える前提です！*

```bash
# Install Laravel
composer create-project --prefer-dist laravel/laravel sample1
cd sample1/
# Install npm packages in package.json
npm i
```

**ただしLaravel 5.5LTS系を使う場合は、package.jsonで指定してるパッケージのバージョンが古いので**
**`npm i`する前にpackage.json内のdevDependenciesを書き変えてあげたほうが良いです。**

# やりたいこと
フロントエンドのビルド処理で必要な処理と言えば

- jsのモジュール分割
- scssの利用
- js, cssのMinify。ただし本番環境だけ。
- eslint, stylelint によるjsとscssのsyntax check
- ブラウザキャッシュ対策のクエリ生成
- 共通モジュールのvendor.js化

あたりが挙げられると思いますが、上記を実現するためのLaravel Mixの設定や必要なファイルを
紹介したいと思います。
以下のファイルをコピって来れば、そのまま使い始められるはず！。。。

## 必要なファイル
下記のファイル達をプロジェクトルートに置くor修正してください。

- package.json
  - 入れてるパッケージはこんな感じ。あとはbootstrapとかjqueryとかvueとか。
  - `npm -i パッケージ名 -D` とかで入れてください
  - （足りてなかったらごめんなさい。。。）

```js:package.jsonのdevDependenciesに入れるパッケージ
laravel-mix-eslint
laravel-mix-stylelint
eslint
eslint-loader
eslint-plugin-vue
stylelint
stylelint-config-recommended-scss
stylelint-scss
stylelint-webpack-plugin
```

- webpack.min.js
  - こんな感じにしてます
  - 各ページ毎にjsとcssを分離するため、複数のエントリポイントを設定してます。
  - エントリポイントに設定したjsとscssファイルがあるかを見て、ある場合はビルドします。
  - webpackConfigのresolveを↓のようにすると、`resource/assets/` 以下のファイルがjsやscssの中から読み込みやすくなります。
      - scssの`@import`とかなら`@import "~sass/variables"`で`resource/assets/sass/_variables.scss`を読み込める
      - jsの`require()`でファイル探索するルートディレクトリに`resource/assets/`が追加される
  - `npm run watch` でeslintとstylelintが走るようになってます。
  - `jquery`とかの共通で使うモジュールについては別ファイルにまとめるようにしてます。
  - productionビルド時だけブラウザキャッシュ対策のクエリが付きます。

```js:webpack.mix.js
const mix = require('laravel-mix');
const path = require('path');
const fs = require('fs');

require('laravel-mix-eslint');
require('laravel-mix-stylelint');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

const sassOptions = (mix.inProduction()) ?
    {outputStyle: 'compressed'} : {outputStyle: 'nested'};

// ページ毎のjs,scssのエントリポイントをここに追加する
const entryPoints = [
    'index',
    'home',
    'mypage/index',
    'common'
];

for (let ep of entryPoints) {
    // ↓はLaravel 5.5用のpathなのでそれ以降のバージョンの場合は適宜修正してください。
    let jsPath = `resources/assets/js/${ep}.js`,
        sassPath = `resources/assets/sass/${ep}.scss`;

    try {
        fs.accessSync(path.resolve(jsPath));
        mix.js(jsPath, 'public/js');
    } catch (err) {}
    try {
        fs.accessSync(path.resolve(sassPath));
        mix.sass(sassPath, 'public/css', sassOptions);
    } catch (err) {}
}

mix.webpackConfig({
        output: {
            publicPath: '/hogehoge/'
        },
        resolve: {
            modules: [
                path.resolve('./resources/assets/'),
                'node_modules'
            ]
        }
    })
    .eslint()
    .stylelint({configFile: './.stylelintrc.js', files: ['**/*.scss']})
    .extract(['jquery', 'bootstrap', 'vue']);

if (mix.inProduction()) {
    mix.version();
}
```

- .eslintrc.js
  - laravel-mix-eslint用
  - ルールはゆるめ。。。

```js:.eslintrc.js
let rules = {
    "no-unused-vars": ["warn", {"vars": "local", "args": "none"}],
    "no-undef": "warn",
    "no-redeclare": "warn",
    "no-debugger": "warn",
    "no-console": "warn",
    "no-empty": "warn"
};
if (process.env.NODE_ENV == "production") {
    rules["no-debugger"] ="error";
    rules["no-console"] ="error";
}

module.exports = {
    extends: [
        "eslint:recommended",
        "plugin:vue/essential" // vue.js使ってなくてもlaravel-mix-eslintの仕様上必要
    ],
    env: {
        browser: true,
        es6: true,
        amd: true,
        jquery: true
    },
    parserOptions: {
        ecmaVersion: "2018"
    },
    rules
};
```

- .stylelintrc.js
  - 推奨設定だけ。。

```js:.stylelintrc.js
module.exports = {
    "extends": "stylelint-config-recommended-scss"
};
```

# 主なコマンド
Laravelの場合、package.jsonにデフォルトでscriptsが設定されているので
下記コマンドが使えます。
https://github.com/laravel/laravel/blob/master/package.json


開発時にファイルを監視して自動でビルドしてくれる

```bash
npm run watch
```

開発で1回だけビルドする

```bash
npm run dev
```

本番環境用にビルドする

```bash
npm run prod
```

# テンプレート側
Laravel Mixでビルドしたアセットファイルを使う時はbladeテンプレート側で`mix()`ヘルパーを使います。
（productionビルド時のクエリとかを勝手に付けてくれる）

```php:hoge.blade.php
<link href="{{ mix('/css/hello.css') }}" rel="stylesheet" type="text/css"/>
<script src="{{ mix('/js/hello.js') }}" type="text/javascript"></script> 
```

みたいな。

# 所感
まさに
> An elegant wrapper around Webpack for the 80% use case.

↑その通り！と言った感じで、よく使うパターンのWebpackの設定が簡単に書けるようにラップされてて良く出来てるなぁと思いました。
（entryとloaderの概念がうまくまとまってるところとか。）

80%と言ってる通り、めっちゃ細かくWebpackの設定を詰めてたいとか、必ず最新のパッケージで揃えたい、みたいな場合だと
ちょっと物足りないかもしれませんが、
プラグインも割と簡単に書けそうなのでだいたいは事足りる気がします。

現状の感想としては、なにはなくともjs&cssはwebpack通すようにしたほうが色々便利なので
そういう場合にLaravel Mixならサクッと簡単な設定で導入できるので、新規でも既存プロジェクトでもおすすめです！

# やり残したこと
- もうちょっとplugin追加したい。。
- tips. とか書ければよかった。。

長くなるし、力尽きたのでここで終わりにします。


**明日のエキサイトAdvent Calenderもよろしくお願いします！**