Laravelを使っていないプロジェクトでLaravel Mixを導入した話

[ラクス Advent Calendar 2017](https://qiita.com/advent-calendar/2017/rakus) の6日目の記事です。昨日は[@fnzzz](https://qiita.com/fnzzz)さんによる「 [VUIのデザインについて考察してみた](https://qiita.com/fnzzz/items/2f110040bd9f4780dc7a) 」でした。
本日はこの記事でQiita初投稿となります、[@amderbar](https://qiita.com/amderbar)です。

# はじめに

先日、個人的に作っているWebアプリケーションにLaravel Mixを導入しましたのでそのお話です。大筋は「[Laravel Mixでwebpackをもっと簡単に使う方法](https://www.webprofessional.jp/use-laravel-mix-non-laravel-projects/)」に従っていますが、1点だけハマった点があったのでそこも含めてお話しします。

# Laravel Mix とは

本題に入る前に簡単にLaravel Mixのご紹介です。

[Laravel Mix](https://readouble.com/laravel/5.5/ja/mix) はPHPフレームワーク「[Laravel](http://laravel.jp)」に内包される[webpack](http://webpack.github.io/)のAPIラッパーです。webpackは便利なようですが、ちょっと見ただけでも自由度が高い分設定が複雑で敷居が高い印象を受けます。

ですがLaravel Mixを使えば煩わしい設定に悩むことなく簡単に導入でき[^1]、運用もらくちんです。

しかもLaravel自体はこれの使用を強制しているわけでもなく、Laravel Mixの方もLaravelに密結合しているわけではありません。Laravel Mixだけを拝借し、Laravel本体を使わないプロジェクトに組み込むこともできるんです[^2]。

[^1]: この記事の話は導入でハマった話なので悩みどころがないわけではありませんが、解決策が分かっているなら簡単です。
[^2]: MIT licenseなのでその辺も問題ない、はずです。

# 要件

導入したプロジェクトは以下のような構成になっていました。

## 環境

開発に使用しているマシンのOSはWindows10、Node.js、npmはインストールしていませんでした。

サーバー側プログラムではPHPを使用。`autoload`を使いたかったので[composer](https://getcomposer.org)は入れましたが、Laravelその他フレームワークは使っていません。

フロント側では[jQuery](https://jquery.com)と[Vue.js](https://jp.vuejs.org)を読み込んで使用していましたので、Laravel Mix導入後もそれらを使用できるようにします。

## ディレクトリ構成

```
プロジェクトルート
├─app
│  ├─Actions
│  ├─Processes
│  ├─Requests
│  └─Views : 各画面のHTMLの格納場所
├─lib
├─public : 公開ディレクトリ
│  │  index.php
│  ├─css
│  ├─img
│  └─js
├─setup
├─storage
├─tests
└─vendor
```

# 導入の流れ

## NodistのインストールとNode.js, npmの入手

Windows環境でNode.jsを使うことになるので、NodistをインストールしてNode本体とnpmを入手しましょう。[GitHubリリースページ](https://github.com/marcelklehr/nodist/releases)からインストーラをダウンロードしてインストールします。

Nodistについては[こちら](https://qiita.com/siouxcitizen/items/63e17ccae386ffa17563)を参考にさせていただきました。ただNodistのGitHubのリポジトリを見に行くと

> [LOOKING FOR A MAINTAINER :) ]

と書かれていていて今後使い続けられるかどうか不安な感じですが…。

ともあれ、インストールしてパスを通したら、コマンドが使えるかを確認します。

```
$ nodist -v
0.8.8
```

せっかくなのでNodeとnpmは最新版を使うことにしましょう。

```
$ nodist latest
$ nodist
  (x64)
  7.2.1  (global: v7.2.1)
> 9.2.0  (env: 9.2.0)
$ nodist npm latest
$ node -v
v9.2.0
$ npm -v
5.6.0
```

## package.jsonを作成してLaravel Mixをインストールする

[LaravelのGitHubリポジトリ](https://github.com/laravel/laravel/blob/master/package.json)から`package.json`をもらってきて、プロジェクトのルートディレクトリに配置します。
他のパッケージを使っている、ないし使いたい場合はそれを`dependencies`に含めます(`devDependencies`でもいいですが)。今回は`eslint`も一緒に入れることにしました[^3]。

[^3]: 別に最初から`package.json`に書いておかなくても、あとから`npm install hoge --save-dev`とした方がその時の最新版を入れてくれるのでいいかもしれませんね。

```json:package.json
{
    "private": true,
    "scripts": {
        "dev": "npm run development",
        "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch-poll": "npm run watch -- --watch-poll",
        "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
        "prod": "npm run production",
        "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
    },
    "devDependencies": {
        "axios": "^0.17",
        "bootstrap-sass": "^3.3.7",
        "cross-env": "^5.1",
        "eslint": "^4.9.0",
        "jquery": "^3.2",
        "laravel-mix": "^1.0",
        "lodash": "^4.17.4",
        "vue": "^2.5.7"
    }
}
```

`package.json`ができたなら、以下のコマンドでパッケージインストールします。

```
$ npm install
```

依存関係でやたらパッケージがインストールされますが、少し待てば終わります。

## webpack.min.jsの編集

Laravel Mixはwebpackの設定ファイルを自分で作らなくてよく、コンパイル対象のアセットを指定するファイル「`webpack.mix.js`」を作成するだけです。生のwebpackはこの辺の設定がめんどうっぽいですが、Laravel Mixなら簡単ですね。

これも[LaravelのGitHubリポジトリ](https://github.com/laravel/laravel/blob/master/webpack.mix.js)からもらって来てルートディレクトリに配置してしまうのがらくちんでいいです。

いいのですが、そのままだと**ハマり**ます。

具体的には後述のコマンドでビルドしようとすると進捗95％で止まったまま進行しなくなります。さらに`mix-manifest.json`というファイルが`public`ディレクトリ下に作られるはずが、なぜかプロジェクトルートディレクトリ下に作られてしまい内容もおかしなものになります。

```javascript
mix.setPublicPath('public');
```

これ。Laravelを使っていない場合、ハマりポイントを回避するにはこの一行が必要です。参考サイトでは記載されていませんが、この問題自体はLaravel MixのリポジトリのIssueでも話題に挙がっていていました。
→[publicPath is not set when not using laravel #807](https://github.com/JeffreyWay/laravel-mix/issues/807)

以下は件の文を追加してちゃんと動くようにしたコード。`'public'`はプロジェクトの公開ディレクトリを指します（`webpack.mix.js`からの相対パス指定だと思います）。

```javascript:webpack.mix.js
let mix = require('laravel-mix');

mix.setPublicPath('public');
mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css');
```

後はサンプルを参考に設定を記述すればOKです。

`mix.js(..).sass(..)`部分でwebpackでコンパイルするjsファイルとSCSS／Sassファイル、さらに出力するディレクトリを定義しています。画面ごとにファイルを分けたいときなんかも、メソッドチェーンで指定を増やすだけです。

## ソースとなるアセットを作成

`webpack.mix.js`ファイルにコンパイル対象のファイルを定義しましたが、ソースファイルをまだ作っていませんね。`webpack.mix.js`での記述に合わせ、次のようなディレクトリ構造に変更します。

```
├─app
├─lib
├─public : 公開ディレクトリ
│  │  index.php
│  ├─css
│  ├─img
│  └─js
├─resources ← 追加
│  ├─assets : アセットの格納場所
│  │  ├─js
│  │  │  │  app.js
│  │  │  │  bootstrap.js
│  │  │  └─components
│  │  │       ExampleComponent.vue
│  │  └─sass
│  │          app.scss
│  │          _variables.scss
│  └─views : app下にあったものを移動
├─setup
├─storage
├─tests
└─vendor
```

`resources/assets/`下の各種ファイルは例によってLaravelのスカフォードから拝借してきました。

## ビルド

いよいよ実際にHTMLで読み込むjsとcssを作ってみます。以下のコマンドでビルドです。

```
$ npm run dev

 ERROR  Failed to compile with 2 errors                                                                                                                                                                        22:54:13
 error  in ./resources/assets/sass/app.scss

Module build failed: Error: Node Sass does not yet support your current environment: Windows 64-bit with Unsupported runtime (59)
For more information on which environments are supported please see:
（後略）
```

……エラーになってしまいました。メッセージを見ると何となくバージョンエラーな感じがします。Nodeを最新版にしたからでしょう。Nodeのバージョンを下げるのも選択肢の一つですが、とりあえず安易にパッケージをアップデートしてみます。

```
$ npm update
$ npm run dev

 DONE  Compiled successfully in 7435ms
```

できました。

できましたが、開発中はこっちの方がいいですね。

```
$ npm run watch
```

watchコマンドを使えばソースファイルを編集したら自動でビルド実行してくれます。devやwatchコマンドの初回実行時と違って自動ビルドはすぐ終わるので、ちょこっと変更して動作を見てを繰り返したい時などに便利です。

## 動作確認

それではサーバーを起動してテストページにアクセスしてみましょう。ここでは以下のようなテストページを`public`ディレクトリに用意してみました。

```html:mixtest.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="csrf-token" content="some_token">
    <!-- Styles -->
    <link href="css/app.css" rel="stylesheet">
    <title>Document</title>
</head>
<body>
    <main id="app">
        <example-component></example-component>
    </main>

    <!-- Scripts -->
    <script src="js/manifest.js"></script>
    <script src="js/vendor.js"></script>
    <script src="js/app.js"></script>
</body>
</html>
```

`<meta name="csrf-token"～`がないとjsで警告されちゃうので追加しておきます。

問題がなければ以下のようなページが表示されるはずです。

![テストページのスクショ](https://qiita-image-store.s3.amazonaws.com/0/216864/1365bd51-fdf4-8762-64ef-cdd42fea1110.png)

これで導入できました。やったね！ 後は自分の作りたいものを実装するだけです。

# まとめ

* Laravel Mixはwebpackの使用をらくちんにしてくれる。
* けれどLaravelを使っていない場合は必須で付け加えないといけないー文があるので注意！

# 参考

* [Laravel Mixでwebpackをもっと簡単に使う方法](https://www.webprofessional.jp/use-laravel-mix-non-laravel-projects/)
* [publicPath is not set when not using laravel #807](https://github.com/JeffreyWay/laravel-mix/issues/807)
* [Windows(Win7)にNodistでNode.jsをインストール](https://qiita.com/siouxcitizen/items/63e17ccae386ffa17563)