# Firebase+Vue.js 覚書

# まえがき

"テイキョウドットコム"の裏側のCMSをFirebaseで作った話. しばらくフレームワークとしてNuxt.jsを使っていたものの,lang="ts"にしようとしていろいろ躓いたりして面倒だったので,流行り(?)の@vue/cliに全部任せることにした.もうSSRはいいかな.

でもクライアントもサーバ(Firebase Functions)もTypeScriptで書けたらいいじゃん.良いじゃん(断定)

そのうちNuxt.js+TypeScriptちゃんと研究しよ. Nuxt.jsすき.

# Firebase

まずfirebase-toolsで

~/ $ firebase init

今回はHosting, Functions, Firestore, Authenticationを使う

ディレクトリ構造はこう

~/
|- functions/   --- Functionsのコード
|  |- src/         --- .tsファイル群
|  |- lib/         --- コンパイル済み.jsファイル群
|  |- package.json
|     ...
|- src/         --- クライアント側のコード
|  |- common/
|  |- site_name/     --- vue cliで作るディレクトリ
|  |  |- package.json
|  |     ...
|  `- site_name_2/   --- vue cliで作るディレクトリ
|     |- package.json
|        ...
|- public/      --- クライアント側ビルド済みファイル
|  |- site_name/
|  `- site_name_2/
|- .firebaserc
|- firebase.json
|- package.json
   ...

# Hosting

表側と裏側の2サイトを用意するので従量プランにアップグレード. その上で"複数のサイトでプロジェクトのリソースを共有する"の通りに,Hostingサイトと~/public/site_nameを関連付ける. また,SPAの場合すべてのURLリクエストをindex.htmlに飛ばす必要がある."Hosting 動作をカスタマイズする"の"リライト"の項など参考に各target毎に設定を行う. こんな感じ.

  • ~/firebase.json
      "hosting": [ {
        "target": "target_name",
        "public": "public/site_name",
        "rewrites": [ {
          "source": "**",
          "destination": "/index.html"
        } ]
      } ],
    

vue cliの項に続く.

# Authentication

方式はとりあえずメール+パスワード認証にした.あとでGoogleアカウントにしよ. Firebase Consoleからユーザを作成(CMSを利用するのは限定人数なので) 実際に使えるメールアドレスと,仮の適当なパスワードで作る. パスワード再設定用メール送信で実際のパスワードを設定させると同時にメール認証が完了する.

# Firestore

Firebase Consoleから作成しておく.ロックモードでよい. Firestoreのreadは全員許可,writeを前項で作ったユーザのみに限定する. ルールを以下のようにする.uidはユーザを作った時に発行されるやつ. 複数許可したり,emailで絞ることもできる.詳しくはドキュメント参照されたし.

  • ~/firestore.rules
    ...
    allow read;
    allow write:
      if request.auth.token.email_verified
      && request.auth.uid in [
        'XXXXXXXXXXXXXXXXXXXXXXXXXXXX',
        'YYYYYYYYYYYYYYYYYYYYYYYYYYYY'
      ];
    ...
    

複数fieldを使ったソートのクエリを使う必要があったので,indexを作成しておく.

# Functions

functionいっぱい作ってREST APIみたいにするとそれぞれのfunctionの起動に時間がかかるっぽいので、1つのfunctionにexpressを載せる構成にした。 expressのようなFunctionsだけの依存ライブラリは~/functions/package.jsonにインストールしよう.

# Vue

~/src $ vue create site_name

TypeScriptを使う設定にした.その他お好み. これをサイト数ぶん作る.

pugを使う場合は追加で

~/src/site_name $ npm i -D pug pug-plain-loader

Firebase Consoleのoverviewにある"アプリにFirebaseを追加して利用を開始しましょう"から,initializaAppするために必要な値を確認し,.envを以下のように設定. .envの仕様の詳細はドキュメント参照されたし

  • ~/src/site_name/.env.production

    VUE_APP_FIREBASE_FUNCTIONS_URL=https://region-name-project-id.cloudfunctions.net/
    
    VUE_APP_FIREBASE_API_KEY=xxx
    VUE_APP_FIREBASE_AUTH_DOMAIN=project-id.firebaseapp.com
    VUE_APP_FIREBASE_DATABASE_URL=https://project-id.firebaseio.com
    VUE_APP_FIREBASE_PROJECT_ID=project-id
    VUE_APP_FIREBASE_STORAGE_BUCKET=project-id.appspot.com
    VUE_APP_FIREBASE_MESSAGING_SENDER_ID=xxx
    ...
    
  • ~/src/site_name/.env.development

    VUE_APP_FIREBASE_FUNCTIONS_URL=http://localhost:5001/project-id/us-central1/
    
    おなじ
    ...
    

