Laravel + Vue.js のキホン

2021年09月30日
1,894 view

LaravelではフロントエンドのJavaScriptフレームワークとしてVue.jsをサポートしており、 これを利用することにより動的な画面制御が非常に楽になります。
今回はこのブログの実際のソースコードを例にして、LaravelにおけるVue.jsの基本的な使用方を解説していきます。

1. サンプルコード

  • 記事ページのblade
    この記事ページのコードをそのまま貼りましたが、38〜41行目に「記事本文」と「コメント」という記載があるように、 それぞれ別ファイルにわけており、記事IDによって対象ファイルを開くようにしています。
    (例えば記事ID=9であれば「blog/articles/9.blade.php」というファイルを読み込む)
    そして、対象記事に対するコメントのリストを「partials/comments.blade.php」へ引数として渡しています。
@extends('layouts.app')
@section('head')
<link href="https://spacethings.jp/css/_article.css?20200625" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<title>{​{ $article->title }​}</title>
<meta property="og:title" content="{​{ $article->title }​}">
<meta name="description" content="{​{ $article->description }​}">
<meta property="og:description" content="{​{ $article->description }​}">
<meta property="og:image" content="{​{ asset('/images/blog/' . $article->image_file) }​}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="{​{ asset('/images/blog/' . $article->image_file) }​}">
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?skin=sunburst"></script>
@endsection

@section('content')
<div class="container article">
    <div class="main">
        @include(
            'partials.breadcrumb',
            [
                'items' => [
                    ['path' => '/', 'label' => 'Top'],
                    ['path' => '/category/'.$article->category, 'label' => $article->category()],
                ]
            ]
        )

        <div class="heading">
            <div class="title"><h1>{​{ $article->title }​}</h1></div>
            <div class="thumbnail text-center w-100">
                <div class="front"><img src="{​{ asset('/images/blog/' . $article->image_file) }​}"/></div>
                <div class="thumbnail-back" style="background-image: url({​{ asset('/images/blog/' . $article->image_file) }​})"/></div>
            </div>
            <div class="provider">{!! $article->image_copyright !!}</div>
            <div class="date"><i class="material-icons"></i>{​{ $article->created_at->format('Y年m月d日') }​}</div>
        </div>

        {​{-- 記事本文 --}​}
        @include('blog.articles.' . $article->id)
        {​{-- コメント --}​}
        @include('partials.comments', ['comments' => $comments])

    </div>

    <div class="page-bottom">
        @include('partials.profile')
        @include('partials.categories')
    </div>
</div>
@endsection

  • comments.blade.php
    こちらのbladeではCommentComponent.vueファイルを読み込んでいます。 (app.jsにてcomment-componentと紐付けています。)
    引数として、記事IDとコメントリストを渡しています。
<div>
    <div class="content comment-area">
        <div class="title"><i class="material-icons">comment</i>コメント</div>

        {​{-- new comment --}​}
        <comment-component
            :id="{​{ $article->id }​}"
            :comments="{​{ $comments }​}"
        ></comment-component>

    </div>
</div>

  • CommentComponent.vue
    Componentの中身です。
    bladeから受け取った引数を props: 内で定義し、 data: でこのcomponent内で使用する変数を定義しています。
    computed: では数式を定義しておくことができます。 例えば、「enableBtn」という数式ではコメントエリアのNameとCommentの両方が埋まればTRUEを返すように定義されています。
    これにより10〜12行目のように、NameとCommentが埋まった時、送信中、それ以外の場合でボタンの表示を切り替えるというようなことが可能になります。
<template>
    <div class="post-area">
        <div class="name">
            <input type="text" placeholder="Name" v-model="data_name" />
        </div>
        <div class="comment">
            <textarea placeholder="Comment" v-model="data_comment"></textarea>
        </div>
        <div class="btn-post">
            <button v-if="enableBtn" @click="comment">Comment</button>
            <button v-else-if="data_sending" class="btn-disabled">Sending</button>
            <button v-else class="btn-disabled">Comment</button>
        </div>

        <div v-if="isEmpty" class="empty">コメントはまだありません</div>
        <ul v-else class="list-unstyled">
            <li v-for="item in data_comments">
                <div class="comment-header">
                    <div>
                        <span class="name">{​{ item.name }​}</span>
                        <span class="date">{​{ item.created_at }​}</span>
                    </div>
                </div>
                <div class="comment">
                    <span>{​{ item.comment }}</span>
                </div>
            </li>
        </ul>
    </div>
</template>

<script>
    export default {
        props: {
            id: {
                type: Number
            },
            comments: {
                type: Array
            }
        },
        data: function() {
            return {
                data_id: this.id,
                data_name: '',
                data_comment: '',
                data_comments: this.comments,
                data_sending: false,
            }
        },
        computed: {
            enableBtn: function() {
                return this.data_name !== '' && this.data_comment !== '' && !this.data_sending;
            },
            isEmpty: function() {
                return this.data_comments.length === 0;
            }
        },
        methods: {
            comment: function() {
                this.data_sending = true;
                const endpoint = '/api/comment';

                axios.post(endpoint, {
                    article_id: this.data_id,
                    name: this.data_name,
                    comment: this.data_comment,
                }).then((res) => {
                    this.data_comments.unshift(res.data);
                    this.data_name = '';
                    this.data_comment = '';
                    this.data_sending = false;
                }).catch(error => {
                    console.log(error);
                    this.data_comments.unshift(res.data);
                    this.data_name = '';
                    this.data_comment = '';
                    this.data_sending = false;
                });
            }
        }
    }
</script>


2. Ajax

さて、先ほどのCommentComponent.vueではコメントの投稿をAjax処理にて行っています。
NameとCommentが埋まり、コメントボタンの押下が可能になった時、ボタンを押下すると「comment」メソッドを呼び出しています。
メソッドは method: にて定義しています。中身の処理は単純に axios によってpostリクエストを投げ、 サーバーサイドで処理した結果を受け取り、変数を更新しているだけのシンプルな構成です。
だいたいVueの基本的な用途はこれぐらいだと思いますし、なるべく基本的な処理だけで完結するように工夫することでソースコードがシンプルになり、 可読性も上がります。

さいごに

フロントエンド開発はだいたいこんな風に実装していて、これ以外の特別な技術なんかもあまり使う機会は無いんじゃないかなと思います。
デザインとかcssの方が個人的には面倒な気がしますね。

thumb_upオススメ記事
commentコメント

Yoshiyuki Uemoto

Twitter :@yu___space
1991年生まれ / フリーランス webエンジニア
主にSalesforce、PHP(Laravel)等が専門です。物理学科出身で特に宇宙分野(素粒子など)が好きです。
プログラミング / 物理学 / 宇宙 など、その時々に思ったことを書いていきたいと思います。