vue.jsとFirebaseのRealtime DatabaseとAuthorization機能を利用したSlackクローンの構築方法について複数回にわけて説明を行います。1回目の本文書ではチャットのリアルタイム更新に重要な役割をもつFirebaseのRealtime Databaseの使用方法に重点をおいて説明を行っています。読み終えると下記を理解することができます。

  • Vue.jsからのFirebaseのRealtime Databaseへの書き込み、読み込み
  • Vue.jsとFirebaseのイベントを利用したリアルタイム更新機能の理解

本書を読んだだけでもFirebaseを利用することでリアルタイムのチャット機能を行えることががわかるはずです。リアルタイムのチャット機能のポイントはFirebaseのデータベースにデータが追加されるとFirebase側からVue.jsに伝える仕組みがあるかどうかです。そのためにFirebaseのイベント(valueイベントとchildaddイベント)を利用します。

vue.jsプロジェクトの作成

vue/cliパッケージのインストールが完了している場合はvue createコマンドでプロジェクトの作成を行います。vue/cliパッケージのインストールを行っていない場合はnpxコマンドを利用してプロジェクトの作成を行います。ここではプロジェクトの名前はvue_slack_cloneにしていますが任意の名前をつけてください。

vueコマンドでnpxコマンドのどちらでもvueのプロジェクトを作成することができます。


 $ vue create vue_slack_clone
 or
 $ npx vue create vue_slack_clone

createコマンド実行後に表示されるManually select featuresではBabel, Router, Vuex, Linter / Formatterを選択して進めます。HistoryモードはYを選択してください。

本文書ではVueのバージョンは2で行っています。

インストールが完了したらプロジェクトディレクトリに移動してコンパイルを行います。


 $ cd vue_slack_clone
 $ npm run serve

ブラウザを起動してvue.jsの初期画面が表示されるか確認を行ってください。

vue.jsプロジェクトの初期画面
vue.jsプロジェクトの初期画面

Firebaseの設定

アカウントの登録

Firebaseのユーザアカウントがない人はFirebaseのアカウントの登録が必要になります。アカウントの登録については下記の文書の”アカウントの登録”を参考に行ってください。

アカウント登録時にクレジットカード等の入力は必要なく無料の範囲で動作確認を行うことができます。

プロジェクトの作成

アカウントの登録が完了したらFirebaseの管理画面からプロジェクトの作成を行う必要があります。

プロジェクトの作成ボタンをクリックしてください。

プロジェクトの新規作成
プロジェクトの新規作成

プロジェクトの名前を入力してください。ここではvue-slack-cloneという名前にしています。

プロジェクトの名前を入力
プロジェクトの名前を入力

Googleアナリティクスに関する設定ですが今回は動作確認用なので無効にしてください。無効後に”プロジェクトを作成”ボタンをクリックしてください。

google Analyticsの設定
google Analyticsの設定

プロジェクトの作成完了まで少し時間がかかります。完了すると”続行”ボタンが表示されるのでクリックしてください。

プロジェクト作成中
プロジェクト作成中

クリックすると以下のプロジェクトの概要画面が表示されます。

管理画面
プロジェクト概要画面

Realtime Databaseの作成

左側の開発メニューの2番目にあるDatabaseをクリックしてください。Realtime Databaseの画面が表示されるので”データベースを作成”ボタンをクリックしてください。

リアルタイムデータベースを選択
リアルタイムデータベースを選択

ロケーションの設定画面が表示されるので選択肢に日本はないのでそのまま次へボタンをクリックします。

ロケーションを選択
ロケーションを選択

Realtime Dabaseのセキュリティルールの設定画面が表示されますが動作確認を行いたいので読み取り書き込みがすべて許可されている”テストモードで開始”を選択して、”有効にする”をクリックしてください。

セキュリティルールの設定
セキュリティルールの設定

”有効にする”ボタンをクリックすると以下の画面が表示されます。データベースが作成され、名前はvue-slack-clone-7fecaでデータが何も入っていないのでnullと表示されています。データを保存するとこの画面に保存したデータが表示されます。

データベースの作成完了
データベースの作成完了

アプリケーションの追加

vue.jsなど外部のアプリケーションからFirebaseのデータベースに接続するためには接続情報が必要となります。

プロジェクトの概要画面に戻り画面中央の赤丸</>をクリックしてください。

