Laravel＆Vue.jsでSPAの下準備

#やること

- Laravelの上にVue.jsを乗っけた（viewをVue.jsにお任せした）形でSPAを作成
 →要ルーティングを設定
- js、cssプリプロセッサ（scssを利用）のコンパイラとして　Laravel Mixを利用
- Vue.jsでコンポーネントを作成、リンクの設定


#環境

・maxOS High Sierra

・php : v7.1
・Laravel : v5.6
・Laravel Mix : v2.0
・Node : v8.9
・npm : v5.7
・Vue : v2.5
・vue-router : v3.0



#下準備

```termcap
cd プロジェクトフォルダ作るところ
composer create-project --prefer-dist laravel/laravel プロフジェクト名
```
→プロフェクト名のフォルダができて、installされる

```termcap
cd プロフジェクトのフォルダ
```


→`php artian serve` http://localhost:8000 デフォルトページ表示される

特に急がないけど、とりあえず↓。

```php:config/app.php　書きかえ後
'timezone' => 'UTC+09:00',
'locale' => 'ja',
'fallback_locale' => 'ja',
```

##nodeの準備

```termcap
npm install
```
→`npm run dev`　/ `npm run watch` できるようになる



#ファイルの確認

##Laravelのルーティング

```php:routes/web.php
Route::get('/', function () {
    return view('welcome');
});
```
→HomeURLアクセスで`resouces/view/welcom.blade.php`を見に行っているようです。
welcomもなんなので、お好みで変更。

```txt:resources/views/
welcom.blade.php →ファイル名変更→ app.blade.php
```

```php:routes/web.php
 return view('welcom'); →書き換え→ return view('app');
```

#Vue.jsの利用

（参考：https://jp.vuejs.org/index.html）

LaravelはデフォルトでVue.jsが入っており(参照：package.json)、↓な感じでvueなファイル発見。

```txt:resouces/assets/js
 ├ components
 │　　└ ExampleComponent.vue
 ├ app.js
 └ bootstrap.js
```
##お試しexampleコンポーネントを表示してみる

```js:app.js
Vue.component('example-component', require('./components/ExampleComponent.vue'));
```
とあるので、、、

```php:resources/views/app.blade.php
<body>
  <div id="app">
     <example-component></example-component>
  </div>
  <script src="js/app.js"></script>
</body>
```

```npm run dev (もしくはwatch)``` → http://localhost:8000　 をアクセスして確認



#vue-routerでSPA化

（参考：https://router.vuejs.org/ja/）
Vue.js側にルーターを設定し、SPA化。

##Laravel側設定
どのURLでアクセスしても、同一のLaravelのviewファイルを返す。

```php:routes/web.php
Route::get('/{any}', function () {
    return view('app');
})->where('any', '.*');
```

##Vue.js側のviewのベース設定

とりあえず、exampleなコンポーネント表示と、vue-routerで表示されるとこ設定。

```html:resources/assets/js/components/App.vue　ファイルを作成
<template>
　　<div id="app">
　　　　<example-component></example-component>
　　　　<main><router-view></router-view></main>
　　</div>
</template>

<script>
　　export default {
　　　　name: 'app',
　　}
</script>
```

↑　```<router-view></router-view>``` のところに、vue-routerの設定に応じた中身が表示される。

LaravelのビューファイルにもVue.jsを表示させる部分を記載↓。

```php:resources/views/app.blade.php
<body>
	<div id="app"></div>
	<script src="js/app.js"></script>
</body>
```

##vue-router を設定

デフォルトでは入ってないのでインストール

```termcap:インストール
npm install vue-router
```

appの設定とルーティング設定のファイルは別々にしとこうと思います。

```javascript:resources/assets/js/app.js　←app.blade.phpで読み込まれているapp.js
require('./bootstrap');

import Vue from 'vue'
import VueRouter from 'vue-router'

import router from './router'
import app from './components/App.vue'

Vue.use(VueRouter)

new Vue({
  el: '#app',
  router: router,
  template: '<app/>',
  components: { app }
})

```

↑ app.blade.phpの```<div id="app"></div>``` に ```App.vue```が挿入される。
ルーティング設定ファイルも読み込んでおく。