$ firebase serveでローカルで動かすとき,Firestoreだけはローカルで立たないので,.env.developmentでは予め用意したstaging用のFirebaseプロジェクトを使うようにすると◎.(その場合Firestoreだけ使うのでstaging用プロジェクトの料金プランアップグレードは不要)

.envで設定したもののうちVUE_APP_で始まるものは以下のようにクライアントサイドのコードで呼び出せるのでそうなっている.

  • process.env.VUE_APP_XXX
  • <%= VUE_APP_XXX %> (~/src/site_name/public/index.htmlなど)

headのmetaでcanonical urlが欲しいときはVUE_APP_PUBLIC_PATHなど設定しておくと◎.

そして最後にこう

  • firebase.ts

    const config = {
      apiKey: process.env.VUE_APP_FIREBASE_API_KEY,
      authDomain: process.env.VUE_APP_FIREBASE_AUTH_DOMAIN,
      databaseURL: process.env.VUE_APP_FIREBASE_DATABASE_URL,
      projectId: process.env.VUE_APP_FIREBASE_PROJECT_ID,
      storageBucket: process.env.VUE_APP_FIREBASE_STORAGE_BUCKET,
      messagingSenderId: process.env.VUE_APP_FIREBASE_MESSAGING_SENDER_ID,
    };
    
    firebase.initializeApp(config);
    
  • axios.ts

    import axios from 'axios';
    
    export default axios.create({
      baseURL: process.env.VUE_APP_FIREBASE_FUNCTIONS_URL,
    });
    

# Build, Serve, Deploy

npm scriptだけ列挙する

  • ~/functions/package.json

        "build": "tsc",
        "watch": "tsc -w",
    
  • ~/src/site_name/package.json

        "build": "vue-cli-service build --dest=../../public/site_name --mode=development",
        "watch": "vue-cli-service build --dest=../../public/site_name --mode=development --watch",
        "prod": "vue-cli-service build --dest=../../public/site_name --mode=production",
    
  • ~/package.json

        "build": "run-s build:*",
        "build:functions": "npm --prefix=functions run build",
        "build:site_name": "npm --prefix=src/site_name run build",
        "build:site_name_2": "npm --prefix=src/site_name_2 run build",
        "serve": "firebase serve --project project-id-staging",
        "prod:site_name": "npm --prefix=src/site_name run prod",
        "prod:site_name_2": "npm --prefix=src/site_name_2 run prod",
        "predeploy": "run-s clean prod:*",
        "deploy": "firebase deploy --project project-id",
        "clean": "rm -r public || true",
    

開発中はnpm run watchを各package.jsonの位置で実行する.またはルートでnpm run build. その後ルートでnpm run serveでFirebase Hostingがローカルで動く. npm run deployで全部productionビルドしてデプロイする.Functionsはfirebase-toolsが勝手にビルドします. npm run deploy -- --only functionsとかも可能.詳しくはfirebase-toolsのドキュメント参照されたし

run-snpm-run-allパッケージ

# 蛇足

複数サイト間やVue・Functions間でinterfaceの定義を共有したかったりする. tsconfig.jsonのpathsで"@common/*": ["../../comon/*"]とするか, シンボリックリンクln -s ../../common ./commonとするか, どっちがいいんだろうか?

どちらにせよ実体がtsconfig.jsonやtslint.json/eslint.jsonの外側ディレクトリにあるので, lintはかからないしwatchしてくれない・・・?

# VSCode

プロジェクトルートから見てtsconfigが複数あるので,paths解決やインテリセンス等々を上手にやってくれない. tsconfigの位置毎にVSCodeを複窓して書いてるけどめんどくさいぞ! いい方法教えてください.

# type safeなstore

Vuex+TypeScript問題に足突っ込む

Last Updated: 5/13/2020, 12:57:47 PM