当ブログ人気記事!
転職体験談
Laravelで学ぶWebアプリ開発
誰でも作れるチャットアプリ
未経験への勧め

【Laravel初心者向け講座】いいね機能を実装してみる(フロント編:lodashでリクエスト制御)

プログラミング

いいね機能って実際どうやって実装するんだろう
Lodashの具体的な使い方を知りたい!

こういった方に対して書きました!


この記事を書いている私(@Shoot58153748)は、
2020年2月現在メガベンチャーの社内スタートアップの部署でエンジニア(1年目)をしており、

プログラミング未経験からメガベンチャーへの転職を成功させた経験・ノウハウ
Webエンジニアになってから学んだこと

をブログにまとめています。


前回は、Vueでの無限スクロールを実装しました。

更に実践的なアプリに踏み込んでいきましょう。

今回から2記事(フロント側実装、API実装)にわたって、いいね機能の実装をしていきます!
ぜひAPI編も一緒に参考にしてください。


今回のポイント

  • font awesomeの使用
  • vueでハートボタン制御
  • lodashでリクエスト制御



今までのLaravelでのWebアプリ開発記事もまとめていますので、
こちらも参考にしてください!

また、ブログでは最低限必要な(更新した)部分のみ掲載するので、
全体のソースコードをみたい場合は以下にあげてあります。
Github: https://github.com/Shuto-san/laravel-vue-docker

スポンサーリンク

lodashとは?

値の操作など、開発に役立つ便利な関数を提供してくれるJavaScriptのライブラリです。

便利な関数の1つにdebounceがあります。
今回はこのdebounceというリクエスト制御できる関数を使用することで、
いいね機能のリクエストを送るタイミングをコントロール
します。


さて、リクエスト制御とはどういうことか。

例えば、jsのイベントをシンプルに使うと(click)
サーバー側にリクエストを送るタイミングはハートボタンのクリック時ですよね?

相手が普通の優良ユーザーだったら何も問題ありません。
常識の範囲内でいいねボタンをクリックし、
「やっぱ違う。間違えた。」
と思ったらいいねボタンを再度クリックし取り消します。


しかし、ひたすら高速にボタン連打を繰り返す変なユーザーがいたとしたらどうでしょうか?

実践的なアプリを開発する場合、常に悪意のあるユーザーを想定しなければなりません。

そのため
リクエスト内容をまとめて送る、
一定時間対象の更新が止まったらリクエストを送る
などの工夫が必要になります。

debounceを使用して、いいねボタンのリクエスト送信を
クリック時ではなくいいねボタンの操作が止まった1秒後にリクエストを送るようにします
つまり、いいねボタンを連打しても一生送信されません。


さて、さっそく開発していきましょう!

画面イメージ

いいね機能実装例(フロント側)

今回の制作物(実装内容)は以下になります。

  • ライブラリインストール
  • 画面
  • SCSS
  • Js


前準備としてライブラリのインストールから実行していきます。

ライブラリインストール

インストールするライブラリはfont awesomelodashです。

lodashは前述の通り、リクエスト制御のために使いますが、
font awesomeはハートなどのアイコンを無料で使うためのライブラリです。

サイトで使用したいアイコンを探して、指定されたタグとクラスを埋め込むだけで画面にアイコンを表示することができます。
今回は、いいねのアイコンをハートにします。

それでは以下のコマンドでnpm install

// font awesome, lodashをインストール
npm install @fortawesome/fontawesome-free —save
npm i --save lodash


あとは、JSファイルでインポートしてあげればよし。
先に画面のソースコードを載せます。

画面

ファイル名:resources/views/index.blade.php

            <div class="contents">
                <div class="tweet-timeline">
                    <div class="tweet-card" v-for="tweet in tweets" :key="tweet.id" v-cloak>
                        <div class="tweet-contents">
                            <div class="tweet-contents-tweet">
                                <div>@{{ tweet.tweet }}</div>
                            </div>
                            <div class="tweet-contents-footer">
                                    <i v-if="tweet.is_liked" class="fas fa-heart" @click="pushLike(tweet)"></i>
                                    <i v-else class="far fa-heart" @click="pushLike(tweet)"></i>
                            </div>
                        </div>
                    </div>
                </div>
                <infinite-loading @infinite="fetchTweets"></infinite-loading>
            </div>

<i>タグが今回のコアな部分です。

<i class="fas fa-heart">で、font awesomeのアイコン埋め込み
v-ifv-else部分で、未いいねといいね済の出し分けをしています。

tweet.id_likedはサーバー側から受け取るデータ(後編で実装予定)で、

すでにいいねしたツイートに関してはtrue値が渡され、
すでにいいねを押されている状態でツイートが表示されます。

そして、@clickでクリック時に指定のメソッドpushLiketweetオブジェクトを渡して
イベントを実行します。
実行内容はJsに記載します。

