【发布时间】:2021-11-19 05:20:24
【问题描述】:
问题
Nuxt 中间件下面
const inspectAuthentication: Middleware = async (): Promise<void> => {
await AuthenticationService.getInstance().inspectAuthentication();
};
在返回每个页面的 HTML 之前在服务器端执行,并且检查是否已通过用户身份验证。如果是,则将 CurrentAuthenticatedUser 存储在 Vuex 模块中:
import {
VuexModule,
getModule as getVuexModule,
Module as VuexModuleConfiguration,
VuexAction,
VuexMutation
} from "nuxt-property-decorator";
@VuexModuleConfiguration({
name: "AuthenticationService",
store,
namespaced: true,
stateFactory: true,
dynamic: true
})
export default class AuthenticationService extends VuexModule {
public static getInstance(): AuthenticationService {
return getVuexModule(AuthenticationService);
}
private _currentAuthenticatedUser: CurrentAuthenticatedUser | null = null;
public get currentAuthenticatedUser(): CurrentAuthenticatedUser | null {
return this._currentAuthenticatedUser;
}
@VuexAction({ rawError: true })
public async inspectAuthentication(): Promise<boolean> {
// This condition is always falsy after page reloading
if (this.isAuthenticationInspectionSuccessfullyComplete) {
return isNotNull(this._currentAuthenticatedUser);
}
this.onAuthenticationInspectionStarted();
// The is no local storage on server side; use @nuxtjs/universal-storage instead
const accessToken: string | null = DependenciesInjector.universalStorageService.
getItem(AuthenticationService.ACCESS_TOKEN_KEY_IN_LOCAL_STORAGE);
if (isNull(accessToken)) {
this.completeAuthenticationInspection();
return false;
}
let currentAuthenticatedUser: CurrentAuthenticatedUser | null;
try {
currentAuthenticatedUser = await DependenciesInjector.gateways.authentication.getCurrentAuthenticatedUser(accessToken);
} catch (error: unknown) {
this.onAuthenticationInspectionFailed();
// error wrapping / rethrowing
}
if (isNull(currentAuthenticatedUser)) {
this.completeAuthenticationInspection();
return false;
}
this.completeAuthenticationInspection(currentAuthenticatedUser);
return true;
}
@VuexMutation
private completeAuthenticationInspection(currentAuthenticatedUser?: CurrentAuthenticatedUser): void {
if (isNotUndefined(currentAuthenticatedUser)) {
this._currentAuthenticatedUser = currentAuthenticatedUser;
DependenciesInjector.universalStorageService.setItem(
AuthenticationService.ACCESS_TOKEN_KEY_IN_LOCAL_STORAGE, currentAuthenticatedUser.accessToken
);
}
// ...
}
}
上面的代码在服务器端运行良好,但是在客户端,如果要尝试获取AuthenticationService.getInstance().currentAuthenticatedUser,它将是null!
我预计 Nuxt.js 会将包括 AuthenticationService 在内的 Vuex 存储与服务器端同步,但是它没有。
目标
AuthenticationService必须与服务器端同步,所以如果用户已经通过身份验证,在客户端AuthenticationService.getInstance().currentAuthenticatedUser即使在页面重新加载后也必须是非空的。
服务器端不需要同步整个Vuex store(例如客户端只需要负责浮动通知栏的模块)但是如果没有开发选择性方法,至少同步整个Vuex store现在就够了。
请不要向我推荐用于身份验证的库或 Nuxt 模块,例如 Nuxt Auth module,因为这里我们讨论的是 Vuex 存储与服务器的同步,而不是用于身份验证的最佳 Nuxt 模块。此外,客户端和服务器之间的 vuex 存储同步不仅可以用于身份验证。
更新
preserveState解决方案尝试
很遗憾,
import { store } from "~/Store";
import { VuexModule, Module as VuexModuleConfiguration } from "nuxt-property-decorator";
@VuexModuleConfiguration({
name: "AuthenticationService",
store,
namespaced: true,
stateFactory: true,
dynamic: true,
preserveState: true /* New */
})
export default class AuthenticationService extends VuexModule {}
原因
Cannot read property '_currentAuthenticatedUser' of undefined
服务器端出错。
错误指的是
@VuexAction({ rawError: true })
public async inspectAuthentication(): Promise<boolean> {
if (this.isAuthenticationInspectionSuccessfullyComplete) {
// HERE ⇩
return isNotNull(this._currentAuthenticatedUser);
}
}
我检查了this 的值。这是一个大物体;我只留下值得注意的部分:
{
store: Store {
_committing: false,
// === ✏ All actual action here
_actions: [Object: null prototype] {
'AuthenticationService/inspectAuthentication': [Array],
'AuthenticationService/signIn': [Array],
'AuthenticationService/applySignUp': [Array],
// ...
// === ✏ Some mutations ...
onAuthenticationInspectionStarted: [Function (anonymous)],
completeAuthenticationInspection: [Function (anonymous)],
// ...
context: {
dispatch: [Function (anonymous)],
commit: [Function (anonymous)],
getters: {
currentAuthenticatedUser: [Getter],
isAuthenticationInspectionSuccessfullyComplete: [Getter]
},
// === ✏ The state in undefined!
state: undefined
}
}
我想我需要告诉我如何初始化 vuex 存储。 动态模块的working Nuxt methodology 是:
// store/index.ts
import Vue from "vue";
import Vuex, { Store } from "vuex";
Vue.use(Vuex);
export const store: Store<unknown> = new Vuex.Store<unknown>({});
nuxtServerInit解决方案尝试
这里是另一个问题 - 如何在上面的存储初始化方法中集成nuxtServerInit?我想,要回答这个问题,需要 Vuex 和 vuex-module-decorators。在store/index.ts 下面,nuxtServerInit 甚至不会被调用:
import Vue from "vue";
import Vuex, { Store } from "vuex";
Vue.use(Vuex);
export const store: Store<unknown> = new Vuex.Store<unknown>({
actions: {
nuxtServerInit(blackbox: unknown): void {
console.log("----------------");
console.log(blackbox);
}
}
});
我把这个问题提取到other question。
【问题讨论】:
标签: nuxt.js vuex vuex-module-decorators