【初心者向け】【FirestoreとHosting編】Vuetify3(Vue.js3)とFirebaseを使って掲示板アプリをつくってみました。

※ 当サイトではアフィリエイト広告を利用しています。リンクは広告リンクも含みます。

この記事は約21分で読めます。
広告

Vuetifyは、Vue.jsベースのユーザーインターフェースが簡単に作れます。
Firebaseは、Googleが提供するモバイルおよびWebアプリケーションのためのプラットフォームです。今回その中のCloud Firestore(データベース)とHosting(アプリ公開)を利用します。
開発環境、Node.jsがインストールされてるWindows11で実行しました。エディターはVSCode。

完成品 Firestore+Vuetifyで作った掲示板 名前とメッセージを書いて投稿。削除もできます。
これのコードは載せますが解説はありません。
firebase関連の処理や設定は別ファイルにするのが一般的なようですが、全部1つのファイルに書いてあります。動かすことが目的なのでセキュリティ等の検証は行っていません。その点はご了承ください。

広告

無料なのか?

Vuetifyは無料です。
Firebaseを使用するにはGoogleアカウントがあれば使用できます。クレカ登録もしていません。
Firebase の料金プランでSparkプランについての説明を読むと本当に無料なのか、ちょっと不安です。
今回Cloud Firestore(データベース)とHosting(アプリ公開)を利用します。
Spark プランの内容に、
”有料の Firebase プロダクトに対する無料の使用量割り当て(Cloud Firestore、Cloud Storage、Hosting など)”と書いてあります。
Spark プランに関する重要事項には
”いずれかのプロダクトで 1 か月あたりの無料割り当ての上限を超えた場合、その月の残りの期間にそのプロダクトを使用できなくなります。”
と書いてありますが課金されないか気になります。
その下には
”アクションとシナリオによっては、プロジェクトのお支払いプランが自動で切り替わる場合があります。”と書いてあったり、よくわかりません。
一応使用状況が確認できるようになっています。

上の”当月に課金される追加使用分”とかいてあるところが気になります。英語モードで見ると以下
Additional usage billed for month: Bytes stored over 1GB, Bandwidth over 10GB
多分、ダウンロードがBandwidthでストレージがBytes storedではないかと思います。
1GBではなく10GBではないのか?
使っていて課金されるようであれば、ここでご報告します。

Vuetify

Vuetifyについては2ですがVuetify入門を参考にしてください。
今回は最新のVuetify3でアプリケーションを作ります。Node.js が必要です。Node.jsがインストールされている環境でGet started with Vuetify3に従ってVuetify3の開発環境を用意します。

Node.jsのバージョン確認
node -v
v18.15.0
yarnが入っていないときyarn create vuetifyを実行するとエラーになる。(追記:Vuetify3の公式のインストレーションにyarnしかなかったのに今、見たらnpmもあった。firebase公式がnpmで紹介されてるからnpmのほうがいいかも)

そういうときはyarnインストール。参考:Windows 11でyarnをインストールする方法
npm install -g yarn
yarn create vuetify
以下のように選択
√ Project name: ... vuetify-project
√ Which preset would you like to install? » Default (Vuetify)
√ Use TypeScript? ... No 
√ Would you like to install dependencies with yarn, npm, or pnpm? » yarn
cd vuetify-project
yarn dev
http://localhost:3000/にアクセスするとVuetifyの画面が表示します。
CTRL+Cでプログラムを止めます。

firestoreをインストール(公式はnpm install firebaseとあるがネットで調べたらyarnもできた)
yarn add firebase
code .でVSCodeを立ち上げます。

Vuetify部分は以前Vuetify2で作ったものですがVuetify3でエラーがでたところを少し修正するだけで使えました。
デフォルトでできるHelloWorld.vueコンポーネントを置き換えるだけにします。この段階ではfirebase側の設定をしていないので実行できません。

<template>
  <v-app>
    <v-main>
      <!-- <HelloWorld /> -->
      <FrontEnd/>
    </v-main>
  </v-app>
</template>

<script setup>
  // import HelloWorld from '@/components/HelloWorld.vue'
  import FrontEnd from '@/components/FrontEnd.vue'
</script>

apiKey: と projectId: は”それぞれ人によって違う“,ので変更してください。(後述)