SASS

ファイル名:resources/sass/_tweet_style.scss

.tweet{
    &-card {
        width: 400px;
        height: 100px;
        margin: 10px;
        font-size: 16px;
        background-color:#fff;
        border-radius:5px;
        -moz-box-shadow:0 2px 2px 0 rgba(0,0,0,0.2);
        -webkit-box-shadow:0 2px 2px 0 rgba(0,0,0,0.2);
        box-shadow:0 2px 2px 0 rgba(0,0,0,0.2);
    }
    &-contents {
        width: 90%;
        height: 90%;
        margin: auto;
        display: flex;
        flex-direction: column;

        &-tweet {
            flex-basis: 80%;
        }
        &-footer {
            flex-basis: 20%;
        }
    }
}

デザインを少しだけ整えました。
ツイッターカードのようなものでツイート内容を覆ってあげるようにしました。

コピペするなりして自分の好きなようにしてください。

JS

ファイル名:resources/js/tweet.js

window._ = require('lodash');

new Vue({
    el: '#tweet',
    data: {
        userAction: {
            like: {
                list: [],
                debouncedList: []
            },
        },
    },
    methods: {
        pushLike(tweet) {
            if (this.userAction.like.list.indexOf(tweet.id) == -1) {
                this.userAction.like.list.push(tweet.id);
                this.newPostLike(tweet.id);
            }
            this.userAction.like.debouncedList[tweet.id](tweet.id, !tweet.is_liked);
            tweet.is_liked = !tweet.is_liked;
        },

        newPostLike(tweetId) {
            this.userAction.like.debouncedList[tweetId] = _.debounce(this.postLike, 1000);
        },

        postLike(tweetId, likePushed) {
            axios.post(window.location.origin + `/tweet/like`, {
                tweetId: tweetId,
                likePushed: likePushed
            })
            .then(response => {
                console.log(response);
            })
            .catch(error => {
                console.log(error);
            });
        },

1行目でインストールしたlodashを読み込み

userAction.likelistdebouncedListという変数を準備します。
前者はいいね!ボタンが押されたツイートIDのリストを格納し、
後者はdebounceのインスタンス(_.debounce(this.postLike, 1000))を格納するリストです。
それぞれのツイート(いいね!ボタン)に対してインスタンスを用意してあげないと、
どのツイートに対するイベントかがごちゃごちゃになってしまうので後者のリストは重要です。

debounceメソッドは、指定のメソッド呼び出しが行われる度にタイマーをセットできます。
今回の場合は、this.postLikeの呼び出しが停止した1秒後にリクエストが送信されます。

呼び出しが起きた時点で1秒がリセットされるので、連打されていた場合、一生リクエストが飛びません。


いいね!ボタンがクリックされた時の流れは、
pushLikeが実行
(いいね!ボタンが押されていないツイートに関しては、)
→ツイートIDをリストに追加
this.newPostLikeを呼び出しdebounceのインスタンスをリストに追加
debouncedListに格納されたインスタンスを実行


以降、リスト化されたdebounceのインスタンスが呼び出されたら
最後に呼び出されてから1秒後にリクエストをサーバー側にプッシュする
という流れになっています。


いいね!ボタンが押下される度にボタンの出し分けを判定する変数tweet.is_likedがtrue, falseの値を取ることで
未いいね(中身が白)、いいね済(中身が黒)を制御しています。


少々複雑な実装になりましたが、
_.debounce()の使い方と、ツイート毎のリクエストインスタンスのリスト化が鍵になってきます!

最後にnpm run devでSASSとJSをビルドするのを忘れずに!!

まとめ:リクエスト制御にはlodashを使ってみよう

以上、like機能のフロント側の実装でした。

今回のポイントを振り返りましょう

  • font awesomeの使用
  • vueでハートボタン制御
  • lodashでリクエスト制御


いずれもwebアプリ開発には、欠かせない知識です。
これを機に是非ともマスターしたいですね。

Lodashの実装は少々複雑でしたが、
例えばdebounceをリスト化せずに実行したらどんな問題が起こってしまうのか想像する、または試してみるのも手ですね!

たぶん色々なツイートを1秒以内にいいねしまくったら色々とおかしなことが起きます。


さて、次回はいいね機能のコアであるAPI実装です!

いいね機能は頻繁に書き換えられるので、Redis(Key Value Store)というインメモリDBを用いてデータを管理する手法を学べます。




Redisは現在のwebアプリ開発では必須のスキルなので、
使いこなせれば大きくレベルアップできます。
楽しみにしていてください!

それではまた!

プログラミング
スポンサーリンク
シェアする
SHOOTをフォローする
WebエンジニアSHOOTのブログ

コメント

タイトルとURLをコピーしました