はじめに
Vue.jsとLaravelによるSPA実装のチュートリアル記事です。
本記事は、4本の連載記事の2本目です。
Vue.js + LaravelでシンプルなSPA構築チュートリアル:概要編
Vue.js + LaravelでシンプルなSPA構築チュートリアル:Vueフロントエンド編
↑↑今ここ↑↑
Vue.js + LaravelでシンプルなSPA構築チュートリアル:LaravelAPI編
Vue.js + LaravelでシンプルなSPA構築チュートリアル:VueとAPI結合編
前回まで
前回は、環境構築と必要なパッケージのインストールを行いました。
http://localhost:8000
でLaravelのウェルカムページが表示される状態で
次に進んでください。
コンポーネントの構成
本記事では、この全体図の青色部分、
Vue.jsによるフロントエンド実装のみを行います。
作るページ(コンポーネント)は全部で4つです。
- タスク一覧
- タスク詳細
- タスク登録
- タスク編集
最初に各ページの完成状態の画像を確認します。
前にインストールしたlaravel/ui vueに
デフォルトで組み込まれているbootstrapを使って
最低限のシンプルなUIにしています。
※今回はbootstrapの使い方には言及しません
各ページ上部にある黒い背景色の部分はヘッダーナビで、
全ページ固定で表示されるコンポーネントです。
ヘッダーナビより下の
一覧テーブルや入力フォーム部分が
URLごとに切り替わるメインのコンポーネントになります。
それでは、各ページのメインコンポーネントに加えて
ヘッダーーコンポーネントの
計5つを実装していきます。
ベースbladeとベースルーティングを追加
このアプリでは、
初回アクセス時のみLaravel側でリクエストを受けて
ページを表示し、
それ以降はフロント側のVue Routerによってルーティングが行われます。
その最初のリクエストを受け取る
Laravel側のルーティングとbladeファイルを追加します。
- Route::get('/', function () {
- return view('welcome');
- });
+ Route::get('/{any}', function() {
+ return view('app');
+ })->where('any', '.*');
+ <!doctype html>
+ <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <!-- CSRF Token -->
+ <meta name="csrf-token" content="{{ csrf_token() }}">
+
+ <title>{{ config('app.name', 'Vue Laravel SPA') }}</title>
+
+ <!-- Styles -->
+ <link href="{{ mix('/css/app.css') }}" rel="stylesheet">
+ </head>
+ <body>
+ <div id="app">
+
+ </div>
+ <!-- Scripts -->
+ <script src="{{ mix('/js/app.js') }}" defer></script>
+ </body>
+ </html>
commit:ベースのbladeとルーティング追加
これで、どのURLでアクセスしても
このapp.blade.phpが表示されるようになりました。
また、前回の記事でインストールした
Vue.jsやbootstrapも
<link href="{{ mix('/css/app.css') }}" rel="stylesheet">
<script src="{{ mix('/js/app.js') }}" defer></script>
このjs、cssファイルで読み込まれているため
利用できる状態です。
試しにデフォルトで用意されている
ExampleComponentを表示してみてください。
<div id="app">
+ <example-component></example-component>
</div>
これで
http://localhost:8000
にアクセスすると、
このようにExampleComponentが表示されると思います。
これが正しく表示されていれば、
Vue.js、bootstrapがちゃんと使えている状態です。
(このExampleComponentはbootstrapが使われています)
ヘッダーコンポーネント実装
ベースのbladeが配置できたので、
次に全ページ共通で固定表示する
ヘッダーコンポーネントを実装します。
HeaderComponentの追加
+ <template>
+ <div class="container-fluid bg-dark mb-3">
+ <div class="container">
+ <nav class="navbar navbar-dark">
+ <span class="navbar-brand mb-0 h1">Vue Laravel SPA</span>
+ <div>
+ <button class="btn btn-success">List</button>
+ <button class="btn btn-success">ADD</button>
+ </div>
+ </nav>
+ </div>
+ </div>
+ </template>
+
+ <script>
+ export default {}
+ </script>
classがいろいろとたくさん設定されていますが、
全部bootstrapのclassで見た目を整えているだけなので、
あまり気にしなくてOKです。
そのコンポーネントをVueインスタンスに登録
+ import HeaderComponent from "./components/HeaderComponent";
//↑ファイル先頭
Vue.component('example-component', require('./components/ExampleComponent.vue').default);
+ Vue.component('header-component', HeaderComponent);
登録したコンポーネントをベースbladeに追加
<div id="app">
+ <header-component></header-component>
</div>
commit:ヘッダーコンポーネント実装
この状態でページを表示してみます。
※npm run dev
または npm run watch
でソースをビルドするのを忘れないようにしましょう
ページ上部に黒いヘッダーナビが表示されていると思います。
まだボタンのリンク先は設定されていませんが、
この後ページを追加した際にこのボタンのリンクも設定します。
タスク一覧コンポーネント実装
まずタスク一覧コンポーネントを追加します。
+ <template>
+ <div class="container">
+ <table class="table table-hover">
+ <thead class="thead-light">
+ <tr>
+ <th scope="col">#</th>
+ <th scope="col">Title</th>
+ <th scope="col">Content</th>
+ <th scope="col">Person In Charge</th>
+ <th scope="col">Show</th>
+ <th scope="col">Edit</th>
+ <th scope="col">Delete</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th scope="row">1</th>
+ <td>Title1</td>
+ <td>Content1</td>
+ <td>Ichiro</td>
+ <td>
+ <button class="btn btn-primary">Show</button>
+ </td>
+ <td>
+ <button class="btn btn-success">Edit</button>
+ </td>
+ <td>
+ <button class="btn btn-danger">Delete</button>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">2</th>
+ <td>Title2</td>
+ <td>Content2</td>
+ <td>Jiro</td>
+ <td>
+ <button class="btn btn-primary">Show</button>
+ </td>
+ <td>
+ <button class="btn btn-success">Edit</button>
+ </td>
+ <td>
+ <button class="btn btn-danger">Delete</button>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">3</th>
+ <td>Title3</td>
+ <td>Content3</td>
+ <td>Saburo</td>
+ <td>
+ <button class="btn btn-primary">Show</button>
+ </td>
+ <td>
+ <button class="btn btn-success">Edit</button>
+ </td>
+ <td>
+ <button class="btn btn-danger">Delete</button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </template>
+
+ <script>
+ export default {}
+ </script>
ID、Title、Content(内容)、Person In Charge(担当者)、各種操作ボタン
をカラムに持つテーブルです。
現時点では、サンプルとして3行ほどべた書きで
タスクを表示しています。
後々の作業でここは
LaravelAPIからデータを受け取り表示するようになります。
また、
Show、Edit、Deleteのボタンを設置していますが
いまはリンク先が設定されていません。
後々各コンポーネントを実装したらリンク先を設定していきます。
追加したタスク一覧コンポーネントを
Vue Routerに登録します。
+ import VueRouter from 'vue-router';
import HeaderComponent from "./components/HeaderComponent";
+ import TaskListComponent from "./components/TaskListComponent";
window.Vue = require('vue');
+ Vue.use(VueRouter);
+
+ const router = new VueRouter({
+ mode: 'history',
+ routes: [
+ {
+ path: '/tasks',
+ name: 'task.list',
+ component: TaskListComponent
+ },
+ ]
+ });
const app = new Vue({
el: '#app',
+ router
});
VueRouter自体の詳しい解説は省略しますが、
ポイントはここです。
routes: [
{
path: '/tasks',
name: 'task.list',
component: TaskListComponent
},
]
ここで、
「/tasks」のURLでアクセスしたら
「TaskListComponent」を表示する。
このルーティングの名前は「task.list」である。
と設定しています。
別ページ(コンポーネント)を追加した際は、
同じようにこの routes
に設定を加えていくことになります。
そして、ルーティングで紐づけられたコンポーネントを表示するために、
ベースのbladeに <router-view>
を配置する必要があります。
<div id="app">
<header-component></header-component>
+ <router-view></router-view>
</div>
先ほどVue Routerで設定したとおり、
URLに紐づくコンポーネントがこの
<router-view>
の部分に表示されることになります。
この状態で
http://localhost:8000/tasks
にアクセスしてみましょう。
※ビルドを忘れずに
お手本で見た通りの
一覧テーブルが表示されていると思います。
ついでに、
ヘッダーコンポーネントにある
「List」ボタンのリンク先を設定しておきましょう。
<nav class="navbar navbar-dark">
<span class="navbar-brand mb-0 h1">Vue Laravel SPA</span>
<div>
<button class="btn btn-success">List</button>
+ <router-link v-bind:to="{name: 'task.list'}">
<button class="btn btn-success">List</button>
+ </router-link>
<button class="btn btn-success">ADD</button>
</div>
</nav>
このように <route-link>
の v-bind:to
で
リンク先のルーティング名を設定することで
SPAのリンクとして動作させることができます。
commit:タスク一覧コンポーネント実装
タスク詳細コンポーネント実装
次に、タスク詳細コンポーネントを追加します。
まずコンポーネントファイル作成。
+ <template>
+ <div class="container">
+ <div class="row justify-content-center">
+ <div class="col-sm-6">
+ <form>
+ <div class="form-group row border-bottom">
+ <label for="id" class="col-sm-3 col-form-label">ID</label>
+ <input type="text" class="col-sm-9 form-control-plaintext" readonly id="id"
+ v-bind:value="taskId">
+ </div>
+ <div class="form-group row border-bottom">
+ <label for="title" class="col-sm-3 col-form-label">Title</label>
+ <input type="text" class="col-sm-9 form-control-plaintext" readonly id="title"
+ value="title title">
+ </div>
+ <div class="form-group row border-bottom">
+ <label for="content" class="col-sm-3 col-form-label">Content</label>
+ <input type="text" class="col-sm-9 form-control-plaintext" readonly id="content"
+ value="content content">
+ </div>
+ <div class="form-group row border-bottom">
+ <label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label>
+ <input type="text" class="col-sm-9 form-control-plaintext" readonly id="person-in-charge"
+ value="Ichiro">
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+ </template>
+
+ <script>
+ export default {
+ props: {
+ taskId: String
+ }
+ }
+ </script>
taskIdをURLパラメータとして受け取って、
そのIDのみ
<input type="text" class="col-sm-9 form-control-plaintext" readonly id="id" v-bind:value="taskId">
の v-bind:value="taskId"
部分で動的に表示しています。
それ以外のcontent、person-in-chargeは
まだべた書きにしているだけです。
このコンポーネントをVue Routerに登録します。
import VueRouter from 'vue-router';
import HeaderComponent from "./components/HeaderComponent";
import TaskListComponent from "./components/TaskListComponent";
+ import TaskShowComponent from "./components/TaskShowComponent";
{
path: '/tasks',
name: 'task.list',
component: TaskListComponent
},
+ {
+ path: '/tasks/:taskId',
+ name: 'task.show',
+ component: TaskShowComponent,
+ props: true
+ },
これで、/tasks/:taskId
のURLでアクセスすると、
TaskShowComponentが表示されます。
:taskId
の部分は、任意のタスクIDが入ります。
このURLパラメータが、
先ほどのタスク詳細コンポーネントの中で使われていた
taskId
となります。
http://localhost:8000/tasks/3
のように :taskId
の部分に好きな数字を入れてアクセスすると
タスク詳細コンポーネントが表示されます。
ついでにタスク一覧コンポーネントに置いていた
「Show」ボタンのリンク先を設定しておきましょう。
+ <router-link v-bind:to="{name: 'task.show', params: {taskId: 1}}">
<button class="btn btn-primary">Show</button>
+ </router-link>
+ <router-link v-bind:to="{name: 'task.show', params: {taskId: 2}}">
<button class="btn btn-primary">Show</button>
+ </router-link>
+ <router-link v-bind:to="{name: 'task.show', params: {taskId: 3}}">
<button class="btn btn-primary">Show</button>
+ </router-link>
これで、一覧ページの「Show」ボタンをクリックすると
タスク詳細ページに遷移するようになりました。
commit:タスク詳細コンポーネント実装
タスク登録コンポーネント実装
次にタスク登録コンポーネントを実装します。
まずコンポーネントファイル作成。
+ <template>
+ <div class="container">
+ <div class="row justify-content-center">
+ <div class="col-sm-6">
+ <form>
+ <div class="form-group row">
+ <label for="title" class="col-sm-3 col-form-label">Title</label>
+ <input type="text" class="col-sm-9 form-control" id="title">
+ </div>
+ <div class="form-group row">
+ <label for="content" class="col-sm-3 col-form-label">Content</label>
+ <input type="text" class="col-sm-9 form-control" id="content">
+ </div>
+ <div class="form-group row">
+ <label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label>
+ <input type="text" class="col-sm-9 form-control" id="person-in-charge">
+ </div>
+ <button type="submit" class="btn btn-primary">Submit</button>
+ </form>
+ </div>
+ </div>
+ </div>
+ </template>
+
+ <script>
+ export default {}
+ </script>
ただ空のフォームを表示しているだけです。
現時点では送信処理は書いていません。
このコンポーネントをVue Routerに登録します。
import VueRouter from 'vue-router';
import HeaderComponent from "./components/HeaderComponent";
import TaskListComponent from "./components/TaskListComponent";
+ import TaskCreateComponent from "./components/TaskCreateComponent";
import TaskShowComponent from "./components/TaskShowComponent";
{
path: '/tasks',
name: 'task.list',
component: TaskListComponent
},
+ {
+ path: '/tasks/create',
+ name: 'task.create',
+ component: TaskCreateComponent
+ },
{
path: '/tasks/:taskId',
name: 'task.show',
component: TaskShowComponent,
props: true
},
これで、
http://localhost:8000/tasks/create
でアクセスすればタスク登録ページが表示されます。
ついでにヘッダーコンポーネントに置いていた
「Add」ボタンのリンク先を設定しておきます。
<div>
<router-link v-bind:to="{name: 'task.list'}">
<button class="btn btn-success">List</button>
</router-link>
+ <router-link v-bind:to="{name: 'task.create'}">
<button class="btn btn-success">ADD</button>
+ </router-link>
</div>
commit:タスク登録コンポーネント実装
タスク編集コンポーネント実装
次に、タスク編集コンポーネントを実装します。
まずコンポーネントファイルを作成。
+ <template>
+ <div class="container">
+ <div class="row justify-content-center">
+ <div class="col-sm-6">
+ <form>
+ <div class="form-group row">
+ <label for="id" class="col-sm-3 col-form-label">ID</label>
+ <input type="text" class="col-sm-9 form-control-plaintext" readonly id="id" v-bind:value="taskId">
+ </div>
+ <div class="form-group row">
+ <label for="title" class="col-sm-3 col-form-label">Title</label>
+ <input type="text" class="col-sm-9 form-control" id="title">
+ </div>
+ <div class="form-group row">
+ <label for="content" class="col-sm-3 col-form-label">Content</label>
+ <input type="text" class="col-sm-9 form-control" id="content">
+ </div>
+ <div class="form-group row">
+ <label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label>
+ <input type="text" class="col-sm-9 form-control" id="person-in-charge">
+ </div>
+ <button type="submit" class="btn btn-primary">Submit</button>
+ </form>
+ </div>
+ </div>
+ </div>
+ </template>
+
+ <script>
+ export default {
+ props: {
+ taskId: String
+ }
+ }
+ </script>
詳細ページと同様に、
taskId
をURLパラメータで受け取り、
IDの欄にデータを表示しています。
このコンポーネントをVue Routerに登録します。
import TaskCreateComponent from "./components/TaskCreateComponent";
import TaskShowComponent from "./components/TaskShowComponent";
+ import TaskEditComponent from "./components/TaskEditComponent";
{
path: '/tasks',
name: 'task.list',
component: TaskListComponent
},
{
path: '/tasks/create',
name: 'task.create',
component: TaskCreateComponent
},
{
path: '/tasks/:taskId',
name: 'task.show',
component: TaskShowComponent,
props: true
},
+ {
+ path: '/tasks/:taskId/edit',
+ name: 'task.edit',
+ component: TaskEditComponent,
+ props: true
+ },
これで、
http://localhost:8000/tasks/:taskId/edit
にアクセスするとタスク編集ページが表示されます。
:taskId
の部分は任意のタスクIDになります。
ついでにタスク一覧コンポーネントに置いていた
「Edit」ボタンのリンク先も設定しておきます。
+ <router-link v-bind:to="{name: 'task.edit', params: {taskId: 1}}">
<button class="btn btn-success">Edit</button>
+ </router-link>
+ <router-link v-bind:to="{name: 'task.edit', params: {taskId: 2}}">
<button class="btn btn-success">Edit</button>
+ </router-link>
+ <router-link v-bind:to="{name: 'task.edit', params: {taskId: 3}}">
<button class="btn btn-success">Edit</button>
+ </router-link>
commit:タスク編集コンポーネント実装
おわりに
これで、
・タスク一覧ページ
・タスク詳細ページ
・タスク登録ページ
・タスク編集ページ
が実装できました。
現時点ではAPIでデータを取得する処理はできていませんが、
この状態でもVue.jsによる 静的な SPAにはなっています。
もしデータベースを利用しないような
ウェブサイトなどをVue.jsでSPAとして構築する場合は
今回解説した内容を基本として
ページの追加をしていくだけです。
それでは、次にLaravelのAPI実装に進みましょう。
(次に進む前に、LGTMしてもらえるとうれしいです)
Vue.js + LaravelでシンプルなSPA構築チュートリアル:LaravelAPI編