Vue.js3でSPAの実装方法と画面の再読み込み時に今のページを表示する方法

Vue.js3でSPAの実装方法と画面の再読み込み時に今のページを表示する方法

8 回閲覧されました

みなさんこんにちは、jonioです。

現在個人開発のLaravelのプロジェクトにSPAを導入しています。

元々bladeで作っていたページをvue.js3に変えて表示した時に画面を再読み込みするとbladeを表示する現象に遭遇しました。

解消したのですが方法をすぐに忘れそうなのでそうならないようにアウトプットとしてこの記事を残します。

またSPAについて知識が定着していないので基本的な内容も説明を残します。

なおJavaScriptではなくTypeScriptを使っています。

Laravelの無料学習サイトを作りました

Laravelを勉強したい人向けの無料の学習サイトを作りました。

ここからリンクに飛べますのでぜひ利用してください。

web.php

コードの一番最後に下記の記述をします。

Route::get('/{any}', function () {
    return view('welcome');
})->where('any', '.*');

これはSPAでページ遷移をする為のスタートのページを表示する為のルート設定です。

2行目のwelcome.blade.phpに「id=”app”」の記述をしてSPAを適用するvue.jsの最初のページを表示してページ遷移ができるようにします。

1行目の「/{any}」は例えば「/example」は対応できますが「/example/demo」の「/demo」は対応できません。

それを対応できるようにする為に3行目の「->any」以降があります。

1行目と3行目の「any」は対応しています。

where句に「.*」と記述しているのは0文字以上の任意の文字に対応するという意味です。

これで「/example/demo」にも対応できるようにします。

welcome.blade.phpとApp.vue

下記の記述をします。

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>デモタイトル</title>
        @vite(['resources/css/app.css','resources/js/app.ts'])
    </head>
    <body class="antialiased">
        <div id="app"></div>
    </body>
</html>

「<div id=”app”></div>」を適用するvue.jsのページをApp.vueとします。

「Laravelのプロジェクト > resources > js」の下の階層にApp.vueを作成して下記の記述をします。

<template>
    <router-view />
</template>

「<router-view />」を記述することでこれから記述するapp.tsで設定したURLを適用できます。

app.ts

下記の記述をします。

import { createApp } from "vue"
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import App from './App.vue'
import ProjectIndex from "./pages/project/ProjectIndex.vue"; 

const routes: RouteRecordRaw[] = [
    { path: "/project/index", name: "project.index", component: ProjectIndex },
];

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

const app = createApp(App)

app.use(router).mount("#app")

10行目〜13行目でページ遷移するURLの設定をします。

11行目の第一引数の「history: createWebHistory()」の記述をするとURLを「https://example.com/project/index」みたいな見慣れた見た目で表示できます。

第二引数(routes)はページ遷移に使用するURLやコンポーネントの設定をします。

routesは6行目〜8行目に記述しています。

「path」の項目は表示するページのURLです。

「name」の項目はURLに紐づく名称みたいな感じです。

例えば下記の記述をします。

<router-link :to="{ name: 'project.index' }">一覧</router-link>

17行目の記述でApp.vueを起点としてSPAができるようになります。

「component」の項目は表示するページに使うvue.jsのコンポーネント名です。

画面を表示した時に再びそのページを表示できる仕組み

web.phpに下記の記述をしていました。

Route::get('/{any}', function () {
    return view('welcome');
})->where('any', '.*');

これは画面を読み込んだ時に必ずwelcome.blade.phpを表示するという意味です。

welcome.blade.phpは「<div id=”app”></div>」の記述があるのでApp.vueが表示されます。

App.vueには下記の記述がありました。

<router-view />

この記述があることでどのページにいてもapp.tsが下記の記述から現在のURLを調べて表示するコンポーネントを選択してそれを表示します。

const routes: RouteRecordRaw[] = [
    { path: "/project/index", name: "project.index", component: ProjectIndex },
];

この仕組みより画面を再読み込みしても同じページが表示されます。

App.vueの拡張

今の状態のApp.vueは下記です。

<template>
    <router-view />
</template>

これを使うとページ遷移をした時の挙動の細かい操作ができません。

挙動の細かい操作ができるようにするには下記の記述をします。

<template>
  <RouterView v-slot="slotProps">
    <component :is="slotProps.Component" />
    {{ slotProps.route.path }}
  </RouterView>
</template>

slopPropsは下記になります。

slotProps = {
  Component: ProjectIndex,      // 今表示すべきコンポーネント
  route: {                      // 現在のルート情報
    path: '/project/index',
    name: 'project.index'
  }
}

slotPropsの情報はapp.tsに記述されています。

const routes: RouteRecordRaw[] = [
    { path: "/project/index", name: "project.index", component: ProjectIndex },
];

「<component :is=”slotProps.Component” />」で現在のページを表示するコンポーネントを設定します。

「{{ slotProps.route.path }}」で現在のページを表示するURLの情報の設定をします。

「<component :is=”slotProps.Component” />」と「{{ slotProps.route.path }}」をひとまとめにして下記の記述をすることもできます。

<RouterView v-slot="{ Component, route }">
  <component 
    :is="Component" 
    :key="route.path" 
  />
</RouterView>

3行目でコンポーネントを表示しています。

4行目の「route.path」ですが「route.fullPath」もあり違いは下記になります。

route.path: パスの部分だけでクエリパラメーターを含まない(: /project/123/tickets/index)
route.fullPath: クエリパラメーターも含む(: /project/123/tickets/index?tab=open&page=2#top)