1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【2024年版】ViteでVue+TypeScriptでFirebase Authentication をローカルエミュレートするまで

Last updated at Posted at 2024-02-28

環境

ツール version 前提
macOS Ventura 13.4.1
VSCode 1.86.2 Plugin は Volar + TypeScript Vue Plugin (Volar) -> Vue - Official v2.0.4 1
Vetur は使わない。Yarn Pnp対応
node v20.11.1 nodebrew で管理
yarn v4.1.0 corepack のを使う。 PnPモードで使う
Firebase CLI
(firebase-tools)
13.4.0 プロジェクト毎のインストール推奨
firebase v10.8.0 モジュラー API

【2024-04-05 追記】
パワーアップ版の記事を書きました。

0. 前提(すっ飛ばしても良い)

nodebrew

インストール

アップグレード(するなら)

% nodebrew selfupdate

node

nodebrew で入れます。

% nodebrew ls-remote
% nodebrew install v20.11.1
% nodebrew use v20.11.1

最新のnodeを使うなら nodebrew install latest

yarn

node同梱のcorepackをOnにすればそのまま使える(のでインストールが不要になった)。

Onにする
% corepack enable

何らかのために止めるなら corepack disable

グローバルのyarnを最新化
% corepack prepare yarn@stable --activate

デフォルトでの yarn のバージョンは 1.22.21。依存関係的に問題なければ v4を使う。
yarn はプロジェクト毎に package.json でどのバージョンを使うかを指定して使う。

viteを使いたい時など、プロジェクトに依存しない yarn は上記コマンドで指定しておく。
(設定は macOS では ~/.cache/node/corepack/lastKnownGood.json に残る)

ちなみプロジェクト毎の yarn を最新にする場合は以下のコマンド。このコマンドはカレントディレクトリ直下に以下のような package.json を作るので注意。コマンドを叩いた場所がプロジェクトのディレクトリでないならば そのままにしてはいけない

プロジェクトのyarnを最新化
% yarn set version stable
package.json
{
  "packageManager": "yarn@4.1.0"
}

例えば package.json が作られたカレントディレクトリがプロジェクトを置く用のディレクトリ(例: /Users/yusuke/Workspaces)だったとして、その配下に vite なりで新たにプロジェクトのリポジトリ(/Users/yusuke/Workspaces/MyProject)を作ったとする。

MyProject に cd で移動し yarn すると、以下のようなエラーが出る。

 Usage Error: The nearest package directory (/Users/yusuke/Workspaces/MyProject) doesn't seem to be part of the project declared in /Users/yusuke/Workspaces.

- If /Users/yusuke/Workspaces isn't intended to be a project, remove any yarn.lock and/or package.json file there.
- If /Users/yusuke/Workspaces is intended to be a project, it might be that you forgot to list MyProject in its workspace configuration.
- Finally, if /Users/yusuke/Workspaces is fine and you intend MyProject to be treated as a completely separate project (not even a workspace), create an empty yarn.lock file in it.

MyProject が何らかの親プロジェクトのサブモジュールなら、(親プロジェクトの) package.json に workspace設定を追加しなさい、カレントディレクトリが単なるプロジェクト置き場ならば package.json は削除しなさいとのこと。

関連するエラー

npm や homebrew で入れていたグローバルな yarn v1 がいると下記のようなエラーも出るので消しておくこと。

error This project's package.json defines "packageManager": "yarn@4.1.0". However the current global version of Yarn is 1.22.21.

Presence of the "packageManager" field indicates that the project is meant to be used with Corepack, a tool included by default with all official Node.js distributions starting from 16.9 and 14.19.
Corepack must currently be enabled by running corepack enable in your terminal. For more information, check out https://yarnpkg.com/corepack.
npmで入れていた場合
% npm uninstall -g yarn
homebrewで入れていた場合
% brew uninstall yarn

1. Viteによるプロジェクトの作成

方法1

viteを使って。ここでの プロジェクト名は MyPdojectとしていますがお好きに。

% yarn create vite
➤ YN0000: · Yarn 4.1.0
➤ YN0000: ┌ Resolution step
➤ YN0085: │ + create-vite@npm:5.2.1
➤ YN0000: └ Completed
➤ YN0000: ┌ Fetch step
➤ YN0013: │ A package was added to the project (+ 250.6 KiB).
➤ YN0000: └ Completed
➤ YN0000: ┌ Link step
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0000: └ Completed
➤ YN0000: · Done with warnings in 0s 144ms

✔ Project name: … MyPdoject
? Select a framework: › - Use arrow-keys. Return to submit.
    Vanilla
