Laravel と Vue で簡単なSPAを作ってみるメモ


vueでシングルページアプリケーション(SPA)をやってみる。

# Install

意図的にLaravelは 5.1 にしているけど、ほぼほぼ5.5の内容。5.5からlaravel-mix を持ってきて、使ってやっていく。

```shell
[yasui@Yasui-MBA2: 17-11-18 16:07]
~/Desktop/tmp/laravel-vue-test $ composer create-project --prefer-dist laravel/laravel todo "5.1.*" 
    1/10:	https://packagist.jp/p/provider-archived$c6c8e8b0a604522af7d04fbb23756ec6ce4eea5b361361c3ec65eff846cd91ba.json
...

[yasui@Yasui-MBA2: 17-11-18 16:09]
~/Desktop/tmp/laravel-vue-test $ ls -l todo
total 336
drwxr-xr-x  24 yasui  staff   768B 11 18 16:08 ./
drwxr-xr-x   3 yasui  staff    96B 11 18 16:07 ../
-rw-r--r--   1 yasui  staff   401B 11 18 16:08 .env
-rw-r--r--   1 yasui  staff   385B  4  5  2016 .env.example
-rw-r--r--   1 yasui  staff    61B  4  5  2016 .gitattributes
-rw-r--r--   1 yasui  staff    57B  4  5  2016 .gitignore
drwxr-xr-x  11 yasui  staff   352B  4  5  2016 app/
-rwxr-xr-x   1 yasui  staff   1.6K  4  5  2016 artisan*
drwxr-xr-x   5 yasui  staff   160B  4  5  2016 bootstrap/
-rw-r--r--   1 yasui  staff   1.2K  4  5  2016 composer.json
-rw-r--r--   1 yasui  staff   118K 11 18 16:08 composer.lock
drwxr-xr-x  14 yasui  staff   448B  4  5  2016 config/
drwxr-xr-x   6 yasui  staff   192B  4  5  2016 database/
-rw-r--r--   1 yasui  staff   503B  4  5  2016 gulpfile.js
-rw-r--r--   1 yasui  staff   159B  4  5  2016 package.json
-rw-r--r--   1 yasui  staff    87B  4  5  2016 phpspec.yml
-rw-r--r--   1 yasui  staff   1.0K  4  5  2016 phpunit.xml
drwxr-xr-x   7 yasui  staff   224B  4  5  2016 public/
-rw-r--r--   1 yasui  staff   1.9K  4  5  2016 readme.md
drwxr-xr-x   5 yasui  staff   160B  4  5  2016 resources/
-rw-r--r--   1 yasui  staff   567B  4  5  2016 server.php
drwxr-xr-x   5 yasui  staff   160B  4  5  2016 storage/
drwxr-xr-x   4 yasui  staff   128B  4  5  2016 tests/
drwxr-xr-x  31 yasui  staff   992B 11 18 16:08 vendor/

```

# package.json を 5.5 のにする

Laravel 5.5のコピーしてきて `package.json`にする。

```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 --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
    },
    "devDependencies": {
        "axios": "^0.16.2",
        "bootstrap-sass": "^3.3.7",
        "cross-env": "^5.0.1",
        "jquery": "^3.1.1",
        "laravel-mix": "^1.0",
        "lodash": "^4.17.4",
        "vue": "^2.1.10"
    }
}

```

## webpack.mix.js を持ってくる。

laravel-mix でコンパイルを指示する為のファイルを、package.jsonと同じ階層に設置する

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

/*
 |--------------------------------------------------------------------------
 | 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.
 |
 */

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

```


# node のアップデート

よく忘れる。

```
[yasui@Yasui-MBA2: 17-11-18 16:11]
~/Desktop/tmp/laravel-vue-test $ brew upgrade node
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 5 taps (homebrew/core, homebrew/php, homebrew/binary, homebrew/services, caskroom/cask).
...

==> Summary
🍺  /usr/local/Cellar/node/8.9.1: 5,012 files, 49.4MB
```


# vue をテストしながら開発できるようにする

`npm run test` で テストランナーが動くようしたいので、 まず`mocha`などを追加インストールする。

```shell
[yasui@Yasui-MBA2: 17-11-18 16:18]
~/Desktop/tmp/laravel-vue-test/todo $ npm install --save-dev vue-test-utils mocha mocha-webpack jsdom jsdom-global expect

> fsevents@1.1.3 install /Users/yasui/Documents/tmp/laravel-vue-test/todo/node_modules/fsevents
> node install

[fsevents] Success: "/Users/yasui/Documents/tmp/laravel-vue-test/todo/node_modules/fsevents/lib/binding/Release/node-v57-darwin-x64/fse.node" already installed
Pass --update-binary to reinstall or --build-from-source to recompile

> nodent-runtime@3.0.4 install /Users/yasui/Documents/tmp/laravel-vue-test/todo/node_modules/nodent-runtime
> node build.js

