Vue(2系)×TypeScript×Firebaseでチャットアプリ
まずは基本的な記述方法を理解する
以前少しだけTSにチャレンジした事がありましたが、ほとんど忘れていたので再学習。
おすすめの記事が掲載されている記事
実際に今回学習に使用した記事。記事1を軸に分かりにくい箇所は記事2を参照。型については3の記事が最も分かりやすかった。
とりあえず型システムについての箇所をさらっと読んで後は手を動かしてみる。
アプリ作成にチャレンジ
どんなアプリ?
以下の記事で紹介されているVue.js×Firebaseで作られたチャットアプリをTSで作ろうと思います。
Vue.js+Firebaseで認証付きチャット | 基礎から学ぶ Vue.js
TSの書き方について
色んな記事がありましたが、今回はTSに触れる事が目的のため、もっともVueらしく書けるこちらの記事を参考に作成。
[Vue+TypeScript] Vue.extend で Vue らしさを保ちつつ TypeScript で書くときの型宣言についてまとめた - Qiita
環境構築
- Frirebase
"dependencies": { "firebase": "^7.21.0", "save": "^2.4.0", "vue": "^2.6.11" },
実装
<template> <div id="app"> <header class="header"> <h1>Chat</h1> <div v-if="user.uid" key="logout"> {{ user.displayName }} <button type="button" @click="doLogout">ログアウト</button> </div> <div v-else key="login"> <button type="button" @click="doLogin">ログイン</button> </div> </header> <transition-group name="chat" tag="div" class="list content"> <section v-for="{ key, name, image, message } in chat" :key="key" class="item"> <div class="item-image"><img :src="image" width="40" height="40"></div> <div class="item-detail"> <div class="item-name">{{ name }}</div> <div class="item-message"> <p tag="div">{{ message }}</p> </div> </div> </section> </transition-group> <form action="" @submit.prevent="doSend" class="form"> <textarea v-model="input" :disabled="!user.uid" @keydown.enter.exact.prevent="doSend"></textarea> <button type="submit" :disabled="!user.uid" class="send-button">Send</button> </form> </div> </template>
<script lang="ts"> import Vue from "vue" import firebase, { analytics } from "firebase" export type UserType = { displayName: string; uid: string; photoURL: string; } export type ChatType = { key: string; name: string; image: string; message: string; } export type SnapType = { val(): Function & MessageType; key: string; message: object; } export type MessageType = { message: string; name: string; image: string; } export default Vue.extend({ name: 'App', data() { return { user: {} as UserType, input: "" as string, chat: [] as ChatType[] } }, created() { firebase.auth().onAuthStateChanged((user: any): void => { this.user = user ? user : {}; const refMessage = firebase.database().ref('message') if (user) { this.chat = [] // message に変更があったときのハンドラを登録 refMessage.limitToLast(10).on('child_added', this.childAdded) } else { // message に変更があったときのハンドラを解除 refMessage.limitToLast(10).off('child_added', this.childAdded) } }) }, methods: { doLogin(): void { const provider = new firebase.auth.TwitterAuthProvider() firebase.auth().signInWithPopup(provider) }, doLogout(): void { firebase.auth().signOut() }, scrollBottom(): void { this.$nextTick(() => { window.scrollTo(0, document.body.clientHeight) }) }, childAdded(snap: SnapType): void { const message = snap.val() this.chat.push({ key: snap.key, name: message.name, image: message.image, message: message.message }) this.scrollBottom() }, doSend(): void { if (this.user.uid && this.input.length) { firebase.database().ref('message').push({ message: this.input, name: this.user.displayName, image: this.user.photoURL }, () => { this.input = '' }) } } } }) </script>
- methodは戻値が無いためデータ型はvoid
onAuthStateChanged
の引数は色々試したがany
以外通せず。。。 笑
良いと思った点
- TSに関してはこれを作っただけだと知識不足もあって正直良さが分からなかった。ただ、一旦TS触ってみれたので、今後通常のJSを書きながらここTSだったら防げたな〜という気付きが得られるかもしれない
- 今回の目的とは違うがfirebaseに関してこれまで認証とホスティングでしか使った事無かったけど、リアルタイム通信が簡単に出来てすごいと思った
イマイチと思った点
- 型を通すってテスト通すくらい負担に感じた。実際今回は一部型を通すの妥協したw。
- 外部APIとの連携が特に難しいと感じた。今回だとfirebaseの関数の型をどう通すかggってもよく分からないものもいくつかあった
今後
- 今後もちょいちょい触りながら既存プロジェクトへどう部分的に導入していくかを考えられるくらいには知識を得たい