You need to enable JavaScript to run this app. Quasar(Vue.js)中如何在一个boot文件引用另一boot创建的单例实例
优惠活动
大模型
产品
解决方案
定价
更多

Quasar(Vue.js)中如何在一个boot文件引用另一boot创建的单例实例

Quasar Boot 文件间共享单例实例的实现方案

原代码失效的原因

跑不通根本是踩了两个boot开发的常见坑:

  • ES模块的导出值快照特性:你在模块顶层声明let oidc = null后直接export { oidc },所有import方拿到的都是模块刚加载时的null快照,哪怕后面boot执行逻辑里给oidc重新赋值了,import侧拿到的引用也不会跟着变
  • 导入方式写错了:axios的boot文件里写import oidc from "boot/oidc-service",导出来的是oidc-service里那个默认导出的boot执行函数,根本不是你要的OIDC实例

适配单例场景的改造步骤

1. 重写boot/oidc-service.js的单例导出逻辑

别直接存个变量就往外导,改成带getter的容器对象,保证不管什么时候拿实例,都能取到最新的赋值:

import { AuthService } from '../authorization/AuthService';

// 单例访问容器,通过getter实时读取内部的实例值
const oidcContainer = {
  get instance() {
    return oidc
  }
}
let oidc = null

export default ({ router, store, Vue }) => {
  // boot执行时初始化OIDC实例
  const OIDC = new AuthService();
  router.beforeEach((to, from, next) => {
    const allowAnonymous = to.matched.some(record => record.meta.allowAnonymous)
    if (allowAnonymous) {
      next()
    } else {
      const isAuthenticated = OIDC.isAuthenticated()
      if (isAuthenticated) {
        next()
      } else {
        OIDC.signIn()
      }
    }
  })

  Vue.prototype.$oidc = OIDC
  // 给内部的单例变量赋值
  oidc = OIDC
}

// 导出容器,不要直接导裸变量
export { oidcContainer }

为啥要用getter?就是为了绕开ES模块的快照拷贝问题:getter会在你每次访问oidcContainer.instance的时候,实时读模块作用域下当前的oidc值,永远能拿到正确的单例,不会出现拿到null的情况。

2. 调整boot/axios.js的实例引入逻辑

修正导入方式,通过容器的getter拿实时的单例:

// 用命名导入拿单例容器,别默认导入那个boot函数
import { oidcContainer } from "boot/oidc-service"
import axios from 'axios'

let axiosInstance = null;
export default ({ app, router, store, Vue }) => {
    const AxiosInstance = axios.create({
      baseURL: window.env.BASE_URL
    })
  
    AxiosInstance.interceptors.request.use(function (config) {
      // 每次发请求的时候,通过getter取最新的OIDC单例
      const oidc = oidcContainer.instance
      return oidc.getAccessToken().then(token => {
        config.headers.Authorization = `Bearer ${token}`
        return config
      })
    }, (error) => {
      return Promise.reject(error)
    })
    
    Vue.prototype.$axios = AxiosInstance
    axiosInstance = AxiosInstance
}

export { axiosInstance }

Boot顺序配置

你之前在quasar.config.js里的boot引入顺序是对的,Quasar会严格按照数组里的顺序逐个跑boot导出的函数:

// quasar.config.js 片段
boot: [
  'oidc-service', // 先跑OIDC的初始化,把单例赋值完成
  'axios',       // 再跑axios初始化,这时候容器里的单例已经有值了
]

更省事的简化写法

如果你的AuthService初始化不需要依赖boot注入的router、store、Vue这些上下文参数,可以直接在模块顶层把单例实例化,根本不需要做容器:

// boot/oidc-service.js 简化版单例
import { AuthService } from '../authorization/AuthService';

// 模块加载的时候就实例化,天然是全局单例
export const oidc = new AuthService();

export default ({ router, store, Vue }) => {
  router.beforeEach((to, from, next) => {
    const allowAnonymous = to.matched.some(record => record.meta.allowAnonymous)
    if (allowAnonymous) {
      next()
    } else {
      const isAuthenticated = oidc.isAuthenticated()
      if (isAuthenticated) {
        next()
      } else {
        oidc.signIn()
      }
    }
  })

  Vue.prototype.$oidc = oidc
}

这种写法下,其他boot文件直接import { oidc } from 'boot/oidc-service'就能拿到单例,逻辑更简单。

注意:如果AuthService的初始化必须用到boot上下文里的参数(比如要拿router实例做登录跳转),就别用这种顶层实例化的写法,还是得用之前的getter容器方案。


内容的提问来源于stack exchange,提问作者span

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.02 17:15:03