アプリの追加ボタン
アプリの追加ボタン

アプリのニックネームを入力して”アプリを登録”ボタンをクリックしてください。ここではVue Slack Cloneという名前をつけています。

ウェブアプリの設定
ウェブアプリの設定

FirebaseのSDKが表示されます。このスクリプトをvue.jsプロジェクトのmain.jsファイルに貼り付ける必要があります。

アプリケーションのSDK
アプリケーションのSDK

下記のデータをそのまま貼り付けても動作しないので各自でFirebaseの管理画面からSDKの情報を取得してください。


import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import firebase from "firebase/app";

Vue.config.productionTip = false;

var firebaseConfig = {
  apiKey: "XXXXXXXXXXXXXXXXXXXXXXXXXX",
  authDomain: "vue-slack-3796e.firebaseapp.com",
  databaseURL: "https://vue-slack-3796e.firebaseio.com",
  projectId: "vue-slack-3796e",
  storageBucket: "vue-slack-3796e.appspot.com",
  messagingSenderId: "896148090732",
  appId: "XXXXXXXXXXXXXXXXXXXXXXXXXX9"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);

window.firebase = firebase;

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");

main.jsの上部でfirebaseをimportしていますがfirebaseライブラリをまだインストールしていないのでこれからfirebaseのライブラリのインストールを行います。インストールはnpmコマンドで行います。


 $ npm install --save firebase

Firebaseの動作確認

データベースの初期設定が完了したのでvue.jsからのFirebaseのRealtime Databaseに対して読み書きを行う方法を確認していきます。

MySQLなどのデータベースではデータベース作成後にテーブルや列情報のスキーマを事前に作成する必要がありますがReadtime Databaseでは必要ではありません。リレーショナルデータベースに慣れている人の場合どのようにデータを登録するのか疑問に思うかもしれませんが下記の手順のままに進めてください。

Firebaseのデータベースへの書き込み

vue.jsのプロジェクトのsrc¥viewsの下にあるHome.vueファイルを使ってデータベースへの書き込みの動作確認を行います。Firebaseにデータを書き込む方法はset, pushの2つがあります。両方の動作確認を行い違いを見ていきます。

setメソッドによるデータベースへの書き込み

etによるデータベースの書き込みは下記のように記述することで行うことができます。slack等の名前がでてきますがデータ登録後にこのslackがどこに対応するのかがわかるのでこのまま進めてください。