↓ルーティング設定。とりあえず、ルーティング内でもexampleなコンポーネントを表示。

```javascript:resources/assets/js/router.js　←app.jsでimportしている./router
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

Vue.component('example-component', require('./components/ExampleComponent.vue'));

export default new VueRouter({
    mode: 'history',
    routes: [
        { path: '/', 
            component: require('./components/ExampleComponent.vue') 
        }
    ]
});

```

```npm run dev (もしくはwatch)``` → http://localhost:8000　 をアクセスして確認


#sassのコンパイル設定(Laravel Mix)

（参考：https://readouble.com/laravel/5.6/ja/mix.html
https://github.com/JeffreyWay/laravel-mix/issues/879）

Laravel Mixなるものが、アセットのコンパイルをやってくれるらしい。
↓を書くと、
```resources/assets/js/app.js```が```public/js/app.js```、
```resources/assets/sass/style.scss```が```public/css/style.css（オプション:compressed）```として書き出される。

```javascrip:webpack.mix.jp
mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/style.scss', 'css', {outputStyle: 'compressed', outFile: 'public/css'})
   .options({
      processCssUrls: false,
   });

if (!mix.inProduction()) {
    mix.webpackConfig({
        devtool: 'source-map'
    })
    .sourceMaps()
}
```

↑下部のif文を付け加えないと、sourcemapが作れなかった。どうして。。？

```javascrip:webpack.mix.jp　これではsourcemapできんかった。
mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/style.scss', 'css', {outputStyle: 'compressed', outFile: 'public/css'})
   .sourceMaps()
   .options({
      processCssUrls: false,
   });
```

Laravel Mixを使って、jsやcssのファイルパスを書けるようになる。

```php:resources/views/app.blade.php
<link rel="stylesheet" href="{{ mix('css/style.css') }}">
<script src="{{ mix('js/app.js') }}"></script>　/*←すでに書いてあるものの書き換え*/
```

#もうちょっとアプリなコンポーネント設定（ビューを増やしてリンク設定）

（参考：https://router.vuejs.org/ja/）

せっかくなので、ルーティングをもうちょっと試すべく、ヘッダーとビュー（ページ）を５つほど設定してみる。

##テンプレートヘッダーの追加
exampleなコンポーネントを表示するのやめて、headerを追加。

```html:resources/assets/js/components/App.vue
<template>
  <div id="app">
    <app-header></app-header>　/*<app-header>を表示*/
    <main><router-view></router-view></main>
  </div>
</template>

<script>
  import appHeader from './AppHeader'

  export default {
    name: 'app',
    components: {
      'app-header': appHeader,　/*「<app-header>はimportしたappHeaderだよ」と設定*/
    }
  }
</script>
```

```<app-header>``` の中身を作る。↓

```html:resources/assets/js/components/AppHeader.vue
<template>
  <header>
    <h1>logo</h1>
    <nav>
      <ul>
        <li>menu</li>
      </ul>
    </nav>
  </header>
</template>

<script>
  export default {
    name: 'appHeader',
  }
</script>
```

```npm run dev (もしくはwatch)``` → http://localhost:8000　 をアクセスして確認


##ビュー（ページ）を作成

###ルーティングの設定

```javascript:resources/assets/js/router.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

/*下部記載のルーティングに必要なコンポーネントのimport*/
import pageTop from './components/PageTop'
import pageAbout from './components/pageAbout'
import pageUser from './components/PageUser'
import UserProfile from './components/user/Profile'
import UserPosts from './components/user/Posts'

/*URLと↑でimportしたコンポーネントをマッピングする（ルーティング設定）*/
export default new VueRouter({
  mode: 'history',
  routes: [
    {
      path: '/',
      component: pageTop
    },
    {
      path: '/about',
      component: pageAbout
    },
    {
      path: '/user/:userName',　/* 動的ルートのお試しとしてユーザー名を入れてみる*/
      component: pageUser,
      children: [　/* ネストのお試しとしてchildren設定してみる　*/
      {
          path: 'profile',　　/* URL: /user/ユーザーネーム/profile */
          component: UserProfile
        },
        {
          path: 'posts',　　/* URL: /user/ユーザーネーム/posts */
          component: UserPosts
        }
      ]
    },
  ]
});
```