<template>
    <v-app id="inspire"> 
      <v-app-bar
        class="mt-4"
        max-height="50"
        color="deep-purple"
        dark
        absolute
      >
        <v-app-bar-nav-icon @click="drawer = true"></v-app-bar-nav-icon>
          <v-toolbar-title>Firestore+Vuetifyで作った掲示板</v-toolbar-title>
      </v-app-bar>
  
      <v-navigation-drawer
        v-model="drawer"
        absolute
        temporary
      >
        <v-list
          nav
          dense
        >
          <v-list-item-group
            active-class="deep-purple--text text--accent-4"
          >
            <v-list-item href="https://kikuichige.com" target="_blank">
                <v-list-item-icon>
                    <v-icon>mdi-home</v-icon>
                    </v-list-item-icon>
                    <v-list-item-title>イチゲブログ</v-list-item-title>
            </v-list-item>
  
            <v-list-item href="https://vuetifyjs.com/ja/" target="_blank">
                <v-list-item-icon>
                    <v-icon>mdi-home</v-icon>
                    </v-list-item-icon>
                    <v-list-item-title>Vuetify</v-list-item-title>
            </v-list-item>
          </v-list-item-group>
        </v-list>
  
      </v-navigation-drawer>
      <v-main class="grey lighten-2">
        <v-container>
          <v-row>
            <v-col class="mt-2" cols="12">
              <v-sheet height="230" color="orange lighten-2" class="mt-2 mx-auto" max-width="400">
                  <v-text-field
                    v-model="new_article_heading"
                    label="名前を入力してください"
                    filled>
                  </v-text-field>
                  <v-text-field
                    v-model="new_article_body"
                    label="メッセージを入力してください"
                    filled>
                  </v-text-field>
                  <v-btn color="success" dark v-on:click="createNewUser">投稿</v-btn>
              </v-sheet>
            </v-col>
  
            <template v-for="(article, index) in articles" :key="index">
              <v-col
                
                class="mt-2"
                cols="12"
              >
                <v-card
                    class="mt-2 mx-auto"
                    max-width="400"
                    color="lime lighten-5"
                >
                    <v-card-text class="text--primary">
                      <div><br>ID: {{ article.article_id}}<br>投稿日時: {{ article.pub_date}}<br>名前: {{ article.article_heading}}<br>{{ article.article_body}}</div>
                    </v-card-text>
                    <v-card-actions>
                        <v-btn color="success" dark v-on:click="DeletedArc(article.article_id)">削除</v-btn>
                    </v-card-actions>
                </v-card>
              </v-col>
            </template>
          </v-row>
        </v-container>
      </v-main>
      <!-- フッター -->
      <v-footer padless>
          <v-col
            class="text-center"
            cols="12"
          >
            <p>Copyright (c) 2023 イチゲブログ</p><v-btn href="https://opensource.org/licenses/mit-license.php">Released under the MIT license</v-btn>
          </v-col>
      </v-footer>
      <!-- フッターend -->
    </v-app>
  </template>
    
  <script>
  // 「Cloud Firestore を初期化する」コピーしたものbegin
    import { initializeApp } from "firebase/app";
    import { getFirestore } from "firebase/firestore";
  
      const firebaseConfig = {
          apiKey: "それぞれ人によって違う",
          projectId: "それぞれ人によって違う",
      };
      // Initialize Firebase
      const app = initializeApp(firebaseConfig);
      // Initialize Cloud Firestore and get a reference to the service
      const db = getFirestore(app);
  //「 Cloud Firestore を初期化する」をコピーしたものend
  // 使用する関数(firebaseが用意している)をここに追加する
    import { collection, getDocs,addDoc,doc, deleteDoc} from "firebase/firestore"
  
      export default {
        data() { 
            return{
                drawer: null,
                articles: [],
                new_article_heading:'',
                new_article_body:'',
                del_article_id:'',
            };
        },
        created(){
            this.getArticles();
        },
        methods: {
          // firestoreからデータ取得
            async getArticles() {
              this.articles=[];
              const querySnapshot = await getDocs(collection(db, "users"));
              querySnapshot.forEach((doc) => {
                let article_buf=doc.data();
                article_buf["article_id"]=doc.id
                console.log("article_buf => ", article_buf);
                this.articles.push(article_buf)
              });
            // 日付順にソート
              this.articles.sort((a, b) => new Date(a.pub_date) - new Date(b.pub_date)).reverse();
            },
            // firestoreにデータ書き込み
            async createNewUser(){
              var date = new Date();
              try {
                const docRef = await addDoc(collection(db, "users"), {
                  "article_heading": this.new_article_heading,
                  "article_body": this.new_article_body,
                  "pub_date":date.toLocaleString(),
                });
                console.log("Document written with ID: ", docRef.id);
                this.new_article_heading='';
                this.new_article_body='';
              } catch (e) {
                console.error("Error adding document: ", e);
              }
              this.getArticles();
            },
            // firestoreのデータ1件削除
            async DeletedArc(delId){
              console.log("delid=>",delId);
              await deleteDoc(doc(db, "users", delId));
              this.getArticles();
            },
          },
      }
    </script>
public/index.html
<html lang="en">→<html lang="ja">にしておくとアプリを開いたときブラウザが翻訳するか?とたずねてこなくなります。
タブに表示する文字は以下で設定
<title>Firestore+Vuetifyで作った掲示板</title>
目次へ

Firestore

データベースを作成します。

Firebase CLIのインストール

Firebase CLI をインストールするにしたがってCLIをインストールしGoogleアカウントと紐づけます。

// firebase CLIのインストール(公式はnpmしかないがネットで調べたらyarnもできた)
yarn add firebase-tools
Google アカウントで Firebase にログインする
firebase login

Firestoreの設定

