vue init 设计
auth store
ts
import { loadToken, saveToken, removeToken } from "app-shared/util"
import { defineStore } from "pinia"
type IAuthStatus = 'authenticated' | 'unauthenticated' | 'loading'
export const useAuthStore = defineStore('auth', {
state: () => ({
token: '',
status: 'unauthenticated' as IAuthStatus
}),
getters: {
/**
* true 已登录
*/
isAuthenticated: (state) => state.status === 'authenticated',
},
actions: {
setToken(token: string) {
this.token = token
saveToken(token)
},
loadToken() {
const token = loadToken()
if (token) {
this.token = token
} else {
this.status = 'unauthenticated'
}
},
clearToken() {
this.token = ''
removeToken()
},
authenticating() {
this.status = 'loading'
},
loginSuccess(token: string) {
this.setToken(token)
this.status = 'authenticated'
},
logout() {
this.clearToken()
this.status = 'unauthenticated'
},
},
})简化版
state:
- token: string | null
- initializing: boolean
getters:
- isAuthenticated = !!token
- isLoading = initializing
user store
ts
import { defineStore } from 'pinia'
interface UserInfo {
id: number
account: string
email: string
phone: string
nickname: string
createdAt: string
}
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null as UserInfo | null,
}),
actions: {
async fetchUserInfo() {
},
setUserInfo() { },
clearUserInfo() { },
hasPermission() {
// todo 思考
}
}
})init
让所有调用共享同一个初始化过程
单飞执行(single-flight)
1️⃣ 并发控制(single-flight) 2️⃣ 状态一致性(避免竞态) 3️⃣ 初始化幂等(只执行一次)
ts
import { useAuthStore, useUserStore } from "."
let initPromise: Promise<void> | null = null
export function initAppAuth() {
if (initPromise) {
return initPromise
}
initPromise = (async () => {
try {
const auth = useAuthStore()
const user = useUserStore()
if (!auth.token) {
auth.loadToken()
}
if (!auth.token) return
auth.authenticating()
await user.fetchUserInfo()
auth.loginSuccess(auth.token)
} catch {
const auth = useAuthStore()
const user = useUserStore()
auth.logout()
user.clearUserInfo()
} finally {
initPromise = null // 👈 关键
}
})()
return initPromise
}其他
- 401 自动触发 logout(全局拦截)
- 并发请求 + token 过期处理
- 无感 refresh token(请求排队)