日々是好日

プログラミングについてのあれこれ、ムダ知識など

Firebaseの初期化でハマった

Firebaseの初期化でめちゃくちゃハマったのでメモ。

ネット上をさまよったが、どうもFirebaseの新旧のバージョンで書き方が変わったため、かなり情報がごちゃごちゃになってるっぽい。

環境

  • Windows10
  • TypeScript 3.3.3
  • Vue CLI 4.2.2
  • firebase モジュール ^7.12.0

プロジェクトの生成等は次のとおり。

  1. vue create <project>でテンプレート生成
  2. firebase initでVue ProjectにFirebase Hostingを設定
  3. npm i firebaseでfirebaseモジュールをインストール

とまあ、デプロイ用にFirebase Hostingのみ用意した。 一応エディタはVSCode使用です。

何ができなかったのか

  • firebase.auth is not a functionエラーが頻発し匿名認証ができない
  • npm run serveした場合は、認証もとおるしfirebase.storageでファイルアップロードもできる
  • 一方でfirebase serveした場合は、not a functionエラーにより認証ができない

なぜnpm run serveした方が認証もうまく行くのかはいまだに謎だが、 そもそも初期化の仕方がよく分かっていなかった。

何が原因だったか

コード上でfirebase.initializeAppを実行しつつ、HTML上でも<script ~~~ /firebase/init.js ~~~></script>を読み込んでたのがだめだったっぽい。

できればコード上で完結したかったので、init.jsの行は削除した。 ついでに読み込んでたライブラリ群<script ~~~/firebase-auth.js~~~><script ~~~/firebase-storage.js>等も削除した。

どうやって解決したか

上で書いたとおり、コード上で初期化する形に寄せてみた。 なおドキュメントでは<body>タグ内に書き込む方法が推しっぽいが、どうやってもうまくいかないので諦め。

firebase.google.com

コード上でFirebaseを初期化する

Firebaseコンソールでプロジェクトが作成されているものとして、 下記の場所からfirebaseConfigをコピーする。

f:id:kcpoipoi:20200328021818p:plain:w600

次にVueプロジェクトフォルダのsrc配下にFirebaseConfig.ts等適当なtsファイルを作って貼り付ける。 FirebaseConfig.tsは公開しても全然美味しくないので、.gitignoreに追加しておく。

// FirebaseConfig.ts
const conf = { // 構成をペースト }

main.tsで次のように初期化する。

// main.ts
// @はsrcディレクトリのエイリアス
...
import { conf } from '@/firebaseConfig.ts'
import firebase from 'firebase/app'
...

Vue.config.productionTip = false

// Firebaseの構成を読み込んで初期化
firebase.initializeApp(conf)

new Vue({
  router,
  store,
  vuetify,
  render: h => h(App)
}).$mount('#app')

次に、Firebase AuthだったりStorageを使用するVueコンポーネントで、 必要なパッケージをインポートする。

ここでは、適当にExample.vuecomponentsディレクトリ配下に作ってやった。

// Example.vue
// Vuetifyのv-file-inputコンポーネントを使用
// Firebase Storageへのファイルのアップロードを想定
<template>
  <v-app>
    <v-content>
      <v-file-input
        color="primary"
        @change="fileUpload">FileUpload</v-file-input>
    </v-content>
  </v-app>
</template>

<script lang="ts">

// firebase/authやfirebase/storageなど、必要なパッケージをインポート
import Vue from 'vue'
import * as firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/storage'

export default Vue.extend({
  data: () => ({

  }),

  methods: {
    // file-inputのイベントでfileUploadメソッドをコール
    // 引数はany型。仕方ないね(何
    fileUpload: (e: any) => {
      
      // 匿名で認証しStorageにファイルをputする
      firebase.auth().signInAnonymously().then(() => {
        
        // Storageにsampleバケットを作っておき、とりあえずファイル名で参照を生成する
        const sampleRef = firebase.storage().ref().child(`sample/${e.name}`)
        
        // 生成した参照先にファイルをput
        sampleRef.put(e).then(snapshot => {
          // アップロード後の参照先はfullPathプロパティで確認可能
          console.log(snapshot.ref.fullPath)
        }).catch(err => {
          console.error(err)
        })
      })
    }
  }
})
</script>

あとはExample.vueを適切に読み込んでやる(省略)。

ビルドしてfirebase serveで動作確認してアップロードできればおk

所感

どうやってもbodyタグに埋め込む方法がわからない。 /firebase/init.jsで初期化するとか言っててfirebase.initializeApp外すとエラー吐くし……。

まあ実際のところ、今後Firebaseの実装を隠ぺいする(interfaceをかませる)場合にコード上で制御できるほうが都合いいだろうし、今のところ支障はない。 だけど、できないのは気持ち悪いので機会があれば調べてみる。

npm run serve (Vue CLIでのserve)firebase serveとで動作違うのなんなのマジで……。