## Built /Users/yasui/Documents/tmp/laravel-vue-test/todo/node_modules/nodent-runtime/dist/index.js
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN mocha-webpack@1.0.1 requires a peer of webpack@^2.0.0 || ^3.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN vue-test-utils@1.0.0-beta.5 requires a peer of vue@2.x but none is installed. You must install peer dependencies yourself.
npm WARN vue-test-utils@1.0.0-beta.5 requires a peer of vue-template-compiler@^2.x but none is installed. You must install peer dependencies yourself.

+ jsdom@11.3.0
+ mocha@4.0.1
+ mocha-webpack@1.0.1
+ vue-test-utils@1.0.0-beta.5
+ expect@21.2.1
+ jsdom-global@3.0.2
added 370 packages in 20.293s
```


上記のコマンドを実行したら`package.json`は、下記のように`mocha`や`expect`が追加されています。

```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 --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
    },
    "devDependencies": {
        "axios": "^0.16.2",
        "bootstrap-sass": "^3.3.7",
        "cross-env": "^5.0.1",
        "expect": "^21.2.1",
        "jquery": "^3.1.1",
        "jsdom": "^11.3.0",
        "jsdom-global": "^3.0.2",
        "laravel-mix": "^1.0",
        "lodash": "^4.17.4",
        "mocha": "^4.0.1",
        "mocha-webpack": "^1.0.1",
        "vue": "^2.1.10",
        "vue-test-utils": "^1.0.0-beta.5"
    }
}

```

## テストに必要になるJSを追加する

こんな感じ

```shell
~/Desktop/tmp/laravel-vue-test/todo $ mkdir tests/JavaScript
[yasui@Yasui-MBA2: 17-11-18 16:27]
~/Desktop/tmp/laravel-vue-test/todo $ touch tests/JavaScript/setup.js
```

そして`setup.js`を下記のようにしてやります。おまじないみたいな物だと認識。

```setup.js
require('jsdom-global')();
```


## `package.json` の `scripts`に`test`を追加する。

気合の長文コマンドを叩けばテストは動きますが、そんなことしたくありませんので、せめて`npm run test`でテストが動くようになれば嬉しいです。ですので、そのような設定をします。`scripts.test`を追加。

```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 --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "test": "mocha-webpack --webpack-config=node_modules/laravel-mix/setup/webpack.config.js --require tests/JavaScript/setup.js tests/JavaScript/**/*.spec.js"
    },
    "devDependencies": {
        "axios": "^0.16.2",
        "bootstrap-sass": "^3.3.7",
        "cross-env": "^5.0.1",
        "expect": "^21.2.1",
        "jquery": "^3.1.1",
        "jsdom": "^11.3.0",
        "jsdom-global": "^3.0.2",
        "laravel-mix": "^1.0",
        "lodash": "^4.17.4",
        "mocha": "^4.0.1",
        "mocha-webpack": "^1.0.1",
        "vue": "^2.1.10",
        "vue-test-utils": "^1.0.0-beta.5"
    }
}


```

上記の設定をしたら`npm run test`を実行して、文法ミスがないか、テストを読み込もうとしたか確認をする。もちろん、まだテストは作っていないので、`0 passing (1ms) `を返す。

```shell
 WEBPACK  Compiled successfully in 109ms

 MOCHA  Testing...



  0 passing (1ms)

 MOCHA  Tests completed successfully


```

# Vue の開発を始める

ここまでがテストをする準備。5.1ではVueのテンプレファイルなどが全く無いので、5.5から持ってくる。

## 開発用ディレクトリの作製

```shell
[yasui@Yasui-MBA2: 17-11-18 16:33]
~/Desktop/tmp/laravel-vue-test/todo $ mkdir -p resources/assets/js/components
```

## app.js などの移植

```resources/assets/js/bootstrap.js
window._ = require('lodash');

try {
    window.$ = window.jQuery = require('jquery');

    require('bootstrap-sass');
} catch (e) {}

window.axios = require('axios');

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
```

```resources/assets/js/app.js
require('./bootstrap');

window.Vue = require('vue');
Vue.component('example', require('./components/Example.vue'));

const app = new Vue({
    el: '#app'
});

```

```resources/assets/js/components/Example.vue
<template>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel panel-default">
                    <div class="panel-heading">Example Component</div>

                    <div class="panel-body">
                        I'm an example component!
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

```

## テストを書く

こんな感じ

```tests/JavaScript/example.spec.js
import { mount } from 'vue-test-utils';
import expect from 'expect';
import Example from '../../resources/assets/js/components/Example.vue';

describe ('Example', () => {
	it ('say that it is an example components', () => {
		let wrapper = mount(Example);

		expect(wrapper.html()).toContain('Example Component');
	});
});
```

そしてテストを実行すると、こんな感じになる。

```shell
 WEBPACK  Compiled successfully in 1807ms

 MOCHA  Testing...



  Example
Component mounted.
    ✓ say that it is an example components


  1 passing (139ms)

 MOCHA  Tests completed successfully

```




# 参考

1. (LaraCast )[https://laracasts.com/series/testing-vue/episodes/6]
2. [Mocha + webpack による単一ファイルコンポーネントのテスト](https://vue-test-utils.vuejs.org/ja/guides/testing-SFCs-with-mocha-webpack.html)