RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Vue.jsのプロジェクトでチャット機能を作成する方法

f:id:tech-rakus:20200424153609j:plain

こんにちは。y_kwmtです。

はじめに

少し前にVue.jsとFirebaseを用いてGoogleアカウントのログイン機能とMarkdown書式のメモを作成する機能を作成しました。

tech-blog.rakus.co.jp

以下のサイトを参考にしてMarkdown書式のメモを応用したチャット機能を作成したので、
本記事では実装の準備、実装方法について紹介していきます。

cr-vue.mio3io.com

実装する機能

チャット機能

  • メッセージを送信、受信できる

  • 送信したメッセージをMarkdown書式で表示

  • ページを再訪問しても送信したメッセージが残っている

  • ※前回の記事で作成したメモ機能は使わないので一旦なくします

データベース作成

プロジェクト内の「Database」を選択して「Realtime Database」からデータベースをテストモードで作成します。チャットのメッセージとメッセージ送信者の情報をレコードとして追加します。

f:id:y_kwmt:20200423085810p:plain

Vue.jsでFirebaseを使用する

firebase モジュールをインストールして main.js で読み込みます。

npm install firebase


Firebaseのプロジェクトの「Settings」を開いて 「Firebase SDK snippet」のスクリプト部分を追記してください。

import firebase from firebase
import Vue from 'vue'
import App from './App.vue'
import 'firebase/firestore';

// ここから
var firebaseConfig = {
    apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "XXXXXXXXX.firebaseapp.com",
    databaseURL: "https://XXXXXXXX.firebaseio.com",
    projectId: "XXXXXXXXX",
    storageBucket: "XXXXXXXXX.appspot.com",
    messagingSenderId: "XXXXXXXXX",
    appId: "X:XXXXXXXXXXXX:web:XXXXXXXXXXXXXXXXXXXX",
    measurementId: "X-XXXXXXXXXX"
  };
 
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  firebase.analytics();

// ここまで

new Vue({
  el: '#app',
  render: h => h(App)
})

チャット機能の実装

前回投稿したブログで登場した、メモを作成する画面「Editor.vue」を改修し、
ログイン後の画面にチャット機能を追加します。
先ほど作成したデータベースにチャットのメッセージ、メッセージ送信者、
Googleアカウントの画像パスがレコードとして保存されます。

<template>
<div id="home">
  <h1>エディター画面</h1>
  <p>
    <span>{{ user.displayName }}</span>
    <button @click="logout">ログアウト</button>
  </p>

  <transition-group name="chat" tag="div" class="list content">
    <!-- chatにはキー、送信者名、Googleアカウントの画像パス、メッセージが入っています。 -->
    <section v-for="{ key, name, image, message } in chat" :key="key" class="item">
      <div class="item-image"><img :src="image" width="40" height="40"> {{ name }}</div>
      <div class="item-detail">
        <div class="item-message">
          <!-- メッセージがMarkdown書式で表示されます。 -->
          <div v-html="message"></div>
        </div>
      </div>
    </section>
  </transition-group>
 
   <!-- 入力フォーム -->
  <form action="" @submit.prevent="doSend" class="form">
    <!-- Enterを押すとメッセージが送信されます。Enter + Shiftで改行します。 -->
    <textarea
       v-model="input"
       :disabled="!user.uid"
       @keydown.enter.exact.prevent="doSend"></textarea>
    <div>
      <!-- ボタンを押すとメッセージが送信されます -->
      <button type="submit" :disabled="!user.uid" class="send-button">Send</button>
    </div>
    <div>
      プレビュー<div v-html="preview()"></div>
     </div>
  </form>
</div>
</template>
<script>
import marked from "marked";
// 改行を <br> タグに変換するモジュール
export default {
  name: 'editor',
  props: ["user"],
  data() {
    return {
      chat: [],  // 取得したメッセージを入れる配列
      input: ''  // 入力したメッセージ
    }
  },
  created() {
    // データベースからメッセージ情報取得
    const ref_message = firebase.database().ref('message')
    if (this.user) {
      this.chat = []
      // message に変更があったときのハンドラを登録
      ref_message.limitToLast(10).on('child_added', this.childAdded)
    } else {
       // message に変更があったときのハンドラを解除
       ref_message.limitToLast(10).off('child_added', this.childAdded)
    }
  },
  methods: {
    logout: function() {
      firebase.auth().signOut();
    },
    preview: function() {
      return marked(this.input);
    },
    childAdded(snap) {
      const message = snap.val()
      this.chat.push({
        key: snap.key,
        name: message.name,
        image: message.image,
        message: marked(message.message)
      })
    },
    doSend: function() {
      if (this.user.uid && this.input.length) {
        // firebase にメッセージを追加
        firebase.database().ref('message').push({
          message: marked(this.input),
          name: this.user.displayName,
          image: this.user.photoURL
        }, () => {
          this.input = '' // フォームを空にする
        })
      }
    },
    displayTitle: function(text) {
      return text.split(/\n/)[0];
    }
  }
}
</script>


動作確認

実際の画面がこちらです。メッセージを入力するとプレビューが表示され、
送信するとMarkdown書式でメッセージが表示されます。
黒く塗りつぶしている部分はGoogleアカウントの名前を表しています。

f:id:y_kwmt:20200423091742p:plain

おわりに

今回はVue.jsでMarkdown書式を応用したチャット機能を紹介しました。
データベースなどの設定やApp.vueを2回呼び出してしまうエラーなどに少し時間がかかってしまいました。
今後はVue.jsだけでなく、他のフロント系の技術を学習して紹介していきたいと思います。


  • エンジニア中途採用サイト
    ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
    ご興味ありましたら是非ご確認をお願いします。
    20210916153018
    https://career-recruit.rakus.co.jp/career_engineer/

  • カジュアル面談お申込みフォーム
    どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
    以下フォームよりお申込みください。
    forms.gle

  • イベント情報
    会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.