###各コンポーネントを作成

```html:resources/assets/js/components/PageTop.vue
<template>
  <p>PageTop</p>
</template>

<script>
  export default {
    name: 'pageTop',
  }
</script>
```

ひとまず、↑なノリでコンポーネント名を```<p>```に入れて、前述でimportしたコンポーネントを全部作る。

###ネストのルーティング用のビュー

（参考：https://router.vuejs.org/ja/essentials/nested-routes.html　）

PageUser.vueについては、ネストがあるので、```<router-view>```を追加

```html:resources/assets/js/components/PageUser.vue
<template>
　　<div>
　　　　<p>PageUser</p>
　　　　<section><router-view></router-view></section>
　　</div>
</template>

<script>
  export default {
    name: 'pageUser',
  }
</script>
```

###リンクの作成

ヘッダーにナビゲーションリンクを作成↓

```html:resources/assets/js/components/AppHeader.vue
/*前後略*/
　　<nav>
　　　　<ul>
　　　　　　<li><a href="/">Top</a></li>
　　　　　　<li><a href="/about">about</a></li>
　　　　　　<li><a href="/user/abc">user</a></li> /*動的URLに値を渡す設定してないので、取り急ぎユーザー名適当に固定指定*/
　　　　</ul>
　　</nav>
```

ユーザーページにページリンクを作成↓

```html:resources/assets/js/components/PageUser.vue
/*前後略*/
　　<div>
　　　　<p>PageUser</p>
　　　　<nav>
   /*これはうまく行かない*/
　　　　　　<a href="./profile">profile</a>
　　　　　　<a href="./posts">posts</a>

   /*これだとOK*/
　　　　　　<a href="/user/abc/profile">profile</a>
　　　　　　<a href="/user/abc/posts">posts</a>
　　　　</nav>
　　　　<section><router-view></router-view></section>
　　</div>
```

ってやってもいいけど、

>下記の理由により ```<router-link>``` はハードコードする ```<a href="...">``` よりも好ましいです。

>- HTML5 history モードでも hash モードでも同じ方法で動作します。もしあなたがモードを切り替えたりする場合や、IE9 で hash モードにフォールバックする場合に、何も変更する必要はありません。
>- HTML5 history モードにおいて、ブラウザがページのリロードをしないように router-link はクリックイベントに割り込みます。
>- HTML5 history モードで base オプションを使っている時に、 to プロパティの URL にそれを含める必要がありません。

