Larevel + Vue3 + Typescriptでvue-routerを使いたい

## 【きっかけ】
* Laravel + Vue3 + Typescriptのセット、またVue3 + Typescript + vue-routerのセットの記事は探せば出てくるものの、4つ合わさった記事が見つからなかったので作成
## 【実行環境】
この記事で作成するプロジェクトの環境をDocker上で実現する記事を別途アップしています
「[Docker上でLarevel + Vue3 + Typescriptを使いたかったから環境を作った](https://qiita.com/Hoi-hoi/items/dd1be921610835a793ab)」
(2022/12/08 追記)
## 【構築】
### Laravelプロジェクト作成
* 今回はcomposerコマンドでプロジェクト「sample-project」を作成
```
composer create-project laravel/laravel sample-project
```

### プロジェクト直下に移動
```
cd sample-project
```

### パッケージインストール
```
npm install
```
### Vue3インストール
```
npm install vue@next --save-dev
```

### Typescriptインストール
```
npm install -D typescript ts-loader
```

### vue-routerインストール
```
npm install vue-router@4
```

### tsconfig.json作成
* プロジェクト直下にtsconfig.jsonを作成
* 内容は[公式リファレンス](https://v3.ja.vuejs.org/guide/typescript-support.html)を参照
```json:tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "moduleResolution": "node"
  }
}
```

### コンポーネントファイル作成
* resources/js/に\<router-view>を配置するコンポーネントファイルを作成
    * 今回はファイル名をApp.vueで作成
``` vue:App.vue
<template>
    <router-view></router-view>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
    setup() {   
    }
})
</script>
```

* \<router-view>にルーティングさせるコンポーネントファイルを作成
    * 今回はresources/js/にディレクトリ「components」を作成し、その直下に
      2つのコンポーネントファイル「Sano.vue」「Sena.vue」を作成
``` vue:Sano.vue
<template>
  <router-link to="/color/sena">遷移</router-link>
  {{message}}
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup() {
    const message = ref<string>("Yellow");
    return {
      message
    };
  }
});
</script>
```
``` vue:Sena.vue
<template>
    {{message}}
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup() {
    const message = ref<string>("Purple");
    return {
      message
    };
  }
});
</script>
```

### router.tsを作成
* resources/js/にrouter.tsを作成
* importのfromには該当コンポーネントファイルをresources/js/から見た相対パスで記載
* pathの階層は2階層以上が望ましい
    * 最後に編集するweb.phpの設定のため
``` js:router.ts
import { createRouter,createWebHistory } from 'vue-router';
import sano from "./components/Sano.vue";
import sena from "./components/Sena.vue";

const routes = [
    { path: '/color/sano', name: 'sano', component: sano },
    { path: '/color/sena', name: 'sena', component: sena },
]

const router = createRouter({
    history: createWebHistory(),
    routes,
})
 
export default router;
```


### app.ts作成
* resources/js/に存在するapp.jsの名称をapp.tsに変更
* app.tsの中身を変更
    * 「App.vue」はさっき作成した\<router-view>を配置するコンポーネントファイルのファイル名
``` js:app.ts
import { createApp } from "vue"
import router from './router';
import App from "./App.vue"

const app = createApp(App);
app.use(router);
app.mount("#app")
```

### shims-vue.d.ts作成
* resources/js/にshims-vue.d.tsを作成
``` js:shims-vue.d.ts
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}
```

### webpack.mix.js編集
* プロジェクト直下に存在するwebpack.mix.jsの中身を編集
    * app.jsと違いファイルの拡張子は変更しない
``` js:webpack.mix.js
// この部分を
mix.js('resources/js/app.js', 'public/js')
// 以下に差し替える
mix.ts('resources/js/app.ts', 'public/js').vue()
```

### bladeファイル作成
* resources/views/にapp.blade.phpを作成
    * ファイル名は任意
* headの要素にCSRF Tokenなど使いたいものは任意で追加
``` php:app.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <!-- 以下の1行がないとコンポーネントファイルの内容が反映されない -->
    <script src="{{ asset('js/app.js') }}" defer></script>
</head>
<body>
    <div id="app"></div>
</body>
</html>
```

### ルーティング編集
* routes/に存在するweb.phpを編集する
``` php:web.php
Route::get('/color/{any}', function () {
    // さっき作成した「app.blade.php」から拡張子を除いた「app」をviewに記載
    return view('app');
})->where('any', '.*');
```
* 以上の設定によって、
    1. localhost:8000/color/以下のURLにアクセスした際、必ずapp.blade.phpが開く
    1. app.tsに記載した```app.mount("#app")```によって、app.blade.phpの```<div id="app"></div>```にApp.vueがマウントされているため、必ずApp.vueの内容が反映される
    1. App.vueには```<view-router></view-router>```が存在するため、router.tsによって```<view-router></view-router>```にはアクセスしたURLに対応するコンポーネントファイルの内容が反映される

   となり、localhost:8000/color/以下のURLにアクセスした際はvue-routerの処理が優先される
    *  簡潔に書くと、localhost:8000/color/までがLaravel側の処理、それ以降はvue-routerの処理となる

## 【動作確認】
### Typescriptのコンパイル
```
npm run dev
```

### Laravelプロジェクトを実行
```
php artisan serve
```
### 画面表示確認
* [http://localhost:8000/color/sano](http://localhost:8000/color/sano)にアクセスして以下の画面が表示されるか確認
![スクリーンショット 2022-04-10 12.53.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294010/0816bc83-31d0-a6fa-58c2-ee0cf0836561.png)

### 遷移確認
* 画面に表示されている[遷移]リンクをクリックして以下の表示に変わったか確認
![スクリーンショット 2022-04-10 12.56.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294010/b449362d-3e08-34b3-980f-78d072e82389.png)