firebase.database().ref("slack")
  .set({
    content: this.message,
    user: {
      name: "John Doe"
    }
  }

Home.vueファイル内で実際にsetによる書き込み処理を追加したコードは下記のようになります。input要素にv-modelでmessageを設定し入力した値をFirebaseのデータベースに追加します。


<template>
  <div>
    <h1>Firebaseを使った読み書き確認</h1>
    <input v-model="message" />
    <button @click="addMessage">メッセージを追加</button>
  </div>
</template>

<script>
import firebase from "firebase/app";
import "firebase/database";

export default {
  name: "Home",
  data() {
    return {
      message: "",
    };
  },
  methods: {
    addMessage() {
      firebase.database().ref("slack")
        .set({
          content: this.message,
          user: {
            name: "John Doe"
          }
        });
    }
  }
};
</script>
firebaseのimportの2行も忘れずに追加をしてください。

実際にブラウザを使ってデータの追加を行います。input要素に”はじめてのメッセージ”を入力してメッセージを追加ボタンをクリックします。ボタンを押しても入力したメッセージは消えません。

メッセージ入力画面
メッセージ入力画面

Firebaseの管理画面でデータベースに入力されたデータを確認します。先程refの中で設定していたslackを確認することができます。そのslackの下にcontentとuser、さらにuserの下にnameとツリー構造になっていることが確認できます。

データベースのデータを確認
データベースのデータを確認

slackという名前に決まりはなくmessageなど他の名前についても問題ありません。contentやuserを含め任意の名前をつけることができます。

pushによるデータベースへの書き込み

pushメソッドによる書き込みも確認します。コードは先程のsetメソッドをpushメソッドに変更するだけです。


firebase.database().ref("slack")
  .push({
    content: this.message,
    user: {
      name: "John Doe"
    }
  }
push(object)ではなくpushを実行した後にsetというpush().set(object)でも問題ありません。

setメソッドからpushメソッドに変更しただけなので入力画面は変わりません。

追加のメッセージ
追加のメッセージ

メッセージを追加後にFirebase管理画面でデータベースの中身を確認するとFirebase側で一意のキーが自動で付与されます。pushメソッドに変更することで一意のキーが付与されます。

pushメソッドによるデータの追加
pushメソッドによるデータの追加

setメソッドとは異なりpushメソッドでは、一意のキーによりメッセージを識別することができるのでここからはpushを利用してデータベースへのデータの登録を行っていきます。

setメソッドで書き込んだデータはFirebase管理画面から削除を行って行ってください。データの上にカーソルを乗せるとXが表示されるのでXをクリックして削除してください。

Firebaseのデータベースからの読み込み

Firebaseではイベントによりデータベースへのデータの追加や更新が行われるとその処理をブラウザにリアルタイムに反映させることができます。この機能のおかげでユーザがメッセージを追加すると別のブラウザを閲覧しているユーザにも追加したデータが即座に反映されます。この機能がSlackのようなチャットで重要な役割を持っています。

Firebaseからのデータの取得方法にも書き込みと同様にいくつか方法があるのでここではvalueイベントとchild_addedイベントを使ってデータの取得を行います。

valueイベントによるデータ取得

valueイベントを利用したデータベースからのデータ取得は下記のように記述します。onでvalueイベントを設定するとrefで指定したslack以下に登録されたすべてのデータを一括で取得することができます。一括取得は1回ではなくslackにデータが追加される度に一括でデータが取得されます。


firebase.database().ref("slack")
  .on("value", snapshot => console.log(snapshot.val()));
}

vue.jsのライフサイクルフックのmountedにデータ取得のコードを追加することでブラウザでアクセスすると自動でFirebaseのデータベースからデータ取得を行います。


mounted() {
 firebase.database().ref("slack")
   .on("value", snapshot => console.log(snapshot.val()));
 }
}

ブラウザでページにアクセスしてデベロッパーツールのコンソールを見るとデータベースから取得したデータを確認することができます。setメソッド、pushメソッドで登録したデータです。setメソッドで登録したデータを削除している場合はcontentとuserは表示されません。

valueイベントによるデータの取得
valueイベントによるデータの取得

user, contentとFirebaseの管理上から削除して、ブラウザ上からメッセージを2つ追加します。

ブラウザ上からメッセージの追加を行うと追加したデータを含めすべてのデータ(3件分)が取得されていることがわかります。

メッセージを追加した直後に自動でコンソールログに表示されます。

追加したデータを即座に取得
追加したデータを即座に取得

ここまでの動作確認によりvalueイベントではすべてのデータが一括で取得できることを理解しておいてください。

ライフサイクルフックmountedでvalueイベントを設定しているのでデータが追加される度にコンソールにメッセージが表示されます。

child_addedイベントによるデータ取得

valueイベントからchild_addedイベントに変更を行い動作確認を行います。child_addedと名前の通りchild(子)にadded(追加された)されたらイベントが発生しデータの取得が行われます。


mounted() {
 firebase.database().ref("slack")
   .on("child_added", snapshot => console.log(snapshot.val()));
 }
}

ブラウザを開いて確認するとvalueとは異なりデータが1行ごとに分かれていることが確認できます。またすべてのデータ(3件)が取得させていることもわかります。

child_addedイベントによる取得
child_addedイベントによる取得

次にブラウザ上からデータの追加を行ってみましょう。valueイベントは異なりすべてのデータを追加の度に取得するのではなく追加した情報のみ取得していることがわかります。

child_addedでデータを追加
child_addedでデータを追加

child_addedイベントでは最初はすべてのデータを取得し、その後はデータが追加されると追加したデータのみ取得できることがわかりました。

v-forによる取得したデータの表示

valueイベント、child-addedイベントによりFirebaseのデータベースからデータが取得できることが確認できたので取得したデータをv-forディレクティブを利用してブラウザに表示させます。

valueイベント、child-addedイベントどちらでも実現できますがここではvalueイベントを利用して行います。

vue.jsのデータプロパティにmessagesを追加してデータベースから取得したデータをライフサイクルフックのmountedで挿入しています。messagesはv-forを利用して展開させています。各データに自動的に付与されているキーをindexで意図的に表示しています(通常は表示させる必要はありません)。このキーを利用することでデータの更新、削除を行うことができます。


