Skip to content

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
}

其他

  1. 401 自动触发 logout(全局拦截)
  2. 并发请求 + token 过期处理
  3. 无感 refresh token(请求排队)