>(引用：https://router.vuejs.org/ja/api/router-link.html)


ということなで、```<router-link>```に書きかえ。

####方法１：URL指定

```html:resources/assets/js/components/AppHeader.vue
/*前後略*/
　　<nav>
　　　　<ul>
　　　　　　　　<li><router-link to="top">Top</router-link></li>
　　　　　　　　<li><router-link to="about">about</router-link></li>
　　　　　　　　<li><router-link to="user/abc">user</router-link></li>
　　　　</ul>
　　</nav>
```

```html:resources/assets/js/components/PageUser.vue
/*前後略*/
　　<nav>
　　　　<router-link to="abc/profile">profile</router-link>
　　　　<router-link to="abc/posts">posts</router-link>
　　</nav>
```

####方法2：名前付きルート
（参考：https://router.vuejs.org/ja/essentials/named-routes.html）

ルートに名前をつけておくことで、その名前を指定することでリンクが生成される

```javascript:resources/assets/js/router.js
/*前後略*/
routes: [
  {
    path: '/',
    name: 'top',　/*←各ルートにnameを追記*/
    component: pageTop
  },
  {
    path: '/about',
    name: 'about',
    component: pageAbout
  },
  {
    path: '/user/:userName',
    name: 'user',
    component: pageUser,
    children: [
    {
        path: 'profile',
        name: 'profile',
        component: UserProfile
      },
      {
        path: 'posts',
        name: 'posts',
        component: UserPosts
      }
    ]
  },
]
```


```html:resources/assets/js/components/AppHeader.vue
/*前後略*/
　　<nav>
　　　　<ul>
　　　　　　<li><router-link :to="{ name: 'top'}">Top</router-link></li>
　　　　　　<li><router-link :to="{ name: 'about'}">about</router-link></li>
　　　　　　<li><router-link :to="{ name: 'user', params: { userName: 'abc' }}">user</router-link></li>
　　　　</ul>
　　</nav>
```

```html:resources/assets/js/components/PageUser.vue
/*前後略*/
　　<nav>
　　　　<router-link :to="{ name: 'profile', params: { userName: 'abc' }}">profile</router-link>
　　　　<router-link :to="{ name: 'posts', params: { userName: 'abc' }}">posts</router-link>
　　</nav>
```

##動的ルートにプロパティを渡す

（参考：https://jp.vuejs.org/v2/guide/components.html#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E6%A7%8B%E6%88%90
https://jp.vuejs.org/v2/guide/components.html#%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E4%BC%9D%E9%81%94
）


ユーザー名が動的なので、その仮設定を行う。

###親コンポーネント

```html:resources/assets/js/components/App.vue
<template>
 /*前後略*/
    <main><router-view :user-info="user"></router-view></main>
</template>

<script>
 /*前後略*/
  export default {
    name: 'app',
    components: {
      'app-header': appHeader,
    },
    data: function (){
      return {
        user: {　/* 取り急ぎユーザー情報固定で設定 */
          id: 123,
          name: 'abc'
        }
      }
    }
  }
</script>
```

解説抜粋。

```html:resources/assets/js/components/App.vue
親コンポーネントで、データを取得（取り急ぎ仮に作成。）
<script>
 /*前後略*/
  export default {
    data: function (){ /*dataは関数じゃなきゃいけない。とのこと。*/
      return {　/*後々、ajaxとかで受け取れるとして、とりあえず固定でオブジェクト作成。*/
        user: {　
          id: 123,
          name: 'abc'
        }
      }
    }
  }
</script>
```


```html:resources/assets/js/components/App.vue
親コンポーネントから子コンポーネントにデータを渡す。
<template>
  <router-view :user-info="user"></router-view>
  /*データの[user]を[userInfo]というプロパティで子コンポーネントに渡す。*/
</template>
```

###子コンポーネント


```html:resources/assets/js/components/PageUser.vue
<template>
/*前後略*/
    <div>PageUser:{{userInfo.name}}</div>
    <nav>
      <router-link :to="{ name: 'profile', params: { userName: userInfo.name }}">profile</router-link>
      <router-link :to="{ name: 'posts', params: { userName: userInfo.name }}">posts</router-link>
    </nav>
</template>

<script>
  export default {
    name: 'pageUser',
    props: ['userInfo']
  }
</script>
```
解説抜粋。


```html:resources/assets/js/components/PageUser.vue
/*親コンポーネントから渡ってきた[userInfo]をプロパティとして受け取る*/
<script>
  export default {
    props: ['userInfo']
  }
</script>
```

```html:resources/assets/js/components/PageUser.vue
/*[userInfo]プロパティのnameを表示する*/
<template>
  <div>PageUser:{{userInfo.name}}</div>
  <nav>
    <router-link :to="{ name: 'profile', params: { userName: userInfo.name }}">profile</router-link>
    <router-link :to="{ name: 'posts', params: { userName: userInfo.name }}">posts</router-link>
　　　　　　/*ルーティングで設定したパス、'/user/:userName'に合わせて、リンクのパラメータuserNameに[userInfo]のユーザー名を設定する*/
  </nav>
</template>
```


ちなみに、こちらではうまくリンク作成されず↓。

```html:
　　<router-link to={userInfo.name}"/profile">profile</router-link>
　　<router-link to="{userInfo.name}/profile">profile</router-link>
　　<router-link to={userInfo.name　+"/profile"}>profile</router-link>


　　<a :href="/{userInfo.name}/profile">profile</a>
　　<a :href="/"+{userInfo.name}+"/profile">profile</a>
```


#ここまで！

Vue.jsのあれこれ深いところに入って行きそうなので、ここまで！
乱文失礼いたしました。