<template>
  <div>
    <h1>Firebaseを使った読み書き確認</h1>
    <input v-model="message" />
    <button @click="addMessage">メッセージを追加</button>
    <ul>
      <li v-for="(message, index) in messages" :key="index">
        {{ message.content }} index:{{ index }}
      </li>
    </ul>
  </div>
</template>

<script>
import firebase from "firebase/app";
import "firebase/database";

export default {
  name: "Home",
  data() {
    return {
      message: "",
      messages: []
    };
  },
  methods: {
    addMessage() {
      firebase.database().ref("slack")
        .push({
          content: this.message,
          user: {
            name: "John Doe"
          }
        });
    }
  },
  mounted() {
    firebase.database().ref("slack")
      .on("value", snapshot => (this.messages = snapshot.val()));
  }
};
</script>
v-forを使いデータベースのデータを表示
v-forを使いデータベースのデータを表示

リアルタイムで追加されるメッセージの動作確認

データベースからの書き込みと読み込みの設定が完了すると複数のブラウザでリアルタイムにデータが追加される機能を確認することができます。

2つのブラウザを起動してください。片方のブラウザで下記のようにメッセージを追加しinput要素に記述しメッセージ追加ボタンをクリックしてください。

片方のブラウザからメッセージを追加
片方のブラウザからメッセージを追加

追加後に両方のブラウザで追加したメッセージが即座にブラウザ上に追加されることを確認してください。リアルタイムでの反映が行えることが確認できます。

追加したメッセージが即座に反映
追加したメッセージが即座に反映
ユーザ認証やチャネルなどの設定は行っていないのでだれでもメッセージを書き込める状態です。今後ユーザ認証やチャネルなどの機能を追加していきます。

データの削除方法

v-forで展開した際に表示したindexを利用してデータの削除方法も確認しておきます。各メッセージにspanタグを追加しクリックイベントでdeleteMessageメソッドを追加します。引数にはindexを設定します。


<ul>
  <li v-for="(message, index) in messages" :key="index">
    {{ message.content }} index:{{ index }}
    <span @click="deleteMessage(index)">X</span>
  </li>
</ul>

vue.js側の設定ではclickイベントで受け取ったindexを利用してFirebaseのデータベースからそのindexを持つデータを削除します。そのコードは下記の通りです。slackの子(child)で指定したindexを持つデータを指定してremoveで削除しています。


deleteMessage(index) {
  firebase
    .database()
    .ref("slack")
    .child(index)
    .remove();
}

ブラウザ上でメッセージに右にあるXを押すとそのメッセージがブラウザ上から削除されることを確認してください。valueイベントでは削除後のすべてのデータを再度取得するためvue.js側でmessagesの削除処理を行う必要はありません。

更新を行ったHome.vueファイルの中身は下記の通りです。


<template>
  <div>
    <h1>Firebaseを使った読み書き確認</h1>
    <input v-model="message" />
    <button @click="addMessage">メッセージを追加</button>
    <ul>
      <li v-for="(message, index) in messages" :key="index">
        {{ message.content }} index:{{ index }}
        <span @click="deleteMessage(index)">X</span>
      </li>
    </ul>
  </div>
</template>

<script>
import firebase from "firebase/app";
import "firebase/database";

export default {
  name: "Home",
  data() {
    return {
      message: "",
      messages: []
    };
  },
  methods: {
    addMessage() {
      firebase.database().ref("slack")
        .push({
          content: this.message,
          user: {
            name: "John Doe"
          }
        });
    },
    deleteMessage(index) {
      firebase
        .database()
        .ref("slack")
        .child(index)
        .remove();
    }
  },
  mounted() {
    firebase.database().ref("slack")
      .on("value", snapshot => (this.messages = snapshot.val()));
  }
};
</script>

まとめ

vue.jsからFirebaseのRealtime Databaseへのデータの書き込み、読み込み、削除またブラウザを利用したリアルタイムでの更新を実現することができました。複数のブラウザを開いて実行すると片方のブラウザでの処理が即座に別のブラウザの画面の更新が行われたことからSlackのようなチャットアプリが作成できることがこの時点で理解できるかと思います。

続きは下記の文書で公開しています。