❯   Vue
    React
    Preact
    Lit
    Svelte
    Solid
    Qwik
    Others
? Select a variant: › - Use arrow-keys. Return to submit.
❯   TypeScript
    JavaScript
    Customize with create-vue ↗
    Nuxt ↗

Scaffolding project in /Users/yusuke/Workspaces/MyPdoject...

Done. Now run:

  cd MyPdoject
  yarn
  yarn dev

yarnを最新化して依存ライブラリをインストール

% cd MyPdoject
% yarn set version stable
% yarn

方法2

テンプレートを使う場合。

% yarn create vite MyProject --template vue-ts
➤ YN0000: · Yarn 4.1.0
➤ YN0000: ┌ Resolution step
➤ YN0085: │ + create-vite@npm:5.2.1
➤ YN0000: └ Completed
➤ YN0000: ┌ Fetch step
➤ YN0013: │ A package was added to the project (+ 250.6 KiB).
➤ YN0000: └ Completed
➤ YN0000: ┌ Link step
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0000: └ Completed
➤ YN0000: · Done with warnings in 0s 66ms


Scaffolding project in /Users/yusuke/Workspace/MyProject...

Done. Now run:

  cd MyProject
  yarn
  yarn dev

方式 1 or 2 でプロジェクトを生成したら、vite.config.ts を開いてみる。

capture-vite-config-ts-error.png

エラーが出ている場合には「Yarn の PnPモード に対応する」へ。

Yarn の PnPモード に対応する

Yarn は v2 以降、node_modules 以下にライブラリをダウンロードしなくなった。
これを Plyg and Play の略で、pnpモードという。
VSCode は node_modules 以下にライブラリを探しに行くので、ないよ、となっている。

VSCode を pnpモードに対応させる

  1. VSCodeの拡張機能として ZipFS をインストール(詳細は割愛)
  2. SDKパッケージをダウンロードして実行
  3. VSCodeが使うTypeScriptバージョンをSDKのものに指定
SDKパッケージをダウンロードして実行
% yarn dlx @yarnpkg/sdks vscode

これを行うと、.vscode 以下に settings.json ができる。

settings.json
{
  "search.exclude": {
    "**/.yarn": true,
    "**/.pnp.*": true
  },
  "typescript.tsdk": ".yarn/sdks/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true
}

最後にVSCodeが使うTypeScriptバージョンをSDKのものに指定するには tsファイル上で、Shift + Commnd + pを押し、「Select TypeScript Version」を選択。

capture-shift-cmd-p.png

ワークスペースのバージョン(5.x.x-sdk となっているもの)を選択。

capture-sdks-typescript.png

これでOK。あとは以下を .gitignore するべきとのこと。