Googleアカウントでログインした状態でFirebaseへアクセス→コンソールへ移動(右上にある)→プロジェクトを追加→任意のプロジェクト名を入力→続行→Googleアナリスティクスを使っていなければ「Googleアナリスティクスを有効にする」をOFF(私はOFF)にして続行→プロジェクトを作成→続行。

左の構築にあるFirestore databaseをクリックするとfirestoreに移動します。
Cloud Firestore データベースを作成する(Web Version9(modular))にしたがってデータベースを作ります。
Cloud Firestoreへ移動→データベースの作成→テストモード→データベースのロケーションは私はasia-northeast1東京を選びました。有効にする→完了
左上のFirebaseをクリック→今、作ったプロジェクトをクリック→プロジェクトにアプリを追加するため</>をクリック→アプリのニックネームを入力→アプリを登録→×で閉じる

npm install firebase@9.21.0 --saveはyarn add firebaseで実施済みなので不要

FIREBASE_CONFIGURATION(firebaseConfig)の値はプロジェクトの概要→プロジェクトの設定で下のほうにapiKeyとprojectIdがあるので、上で作ったFrontEnd.vueの所定の位置に貼り付けてください。
プログラムを実行
yarn dev

プログラムを実行して何か投稿するとデータが書き込まれます。
Cloud Firestoreのコンソールを再読み込みするとデータが追加されているのがわかります。
左側の列にusers
中央のドキュメントの列に自動で割り振られたID(例9KoLd0uh1shgezP97mDw)
右のフィールドに書き込んだ項目と日付が入ります。(例article_body”投稿テスト”article_heading”管理人”pub_date”2023/6/7 16:39:54″)

Googleアナリスティクスについては、こちらでどんなものか見てください。私は、このブログ解析で使っています。アクセス数、見るぐらいですが。そのくらいならfirestoreの使用状況見れば済むと思います。

目次へ

セキュリティ

今回作ったものはWebにアクセスした人に以下の*の部分が検証ツールで見えてしまいます。最初全部書いてたけど必要なものがどれか実験していったらapiKeyとprojectIdの2つだけあれば動きました
Google BardにapiKeyとprojectId公開してもいいか聞いたら。apikeyはOKだけど、projectIDはダメと言われました。生成系AIの出す答えは例え自分のところ(Google)のことに関する質問でも答えはあてにならないと私は思っているので、あくまで参考程度で聞きました。projectIdに関してはドメインの一部になってるので隠しても、しょうがないじゃんと考えています。apiKeyに関してもネットで調べた感じでは大丈夫そうです。公式のFirebase の API キーの使用と管理について学ぶにもいろいろ書いてありますが、使っていかないと理解できない感じです。この辺のセキュリティに関してはケースバイケースなので十分注意して自己責任でやってください。

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "**********",
  authDomain: "******",
  projectId: "*****",
  storageBucket: "*****",
  messagingSenderId: "*****",
  appId: "****"
};

読み書きルールの設定

データベースへの読み書き許可のコントロールが簡単にできるようです。Firebase Authenticationと組み合わせて実験したほうがいいですが今回は単純に読み書きできなくすることだけ試します。
データをセキュリティで保護するを実験してみる。
以下のようにするとデータベースで読み書きできなくなります。

最初に書いてある以下はデータベースを作成するときテストモードを選んだので1か月後7/3までは誰でも読み書きできるという設定です。
allow read, write: if request.time < timestamp.date(2023, 7, 3);

もとに戻して公開をクリックすると誰でも読み書きできるようになります。
目次へ

Hosting

Firebase Hosting を使ってみるにしたがってHosting(デプロイ)します。これをすると一般にアプリが公開されます。いろいろ心配な方は、ローカルだけでやって、ここはやらないほうがいいかもしれません。

Firebase CLI はインストール済です。
プロジェクトを初期化する
firebase init hosting
please select an options:で
use an existing projectを選択してデプロイするプロジェクトを選択
what do you want to use as your public directory?にはビルドしてできたファイルがあるディレクトリ、vue(Vuetify)の場合distと入力してEnter
? Configure as a single-page app (rewrite all urls to /index.html)? y/Nはy(urlにアクセスしたらindex.htmlを読みに行くようになる)
? Set up automatic builds and deploys with GitHub? (y/N)N
? File dist/index.html already exists. Overwrite? No
(ここをyesにするとデプロイ後のurlにアクセスしたときWelcome Firebase Hosting Setup Completeと表示される。この場合もう一度ビルドしなおせばいい)
ビルドする
yarn build
そうするとdistディレクトリにデプロイするファイル一式が出来上がる。
サイトにデプロイする
firebase deploy --only hosting
Hosting URL: (私の場合)https://vue-chat-db50b.web.appとでるので、そこにいけば表示されます。

あとがき

料金等の情報に変化があったら更新します。認証機能を試した記事は下にあります。

イチゲをOFUSEで応援する(御質問でもOKです)Vプリカでのお支払いがおすすめです。
MENTAやってます(ichige)
目次へ

コメント

タイトルとURLをコピーしました