URL: https://nrpans-twinkle.vercel.app
Nuxt と Firebase で作成した Twitterクローンサイトです。
よろしければお試しください😄(ツイートにはGoogleアカウントによるログインが必要です)
| 主要パッケージ | バージョン(作成時) | 説明 |
|---|---|---|
| nuxt | 3.3.3 | サイトの作成(SSRモード) |
| @vueuse/core | 9.13.0 | ダークモード、無限スクロール、キーストロークイベントなどの実装 |
| firebase | 9.20.0 | Firebase Javascript SDK |
| tailwindcss | 3.3.1 | UI |
| dayjs | 1.11.7 | 日付のフォーマット |
useAsyncData()でFirestoreからデータを取得した。useHead()やuseSeoMeta()を使用し、サーバーサイドでSEO系タグを生成した。useState()でユーザー情報(/users/uid)を持たせ、サーバーサイドでログイン状態を判定した。
error.vueでもlayouts/default.vueを使用することで404ページのビューを共通化し、$route.name.toString()を参照し細かな表示を切り替えた。- VueUseの
useIntersectionObserver()を使用して無限スクロールを実装した。 - middlewareで手順を踏まない退会処理を防止した。
- Googleアカウントによる認証。
- Authenticationの
uidをFirestoreの/users/uidとした。 - Authenticationの
uidはセキュリティルールで参照したいため、適宜Firestoreのドキュメントフィールドに持たせた。
-
ツイートは実データを
/tweets/xxxのみに持たせ、/users/uid/myTweetsSubCollectionにはtweetDocId(=xxx)を持たせた。
リツイート(いいね)時に更新するドキュメントを/tweets/xxx/retweetUsersSubCollection(/tweets/xxx/likeUsersSubCollection)に限定し、他人のmyTweetsSubCollectionに触れないようにする目的。 -
imageUrl(実際の画像のURL)とimageFullPath(Cloud Storageでのファイルパス)の2つのフィールドを用意した。
前者は読み込み速度改善のため。(Twinkleでは画像の読み取りに制限はないため、画像投稿時にfirebase/storage.getDownloadURL()でURLを生成している)
後者はツイート削除時やプロフィール画像更新時に画像を削除する際に必要となるため。 -
各ツイートに表示する(簡易)ユーザー情報を
/tweets/xxx/userInfoに保存した。
(都度/users/uidからユーザー情報を取得しクライアントサイドでjoinするのはコストが大きい) -
「リツイート = typeフィールドが
retweetのツイート」 は設計ミスだった。
ツイート一覧取得時にtypeフィールドを参照し、retweetであればoriginalTweetDocIdフィールドからツイートを取得する。ツイートとリツイートがどちらも/tweetsコレクションへのドキュメント作成・削除となりセキュリティルールが共通となってしまった。 -
その他感想
- フォロー機能は難しいと感じた。
フォローしている人のツイート一覧を取得することは/tweetsコレクションからslugフィールドがfollowingsに含まれるtweetドキュメントを取得する、という操作になりそうだが、firebase/firestore/where inが最大10個までしか比較できないため、followingsの最大値が10に制限される(= 11人以上フォローできない)と思う。
かといって、フォローされている人が各フォロワーの/users/uid/followingTweetsSubCollection配下にtweetドキュメントを保存していくことも、followingAのフォロワー数が多い場合を考えると間違っている気がする。 - 鍵アカウント機能は難しいと感じた。
セキュリティルールはコレクション(に所属するドキュメント一括)に対して指定するので、鍵アカウントの場合は(フィールドの値更新ではなく)ドキュメントを移動する必要がある。
- フォロー機能は難しいと感じた。
- フォルダ名にuidを含めることで、他人に自分の投稿画像が削除されることを防いだ。