Firebase × Vue.js で サクッとリアルタイムに反映される掲示板を作る


最近よく耳にするFirebase。
サーバーレスなモバイルアプリやWebアプリケーションを構築できるサービスのようです。 最近Vue.jsに入門したので、勉強がてらFirebaseを利用したサーバーレスなVueアプリを作ってみたいと思います。

Vue.jsやFirebaseを利用したことがない方に、これらのイメージをつかんでいただければ幸いです。

自分のレベル感 (対象者)

・Vue.js 入門者
・初 Firebase

作成するアプリ

こんな感じで、リアルタイムに更新される掲示板アプリを作成します。 HTMLとJSだけで動的なサイトを構築することが可能になります。

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

ローカルのvue.jsのプロジェクトを作成します。 すみません。vue-cli使ってないです。手作業で作成しました。

構成

board-app
  |- index.html
  |- index.js
  |- index.css

雛形となるhtmlを作成しておきましょう。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>board_app</title>
  <link href="index.css" rel="stylesheet">
</head>
<body>
  <div id="board">
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
  <script src="main.js"></script>
</body>
</html>

CDNを使ってVue.jsを読み込んでいます。

投稿機能の実装

vue.jsを用いて投稿機能を実装していきます。 勉強がてら、コンポーネントに分割した構成にします。 firebaseは後ほど実装するので、この段階ではデータの永続化がなされていません。

index.htmlを以下のように編集しましょう。

index.html

<div id="board">
   <h2>掲示板</h2>
   <ul class="lists" style="list-style-type: none">
     <board-list v-for="list in lists"
       v-bind:name="list.name"
       v-bind:message="list.message"
       v-bind:date="list.date">
     </board-list>
   </ul>
   コメント: <textarea v-model="message"></textarea> </br>
   <button v-on:click="doAdd">投稿</button> -->
   <board-form v-on:input="doAdd"></board-form>
</div>

index.jsを作成して、次のように編集します。

index.js

Vue.component('board-list', {
  template: '<li>{{name}} {{date}}</br>{{message}}</li>',
  props: ['name', 'message', 'date']
})

Vue.component('board-form', {
  template: '<div class="form-area">名前 : <input v-model="name"> </br>コメント: \
  <textarea v-model="message"></textarea> </br><button v-on:click="doAdd">書き込む</button></div>',
  data: function(){
    return{
      message: '',
      name: ''
    }
  },
  methods: {
    doAdd: function(){
      this.$emit('input', this.name, this.message)
    }
  }
})

var board = new Vue({
  el: '#board',
  data: {
    name:'',
    message: '',
    date: '',
    lists: [
    ]
  },
  methods: {
    doAdd: function(name, message){
      var now = new Date();
      this.lists.push({
        name: name,
        message: message,
        date: now.getMonth()+1 + '月' + now.getDate() + '日' + now.getHours() + '時' + now.getMinutes() + '分'
      })
    }
  }
})

上から順に、board-listコンポーネント、board-formコンポーネントを定義し、new VueでルートになるVueインスタンスを作成し、#boardと紐づけています。

board-structure

ちなみにコンポーネントは、ルートインスタンスが作成される前に定義しておく必要があるそうです。
この段階を、Vue.js devtoolsで見た様子が以下のようになります。 Chrome拡張Vue.js devtools便利すぎるので、vue.jsに入門する方は、絶対に導入しましょう。理解、開発ともに効率化されます。

vud-dev

formから入力された値は、ルートインスタンスに渡され、つぎの形で、dataオプションに登録されているlists配列に追加されます。

name:  入力した名前
message: 入力したコメント
date: 時間

時間は date: now.getMonth()+1 + ‘月’ + now.getDate() + ‘日’ + now.getHours() + ‘時’ + now.getMinutes() + ‘分 で取得します。

これを、v-forを用いて一つずつ要素を取り出しpropsを通して子コンポーネントに受け渡して繰り返し表示させています。しかし、この状態だとブラウザをリロードした段階でデータがリセットされてしまいます。そこで、firebaseを導入してデータの永続化を行なっていきましょう。

Firebaseでデータを永続化する

1. Firebaseにアクセスして、プロジェクトを作成する

firebase-make-project

2. スニペットの取得

get-snippet

後ほどスニペットを使用することになるので、保存しておきましょう。

3. リアルタイムデータベースの作成

データベースを作成後、読み込み書き込みを許可するためにルールで

”.read”: false, => “.read”: true,
“.write”: false => “.write”: true

に変更します。

make-db

4. 実装

