为什么 'user' 在 onAuthStateChanged (Firebase 9 + Vue 3 + Pinia) 中总是 'null'?
Why 'user' is always 'null' in onAuthStateChanged (Firebase 9 + Vue 3 + Pinia)?
我对 Firebase 的 onAuthStateChanged
有问题,无论我尝试了什么,在页面重新加载后总是 returns user
作为 null
。要求的行为是在页面重新加载后自动登录用户。
那是我第一次使用模块化版本的 Firebase 9 和 Pinia 作为商店。
我已经尝试使用 setPersistence
(本地),但结果是一样的。
我卡了一整天,不知道为什么它不起作用。
如果能指出我遗漏的内容,我将不胜感激。
我在 package.json
中使用的软件包:
"dependencies": {
"firebase": "^9.6.11",
"pinia": "^2.0.13",
"vue": "^3.2.13",
"vue-router": "^4.0.3"
}
main.ts
文件:
import { createApp, App as VueApp } from 'vue';
import { createPinia } from 'pinia';
import { auth } from '@/firebase';
import { onAuthStateChanged, User } from 'firebase/auth';
import { useAuthStore } from '@/store/auth';
let app: VueApp;
function initializeApp(user: User | null) {
app = createApp(App)
.use(createPinia())
.use(router);
if (user) {
// PROMEM IS THERE -> user IS ALWAYS null
// I want to set logged user before app is mounted
const authStore = useAuthStore();
authStore.handleAuthStateChange(user);
}
app.mount('#app');
}
onAuthStateChanged(
auth,
(user: User | null) => {
// PROMEM IS THERE -> user IS ALWAYS null
if (!app) {
initializeApp(user);
} else {
const authStore = useAuthStore();
authStore.handleAuthStateChange(user);
}
},
(error: Error) => {
log.error(`Main AuthStateChange handler failed with error`, error);
},
);
firebase.ts
文件:
import { FirebaseApp, initializeApp } from 'firebase/app';
import { Auth, initializeAuth, debugErrorMap } from 'firebase/auth';
import { firebaseConfig } from '@/config';
export const app: FirebaseApp = initializeApp(firebaseConfig);
export const auth: Auth = initializeAuth(app, { errorMap: debugErrorMap });
auth.ts
-> 存储文件:
import { defineStore } from 'pinia';
import { LoginCredentials, SignUpCredentials } from '@/types/auth';
import { FirebaseAuthenticationService, AuthenticationService } from '@/service/AuthenticationService';
import { auth as firebaseAuth } from '@/firebase';
import { log } from '@/service/LoggerService';
export interface AuthStoreUser {
uid: string,
email: string | null
}
export type MaybeAuthStoreUser = AuthStoreUser | null;
export interface AuthStoreState {
user: AuthStoreUser | null,
}
export const authStoreFactory = ($auth: AuthenticationService) => defineStore('auth', {
state: () => ({
user: null,
} as AuthStoreState),
getters: {
isUserLoggedIn(): boolean {
return !!this.user;
},
},
actions: {
async signUpUser(credentials: SignUpCredentials) {
const createdUser = await $auth.createUserWithEmailAndPassword(credentials);
},
async loginUser(credentials: LoginCredentials) {
const user = await $auth.signInWithEmailAndPassword(credentials);
},
async setCurrentUser(user: AuthStoreUser) {
this.user = user;
},
async clearCurrentUser() {
this.user = null;
},
async logoutUser() {
await $auth.signOut();
},
async sendPasswordResetEmail(email: string) {
await $auth.sendPasswordResetEmail(email);
},
async handleAuthStateChange(user: MaybeAuthStoreUser) {
if (user) {
log.debug(`Logging in user from authStateChange handler`);
this.setCurrentUser(user);
} else {
log.debug(`AuthStateChange handler did not receive current user.`);
this.clearCurrentUser();
}
},
},
});
export const useAuthStore = () => {
const $auth = new FirebaseAuthenticationService(firebaseAuth);
return authStoreFactory($auth)();
};
我终于通过在 initializeAuth
中添加 persistence
选项解决了这个问题。这部分的 documentation 具有误导性,并且没有正确解释应该如何使用 initializeAuth
方法完成...
这是 auth
在 firebase.ts
文件中的初始化方式:
import { firebaseConfig } from '@/config';
import { FirebaseApp, initializeApp } from 'firebase/app';
import {
Auth,
initializeAuth,
debugErrorMap,
indexedDBLocalPersistence,
browserLocalPersistence } from 'firebase/auth';
export const app: FirebaseApp = initializeApp(firebaseConfig);
export const auth: Auth = initializeAuth(app, {
persistence: [indexedDBLocalPersistence, browserLocalPersistence],
errorMap: debugErrorMap
});
我在 docs 中发现的内容很有趣:
The default for web browser and React Native apps is local (provided the browser supports this storage mechanism, eg. 3rd party cookies/data are enabled) whereas it is none for Node.js backend apps.
这是我在 auth-public.d.ts
文件中找到的:
export declare interface Dependencies {
/**
* Which {@link Persistence} to use. If this is an array, the first
* `Persistence` that the device supports is used. The SDK searches for an
* existing account in order and, if one is found in a secondary
* `Persistence`, the account is moved to the primary `Persistence`.
*
* If no persistence is provided, the SDK falls back on
* {@link inMemoryPersistence}.
*/
persistence?: Persistence | Persistence[];
// other stuff
}
If no persistence is provided, the SDK falls back on inMemoryPersistence
而inMemoryPersistence
意味着NONE
坚持。这就是我的问题的原因。
我对 Firebase 的 onAuthStateChanged
有问题,无论我尝试了什么,在页面重新加载后总是 returns user
作为 null
。要求的行为是在页面重新加载后自动登录用户。
那是我第一次使用模块化版本的 Firebase 9 和 Pinia 作为商店。
我已经尝试使用 setPersistence
(本地),但结果是一样的。
我卡了一整天,不知道为什么它不起作用。
如果能指出我遗漏的内容,我将不胜感激。
我在 package.json
中使用的软件包:
"dependencies": {
"firebase": "^9.6.11",
"pinia": "^2.0.13",
"vue": "^3.2.13",
"vue-router": "^4.0.3"
}
main.ts
文件:
import { createApp, App as VueApp } from 'vue';
import { createPinia } from 'pinia';
import { auth } from '@/firebase';
import { onAuthStateChanged, User } from 'firebase/auth';
import { useAuthStore } from '@/store/auth';
let app: VueApp;
function initializeApp(user: User | null) {
app = createApp(App)
.use(createPinia())
.use(router);
if (user) {
// PROMEM IS THERE -> user IS ALWAYS null
// I want to set logged user before app is mounted
const authStore = useAuthStore();
authStore.handleAuthStateChange(user);
}
app.mount('#app');
}
onAuthStateChanged(
auth,
(user: User | null) => {
// PROMEM IS THERE -> user IS ALWAYS null
if (!app) {
initializeApp(user);
} else {
const authStore = useAuthStore();
authStore.handleAuthStateChange(user);
}
},
(error: Error) => {
log.error(`Main AuthStateChange handler failed with error`, error);
},
);
firebase.ts
文件:
import { FirebaseApp, initializeApp } from 'firebase/app';
import { Auth, initializeAuth, debugErrorMap } from 'firebase/auth';
import { firebaseConfig } from '@/config';
export const app: FirebaseApp = initializeApp(firebaseConfig);
export const auth: Auth = initializeAuth(app, { errorMap: debugErrorMap });
auth.ts
-> 存储文件:
import { defineStore } from 'pinia';
import { LoginCredentials, SignUpCredentials } from '@/types/auth';
import { FirebaseAuthenticationService, AuthenticationService } from '@/service/AuthenticationService';
import { auth as firebaseAuth } from '@/firebase';
import { log } from '@/service/LoggerService';
export interface AuthStoreUser {
uid: string,
email: string | null
}
export type MaybeAuthStoreUser = AuthStoreUser | null;
export interface AuthStoreState {
user: AuthStoreUser | null,
}
export const authStoreFactory = ($auth: AuthenticationService) => defineStore('auth', {
state: () => ({
user: null,
} as AuthStoreState),
getters: {
isUserLoggedIn(): boolean {
return !!this.user;
},
},
actions: {
async signUpUser(credentials: SignUpCredentials) {
const createdUser = await $auth.createUserWithEmailAndPassword(credentials);
},
async loginUser(credentials: LoginCredentials) {
const user = await $auth.signInWithEmailAndPassword(credentials);
},
async setCurrentUser(user: AuthStoreUser) {
this.user = user;
},
async clearCurrentUser() {
this.user = null;
},
async logoutUser() {
await $auth.signOut();
},
async sendPasswordResetEmail(email: string) {
await $auth.sendPasswordResetEmail(email);
},
async handleAuthStateChange(user: MaybeAuthStoreUser) {
if (user) {
log.debug(`Logging in user from authStateChange handler`);
this.setCurrentUser(user);
} else {
log.debug(`AuthStateChange handler did not receive current user.`);
this.clearCurrentUser();
}
},
},
});
export const useAuthStore = () => {
const $auth = new FirebaseAuthenticationService(firebaseAuth);
return authStoreFactory($auth)();
};
我终于通过在 initializeAuth
中添加 persistence
选项解决了这个问题。这部分的 documentation 具有误导性,并且没有正确解释应该如何使用 initializeAuth
方法完成...
这是 auth
在 firebase.ts
文件中的初始化方式:
import { firebaseConfig } from '@/config';
import { FirebaseApp, initializeApp } from 'firebase/app';
import {
Auth,
initializeAuth,
debugErrorMap,
indexedDBLocalPersistence,
browserLocalPersistence } from 'firebase/auth';
export const app: FirebaseApp = initializeApp(firebaseConfig);
export const auth: Auth = initializeAuth(app, {
persistence: [indexedDBLocalPersistence, browserLocalPersistence],
errorMap: debugErrorMap
});
我在 docs 中发现的内容很有趣:
The default for web browser and React Native apps is local (provided the browser supports this storage mechanism, eg. 3rd party cookies/data are enabled) whereas it is none for Node.js backend apps.
这是我在 auth-public.d.ts
文件中找到的:
export declare interface Dependencies {
/**
* Which {@link Persistence} to use. If this is an array, the first
* `Persistence` that the device supports is used. The SDK searches for an
* existing account in order and, if one is found in a secondary
* `Persistence`, the account is moved to the primary `Persistence`.
*
* If no persistence is provided, the SDK falls back on
* {@link inMemoryPersistence}.
*/
persistence?: Persistence | Persistence[];
// other stuff
}
If no persistence is provided, the SDK falls back on inMemoryPersistence
而inMemoryPersistence
意味着NONE
坚持。这就是我的问题的原因。