本番環境でCloud Firestoreを使う場合、セキュリティルールをしっかり設定しないといけないですが、プロトタイプとして誰でも読み書きOKとしてさささっと実装したい場合もあります。
今回は、
- ログイン不要で誰でもデータを読み書きできる
- モジュールバンドラーなしでHTML、CSS、JavaScriptを書く
という方針で、さささっとCloud Firestoreを使ってみようと思います。
DEMO
lighting-switch-prototype.web.app
ボタンを押すと、電気をつけたり消したりすることができます。
電気のON・OFFはCloud Firestoreで管理しているので、すべてのユーザー間でリアルタイムに共有されます。
誰かが電気を消せば、開かれているブラウザ上に表示されている電気がすべて消えるということです。
実装手順
❶ パッケージを用意する
yarn add firebase firebase-tools
❷ package.jsonを編集する
package.json
{ "name": "lighting-switch-prototype", "scripts": { "login": "firebase login --no-localhost", "init": "firebase init", "deploy": "firebase deploy" }, "dependencies": { "firebase": "^10.3.1", "firebase-tools": "^12.5.2" } }
❸ Firebaseのコンソールからプロジェクトをつくり、Cloud Firestoreを有効にする
- ルールはテストモードで開始する
- ロケーションは近いところを選ぶ
❹ Firebaseプロジェクトを初期化する
ログイン
yarn run login
初期化
yarn run init
初期化後、❸で作成したプロジェクトに紐付けます。
必要なものを有効化
◉ Firestore: Configure security rules and indexes files for Firestore ◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys ◉ Emulators: Set up local emulators for Firebase products
必要なエミュレータを有効化
◉ Authentication Emulator ◉ Firestore Emulator ◉ Hosting Emulator
いろいろ選択肢が出てきますが、基本的にEnterを連打でOKです。
❺ コードを書く
public/index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>💡</title> <!-- update the version number as needed --> <script defer src="/__/firebase/10.3.1/firebase-app-compat.js"></script> <!-- include only the Firebase features as you need --> <script defer src="/__/firebase/10.3.1/firebase-auth-compat.js"></script> <script defer src="/__/firebase/10.3.1/firebase-database-compat.js"></script> <script defer src="/__/firebase/10.3.1/firebase-firestore-compat.js"></script> <script defer src="/__/firebase/10.3.1/firebase-functions-compat.js"></script> <script defer src="/__/firebase/10.3.1/firebase-messaging-compat.js"></script> <script defer src="/__/firebase/10.3.1/firebase-storage-compat.js"></script> <script defer src="/__/firebase/10.3.1/firebase-analytics-compat.js"></script> <script defer src="/__/firebase/10.3.1/firebase-remote-config-compat.js"></script> <script defer src="/__/firebase/10.3.1/firebase-performance-compat.js"></script> <!-- initialize the SDK after all desired features are loaded, set useEmulator to false to avoid connecting the SDK to running emulators. --> <script defer src="/__/firebase/init.js?useEmulator=true"></script> <style> * { margin: 0; padding: 0; } div { display: flex; align-items: center; justify-content: center; position: fixed; top: 0; bottom: 0; left: 0; right: 0; } [data-is-on] { font-size: 80px; background: radial-gradient(#FAFAFA, #E0E0E0); } [data-is-on]::before { position: absolute; top: 0; bottom: 0; left: 0; right: 0; content: ''; background: radial-gradient(#424242, #212121); transition: opacity .2s ease-in-out; } [data-is-on='true']::before { opacity: 0; } [data-is-on='true'] dt { opacity: 1; } [data-is-on='true'] #btn-on { display: none; } [data-is-on='false']::before { opacity: 1; } [data-is-on='false'] dt { opacity: .2; } [data-is-on='false'] #btn-off { display: none; } dl { position: relative; } dd { display: flex; align-items: center; justify-content: center; } button { padding: 8px; font-size: 16px; cursor: pointer; } </style> </head> <body> <div data-is-on="false"> <dl> <dt>💡</dt> <dd> <button id="btn-on">ON</button> <button id="btn-off">OFF</button> </dd> </dl> </div> <script> document.addEventListener('DOMContentLoaded', function() { const loadEl = document.querySelector('#load'); // // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥 // // The Firebase SDK is initialized and available here! // // firebase.auth().onAuthStateChanged(user => { }); // firebase.database().ref('/path/to/ref').on('value', snapshot => { }); // firebase.firestore().doc('/foo/bar').get().then(() => { }); // firebase.functions().httpsCallable('yourFunction')().then(() => { }); // firebase.messaging().requestPermission().then(() => { }); // firebase.storage().ref('/path/to/ref').getDownloadURL().then(() => { }); // firebase.analytics(); // call to activate // firebase.analytics().logEvent('tutorial_completed'); // firebase.performance(); // call to activate // // // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥 try { let app = firebase.app(); let features = [ 'auth', 'database', 'firestore', 'functions', 'messaging', 'storage', 'analytics', 'remoteConfig', 'performance', ].filter(feature => typeof app[feature] === 'function'); firebase.auth().signInAnonymously().then(async () => { const db = firebase.firestore(app); const isOnRef = await db.doc('led/isOn'); const isOnDoc = await isOnRef.get(); const isOnElm = document.querySelector('[data-is-on]'); const btnOn = document.getElementById('btn-on'); const btnOff = document.getElementById('btn-off'); isOnRef.onSnapshot((doc) => { if (doc.data()) { isOnElm.dataset.isOn = doc.data().value; } }); btnOn.addEventListener('click', async () => { if (isOnDoc.exists) { await isOnRef.set({ value: true }); } }); btnOff.addEventListener('click', async () => { if (isOnDoc.exists) { await isOnRef.set({ value: false }); } }); }); } catch (e) { console.error(e); } }); </script> </body> </html>
❻ エミュレータで確認する
yarn dev
❼ デプロイする
yarn deploy
こんな感じです!