index.htmlとindex.jsを次のように編集しましょう。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>board_app</title>
  <link href="index.css" rel="stylesheet">

//<===========取得したスニペットを貼り付け=========
<script src="https://www.gstatic.com/firebasejs/5.1.0/firebase.js"></script>
<script>
  // Initialize Firebase
  var config = {
    apiKey: "取得したapi keyを記述",
    authDomain: "取得したauthDomainを記述",
    databaseURL: "取得したdatabaseurlを記述",
    projectId: "取得したprojectIdを記述",
    storageBucket: "",
    messagingSenderId: "取得したmessagingSendrldを記述"
  };
  firebase.initializeApp(config);
</script>
//=====================================>

</head>
<body>
  <div id="board">
    <h2 class="board-title">掲示板</h2>
    <ul class="lists" style="list-style-type: none">
      <board-list v-for="(list, key) in lists"
        :key="key"
        :name="list.name"
        :message="list.message"
        :date="list.date">
      </board-list>

    </ul>
    <board-form v-on:input="doAdd"></board-form>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
  <script src="index.js"></script>
</body>
</html>

head内に、2. で取得したスニペットを貼り付けます

index.js

Vue.component('board-list', {
  template: '<li class="board-list"><div class="board-list__upper">名前:{{name}}{{date}}</div>{{message}}</li>',
  props: ['name', 'message', 'date', 'id'],
})

Vue.component('board-form', {
  template: '<div class="form-area">名前 : <input v-model="name"> </br>コメント: \
  <textarea v-model="message"></textarea> </br><button v-on:click="doAdd">書き込む</button></div>',
  data: function(){
    return{
      message: '',
      name: ''
    }
  },
  methods: {
    doAdd: function(){
      this.$emit('input', this.name, this.message)
      this.message =  ''
      this.name = ''
    }
  }
})

var board = new Vue({
  el: '#board',
  data: {
    lists: [
    ]
  },
  created: function(){
    var vue = this;
    firebase.database().ref('board').on('value', function(snapshot) {
      vue.lists = snapshot.val();
    });
  },
  methods: {
    doAdd: function(name, message){
      var now = new Date();
      firebase.database()ref('board').push({
        name: name,
        message: message,
        date: now.getMonth()+1 + '月' + now.getDate() + '日' + now.getHours() + '時' + now.getMinutes() + '分'
      });
    }
  }
})

注目するべき箇所は3つほどですかね。

1.firebaseへの書き込み

firebase.database()ref(‘board’).push()でpushの引数に渡されたデータをfirebaseへ書き込むことができます。

2.firebaseから読み込み

firebase.database().ref(‘board’).on(‘value’, function(snapshot) {})とすることで、イベントの発生時(データベースの変更時)に、リッスンすることができます。つまり、データベースの変更を自動で検知して、onに渡した関数を実行させることができます。これまでは、フォームに入力された値は直接ルートインスタンスの持つlists配列に収められていましたが、firebaseを経由してlists配列に収めバインドされるように変更しました。こうすることで、クライアント側に依存せずに、複数のクライアントでで同期した掲示板を運営することができるようになりました!!

3.keyの設定

v-forでコンポーネントを繰り返し表示させているので、一意なkeyを与えなくてはなりません。先ほどfirbaseから取得した値は、listsに次のような形で収められています。

firebaseにデータを保存した際に一意となる値が割り振られているのこの値をkeyとして用いています。

board-gif

これで、リアルタイムに更新される掲示板を作成することができたはずです。 登録されたデータは、firebaseのワークスペース上から確認することが可能です。

見た目をリッチに

最後に、簡単なcssを追加して見た目を少しだけよくしておきます。

index.css

body{
  background: #FFC766;
}

#board{
  width: 70%;
  padding: 10px 0;
  border: 1px solid #333;
  border-radius: 0.75em / 0.75em;
  margin: 0 auto;
  background: #EFEFEF;
}

.board-list{
  margin: 15px 0;
}

.board-list__upper{
  font-weight: 600;
}

.form-area, .board-title{
  width: 90%;
  margin: 0 auto;
}

これで多少は、見た目も掲示板らしくなったはずです。 これにて完成。

board-complete

まとめ

いかがでしたか? 僕個人としては、vue.jsは割と直感的に記述できる印象を受けました。 firebaseも数クリックでNoSQLデータベースを作成でき、かつリアルタイムで 更新されるのに驚きました。便利な世の中です!! いずれはVue.jsでSPA作成できるように今後も勉強を続けていこうと思います。

参考文献:

『基礎から学ぶVue.js』

firebase 公式

vue.js 公式

comments powered by Disqus