.gitignore
+ # yarn
+ .pnp.*
+ .yarn/*
+ !.yarn/patches
+ !.yarn/plugins
+ !.yarn/releases
+ !.yarn/sdks
+ !.yarn/versions

ちなみに v1 互換である node-modulesモードにすることもできて、プロジェクトのルートに .yarnrc.yml` を作って以下のようにすればよい。

.yarnrc.yml
nodeLinker: node-modules

dev serverを起動して動作確認

% yarn dev

2. Firebaseの導入

ローカルのエミュレータでテストができるを目指します。

ローカルマシンに Firebase CLI をインストール

Cloud Functionsをローカルでエミュレートしたい人は注意

インストール方法がいくつかありますが、自動インストールスクリプトでスタンドアロンバイナリをインストールするのでなく、yarnでプロジェクト毎にインストールするのがオススメです。

Cloud Functionsをエミュレータで使う場合、スタンドアロンのFirebase CLIを使っていると、バイナリに同梱されているnodeのバージョン(現状は 18)を指定しなければいけません。
例えば 20 を使いたいならば、yarnでローカルインストールが良いです。

yarnでプロジェクト毎にインストール(推奨)

ローカルインストール
% yarn add --dev firebase-tools
ログイン
% yarn firebase login

自動インストールスクリプトでインストール(するなら)

ちなみに シェルの中 に使い方が書いてあります。

自動インストールスクリプトによるインストール
% curl -sL https://firebase.tools | bash
更新する場合
% curl -sL https://firebase.tools | upgrade=true bash
アンインストールする場合
% curl -sL https://firebase.tools | uninstall=true bash

プロジェクトにFirebase設定を追加(ここではAuthのエミュレータ設定だけ)

エミュレータが動けば Firebaseコンソールでのプロジェクトはいらないやと思ったのだけど、それだとエミュレータが起動しなかったのでこれの後でプロジェクトも作っています。

% yarn firebase init
     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /Users/yusuke/Workspaces/robustive-ts-boilerplate

? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, 
<i> to invert selection, and <enter> to proceed)
 ◯ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
 ◯ Firestore: Configure security rules and indexes files for Firestore
 ◯ Functions: Configure a Cloud Functions directory and its files
 ◯ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
 ◯ Hosting: Set up GitHub Action deploys
 ◯ Storage: Configure a security rules file for Cloud Storage
❯◉ Emulators: Set up local emulators for Firebase products
 ◯ Remote Config: Configure a template file for Remote Config
 ◯ Extensions: Set up an empty Extensions manifest
(Move up and down to reveal more choices)

=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add, 
but for now we'll just set up a default project.

? Please select an option: 
  Use an existing project 
  Create a new project 
  Add Firebase to an existing Google Cloud Platform project 
❯ Don't set up a default project 

=== Emulators Setup
? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert 
selection, and <enter> to proceed)
❯◉ Authentication Emulator
 ◯ Functions Emulator
 ◯ Firestore Emulator
 ◯ Database Emulator
 ◯ Hosting Emulator
 ◯ Pub/Sub Emulator
 ◯ Storage Emulator
 ◯ Eventarc Emulator
(Move up and down to reveal more choices)


? Which port do you want to use for the auth emulator? (9099) 

? Would you like to enable the Emulator UI? (Y/n) 

? Which port do you want to use for the Emulator UI (leave empty to use any available port)? 

? Would you like to download the emulators now? (Y/n) 

i  ui: downloading ui-v1.11.7.zip...

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!
Progress: =======>------------------------------------------------------------------------------------------------------------------------------------------------------- (5% of 4MB

プロジェクトルートに以下のファイルができる。

firebase.json
{
  "emulators": {
    "auth": {
      "port": 9099
    },
    "ui": {
      "enabled": true
    },
    "singleProjectMode": true
  }
}

本当にエミュレータのセットアップだけを後から追加で行うなら、以下のコマンド。

% yarn firebase init emulators

Firebaseプロジェクトを作る

Project ID はグローバルでユニークである必要があるので誰かと被るとErrorになる。。

% yarn firebase init emulators
     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /Users/yusuke/Workspaces/MyProject

Before we get started, keep in mind:

  * You are initializing within an existing Firebase project directory


=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add, 
but for now we'll just set up a default project.

? Please select an option: 
  Use an existing project 
❯ Create a new project 
  Add Firebase to an existing Google Cloud Platform project 
  Don't set up a default project 

i  If you want to create a project in a Google Cloud organization or folder, please use "firebase projects:create" instead, and return to this command when you've created the project.
? Please specify a unique project id (warning: cannot be modified afterward) [6-30 characters]:
 myproject-4649
? What would you like to call your project? (defaults to your project ID) MyProject
✔ Creating Google Cloud Platform project
✔ Adding Firebase resources to Google Cloud Platform project

🎉🎉🎉 Your Firebase project is ready! 🎉🎉🎉

Project information:
   - Project ID: myproject-4649
   - Project Name: MyProject

Firebase console is available at
https://console.firebase.google.com/project/myproject-4649/overview
i  Using project myproject-4649 (MyProject)

fireabseプロジェクトの情報は、プロジェクトルートの以下のファイルに書かれる(のでオープンソースなどで公開する場合は .firebaserc.gitignore に追加すること)。

.firebaserc
{
  "projects": {
    "default": "myproject-4649"
  }
}

Firebaseコンソールにて、ウェブアプリ </> を追加し、configファイルの内容などを取得する(詳細は割愛)。

取得した内容は firebase.json などのファイルにして import して使うか、以下のように vite.config.ts にて define で定義し、グルーバル変数とするでも良い。

vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
+ define: {
+   __FIREBASE_CONFIG__: {
+     apiKey: "AIzaSyAv9VnvR6YK4ljBjPe7UZjnBV9N4h0c7qE",
+     authDomain: "myproject-4649.firebaseapp.com",
+     projectId: "myproject-4649",
+     storageBucket: "myproject-4649.appspot.com",
+     messagingSenderId: "539906050176",
+     appId: "1:539906050176:web:24e2561daf2e7c4b785973"
+   }
  }
})

その場合、type を declare してあげないとErrorになるので vite-env.d.ts にて型を宣言する。

vite-env.d.ts
/// <reference types="vite/client" />

+ declare var __FIREBASE_CONFIG__: { [x:string]: string }

エミュレータを起動するようにpackage.jsonのscriptsを修正

ちなみにエミュレータはデータを保存(永続化)してくれない。なので終了時にデータをエクスポートし、起動時にインポートする設定も入れる。

package.json
  "scripts": {
-   "dev": "vite",
+   "dev": "firebase emulators:start --import .emulator-data --export-on-exit .emulator-data & vite",

起動すると firebase-debug.log および ui-debug.log ができるので .gitignore に追加する。エミュレータのデータも追加。

.gitignore
+ # firebase emulator
+ *-debug.log
+ .emulator-data

Firebaseの依存関係を追加

% yarn add firebase

ソースコードにFirebaseの接続設定

main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

+ import { initializeApp } from 'firebase/app'

+ // Initialize Firebase
+ const firebaseApp = initializeApp(__FIREBASE_CONFIG__)

createApp(App).mount('#app')

ローカルではエミュレータに接続するようにする

viteには 環境変数 import.meta.env が用意されていて、dotenv も使える。

yarn dev で起動した場合、import.meta.env.MODEdevelopment になるので、これで場合分けを行う。

ちなみに import.meta.env が 'ImportMeta' に存在しないと怒られるので、vite-env.d.ts に以下を追記する。

vite-env.d.ts
/// <reference types="vite/client" />

declare var __FIREBASE_CONFIG__: { [x:string]: string }

+ interface ImportMetaEnv {
+     readonly MODE: string
+ }

+ interface ImportMeta {
+     readonly env: ImportMetaEnv
+ }
main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

import { initializeApp } from 'firebase/app'
+ import { connectAuthEmulator, getAuth } from "firebase/auth"

// initialize firebase
const firebaseApp = initializeApp(__FIREBASE_CONFIG__)

+ // Initialize Firebase Authentication and get a reference to the service
+ if (import.meta.env.MODE === 'development') {
+     const auth = getAuth()
+     connectAuthEmulator(auth, "http://127.0.0.1:9099")
+ } else {
+     getAuth(firebaseApp)
+ }

createApp(App).mount('#app')

GoogleOAuthでのサインイン/サインアウトを実装

ここではアーキテクチャは考えず、初めからある HelloWorld.vue を 魔改造してサインイン/アウトの確認だけを行う。

HelloWorld.vue
<script setup lang="ts">
- import { ref } from 'vue'
+ import { reactive } from 'vue'
+ import { AuthError, getAuth, GoogleAuthProvider, onAuthStateChanged, signInWithPopup, signOut, User, UserCredential } from 'firebase/auth'

defineProps<{ msg: string }>()

- const count = ref(0)
+ const state = reactive<{
+   user: User | null
+ }>({
+   user: null
+ })

+ // サインインステータスを観測
+ onAuthStateChanged(getAuth(), (user: User | null) => {
+     if (user) {
+         console.log("onAuthStateChanged: signIn", user)
+         state.user = user
+     } else {
+         console.log("onAuthStateChanged: signOut")
+         state.user = null
+     }
+ })

+ const requiredScope: string[] = ["https://www.googleapis.com/auth/contacts.readonly"]

+ const signin = () => {
+     const provider = new GoogleAuthProvider()
+     requiredScope.forEach(scope => provider.addScope(scope))
+     return signInWithPopup(getAuth(), provider)
+         .then((result: UserCredential) => {
+             // This gives you a Google Access Token. You can use it to access the Google API.
+             const credential = GoogleAuthProvider.credentialFromResult(result)
+             console.log('credential', credential)
+         })
+         .catch((error: AuthError) => console.error('Google Sign-In Error:', error))
+ }

+ const signout = () => {
+     return signOut(getAuth())
+         .then(() => console.log('signOut'))
+         .catch((error: AuthError) => console.error('Google Sign-Out Error:', error))
+ }
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
-   <button type="button" @click="count++">count is {{ count }}</button>
+   <button v-if="state.user === null" type="button" @click="signin()">Sign In</button>
+   <button v-else type="button" @click="signout()">Sign Out [{{ state.user.email }}] </button>
    <p>
      Edit
      <code>components/HelloWorld.vue</code> to test HMR
    </p>
  </div>

初め、signInWithRedirect で確認していたのだけど、現時点では動かなかった(特にエラーもなくリダイレクトするけど、エミュレータにアカウントが作成されていない)2 3

  1. VolarVue - Offical にリネームされ、TypeScript Vue Plugin (Volar)は不要になった模様(参照

  2. 回答のついていないstackoverflow

  3. 関連ありそうなIssue

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?