面试官问 Vue3 全局变量怎么设置?把这 4 种方法甩给他

6 阅读7分钟

Vue3 全局变量设置的 4 种方法

引言

在 Vue3 项目开发中,我们经常需要在多个组件间共享一些全局状态,比如用户登录信息、应用主题配置、全局加载状态、API 基础地址等。如何优雅地设置和使用这些全局变量,直接影响代码的可维护性和开发效率。

本文基于实际开发经验,详细介绍 Vue3 中设置全局变量的 4 种主流方法,帮助你根据具体场景选择最合适的方案。

一、为什么需要全局变量?

在深入具体方法之前,先明确哪些场景需要考虑使用全局变量:

  • 用户登录状态:用户是否登录、用户 ID、用户名、头像等信息,几乎每个页面都可能用到
  • 应用主题配置:比如当前是亮色模式还是暗色模式,这个设置会影响整个应用的样式
  • 全局的加载状态:在发起网络请求时,你可能希望在全应用范围显示一个加载动画
  • 一些不变的配置信息:比如后端的 API 基础地址、应用的版本号、第三方服务的 Key 等
  • 需要在多个无关联组件间共享的数据:两个组件既不是父子关系,也不是兄弟关系,但需要同步数据

如果你的项目里有上面这些需求,那么设置全局变量就是一个好选择。

二、方法一:使用 app.config.globalProperties

这是 Vue3 官方提供的一种方式,非常直接。你可以把任何变量或方法挂载到 Vue 应用的全局实例上,这样在任何组件里都能通过 this 来访问。

2.1 基本用法

main.jsmain.ts 文件中创建 Vue 应用时进行配置:

// main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// 定义你想全局使用的变量或方法
const globalData = {
  apiBaseUrl: 'https://api.yoursite.com/v1',
  siteName: '我的 Vue 应用',
  showToast(message) {
    // 假设这里有一个显示提示框的逻辑
    console.log('Toast:', message)
  }
}

// 将它们挂载到 app.config.globalProperties 上
app.config.globalProperties.$global = globalData

app.mount('#app')

2.2 在组件中使用

设置完成后,在组件模板和选项式 API 中可以这样使用:

<template>
  <div>
    <h1>欢迎来到 {{ $global.siteName }}</h1>
    <button @click="fetchData">获取数据</button>
  </div>
</template>

<script>
export default {
  methods: {
    fetchData() {
      // 使用全局的 api 地址
      const url = this.$global.apiBaseUrl + '/user'
      console.log('请求地址:', url)
      // 调用全局方法
      this.$global.showToast('开始获取数据')
    }
  }
}
</script>

2.3 在 Composition API 中使用

<script setup> 语法糖里,没有 this。你需要用 getCurrentInstance 来获取:

<script setup>
import { getCurrentInstance } from 'vue'

const instance = getCurrentInstance()
const globalData = instance?.appContext.config.globalProperties.$global

const handleClick = () => {
  globalData?.showToast('来自 setup 的消息')
}
</script>

2.4 TypeScript 类型支持

如果你使用 TypeScript,需要扩展全局类型声明:

// env.d.ts 或 shims-vue.d.ts
declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $global: {
      apiBaseUrl: string;
      siteName: string;
      showToast: (msg: string) => void;
    }
  }
}

2.5 优缺点分析

优点:

  • 设置简单,符合 Vue2 升级用户的习惯(类似 Vue.prototype
  • 在选项式 API 或模板中直接使用非常方便

缺点:

  • 在 Composition API 中使用不太方便,需要通过 getCurrentInstance 获取
  • 类型声明需要额外处理
  • 不够清晰,组件依赖关系不明确

三、方法二:使用依赖注入(Provide / Inject)

这是 Vue3 更推荐的一种方式,尤其适合 Composition API。它的思想是,在应用顶层"提供"数据,在任何深层的子组件里都可以"注入"并使用这些数据。

3.1 基本用法

main.js 中,用 app.provide 来提供全局数据:

// main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// 准备全局数据
const globalData = {
  apiBaseUrl: 'https://api.yoursite.com/v1',
  siteName: '我的 Vue 应用',
}

// 使用 app.provide 提供出去
// 第一个参数是"钥匙"(key),第二个参数是"值"(value)
app.provide('globalData', globalData)

app.mount('#app')

在子组件中使用 inject 来获取:

<!-- 某个深层子组件 ChildComponent.vue -->
<template>
  <div>
    <p>网站名称:{{ siteName }}</p>
    <button @click="testApi">测试 API 地址</button>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// 使用 inject 注入,参数就是之前提供的 key
const globalData = inject('globalData')

// 现在你可以使用 globalData 了
const siteName = globalData.siteName

const testApi = () => {
  console.log('API 基础地址是:', globalData.apiBaseUrl)
}
</script>

3.2 响应式支持

让全局数据变成响应式的,这样所有组件都能同步更新:

// main.js
import { createApp, reactive } from 'vue'
import App from './App.vue'

const app = createApp(App)

// 使用 reactive 创建响应式全局对象
const globalState = reactive({
  user: {
    name: '访客',
    isLogin: false
  },
  theme: 'light'
})

// 提供一个修改用户登录状态的方法
function login(userInfo) {
  globalState.user.name = userInfo.name
  globalState.user.isLogin = true
}

// 可以同时提供多个
app.provide('globalState', globalState)
app.provide('login', login)

app.mount('#app')

在组件中使用:

<script setup>
import { inject } from 'vue'

const globalState = inject('globalState')
const login = inject('login')

const handleLogin = () => {
  // 调用全局方法,修改全局状态
  login({ name: '张三' })
  // 此时,所有注入了 globalState 的组件,都会看到 user.name 变成了'张三'
}
</script>

<template>
  <div>当前用户:{{ globalState.user.name }}</div>
  <button @click="handleLogin">登录</button>
</template>

3.3 优缺点分析

优点:

  • 响应式支持好,是 Vue3 的官方推荐模式
  • 与 Composition API 集成度高
  • 组件依赖关系清晰,一目了然

缺点:

  • 需要稍微理解"提供"和"注入"的概念
  • 数据流向依然是自上而下的,不适合跨越多层"兄弟组件"的通信

四、方法三:使用状态管理(Pinia)

在 Vue3 的生态里,Pinia 是官方推荐的状态管理库,它比之前的 Vuex 更简单、更强大,也完美支持 TypeScript。你可以把 Pinia 理解为一个专业的、响应式的"全局变量仓库"。

4.1 安装与配置

首先安装 Pinia:

npm install pinia

main.js 中创建 Pinia 实例并挂载到 Vue 应用上:

// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
const pinia = createPinia() // 创建 Pinia 实例
app.use(pinia) // 使用 Pinia

app.mount('#app')

4.2 定义 Store

src/stores 目录下创建一个 global.js(或 global.ts):

// src/stores/global.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

// defineStore 定义仓库,第一个参数是仓库的唯一 ID
export const useGlobalStore = defineStore('global', () => {
  // 状态 - 相当于你的全局变量
  const siteName = ref('我的 Vue 应用')
  const apiBaseUrl = ref('https://api.yoursite.com/v1')
  const user = ref({ name: '访客', isLogin: false })
  const theme = ref('light')
  
  // 计算属性 - 基于状态衍生出的值
  const welcomeMessage = computed(() => {
    return user.value.isLogin ? `欢迎回来,${user.value.name}` : '请先登录'
  })
  
  // 动作 - 修改状态的方法
  function login(userInfo) {
    user.value.name = userInfo.name
    user.value.isLogin = true
    // 这里通常还会做一些异步操作,比如调用登录接口
  }
  
  function logout() {
    user.value.name = '访客'
    user.value.isLogin = false
  }
  
  function toggleTheme() {
    theme.value = theme.value === 'light' ? 'dark' : 'light'
  }
  
  // 最后,返回所有需要在组件中使用的东西
  return {
    siteName,
    apiBaseUrl,
    user,
    theme,
    welcomeMessage,
    login,
    logout,
    toggleTheme
  }
})

4.3 在组件中使用

<!-- 任意组件 -->
<template>
  <div :class="`app-${globalStore.theme}`">
    <h1>{{ globalStore.siteName }}</h1>
    <p>{{ globalStore.welcomeMessage }}</p>
    <button @click="handleLogin">登录</button>
    <button @click="globalStore.toggleTheme">切换主题</button>
  </div>
</template>

<script setup>
// 导入仓库定义函数,并执行它,得到仓库实例
import { useGlobalStore } from '@/stores/global'

const globalStore = useGlobalStore()

const handleLogin = () => {
  globalStore.login({ name: '李四' })
}
</script>

4.4 优缺点分析

优点:

  • 类型安全:TypeScript 支持非常好,所有状态和方法都有完整的类型推断
  • 模块化:可以定义多个不同的仓库(比如用户仓库、商品仓库、设置仓库)
  • 开发工具支持:有 Vue Devtools 的专门插件,可以方便地跟踪状态变化和时间旅行调试
  • 灵活:可以在组件、甚至其他仓库中自由使用
  • 服务端渲染友好:对 SSR 有很好的支持

缺点:

  • 需要额外学习一个库的概念
  • 对于非常简单的全局变量来说有点"杀鸡用牛刀"

五、方法四:使用纯粹的 JavaScript 模块

有时候,你的全局变量非常简单,可能只是一些配置常量,或者一些纯工具函数。它们不需要响应式,也不会改变。这种情况下,一个最简单的 ES 模块就足够了。

5.1 基本用法

创建一个 .js.ts 文件,导出你的常量或函数:

// src/config/constants.js
export const API_BASE_URL = 'https://api.yoursite.com/v1'
export const SITE_NAME = '我的 Vue 应用'
export const APP_VERSION = '1.0.0'
export const MAX_UPLOAD_SIZE = 5 * 1024 * 1024 // 5MB

// 一些纯函数
export function formatDate(date) {
  // 格式化日期的逻辑
  return new Date(date).toLocaleDateString()
}

在任何需要的地方导入使用:

<script setup>
// 直接导入即可
import { API_BASE_URL, SITE_NAME, formatDate } from '@/config/constants'

console.log(`正在访问 ${SITE_NAME},API 地址:${API_BASE_URL}`)
const today = formatDate(new Date())
</script>

5.2 适用场景

  • 应用发布后就不会改变的配置项
  • 纯工具函数库(比如日期处理、字符串格式化、数学计算等)
  • 第三方服务的静态配置

5.3 优缺点分析

优点:

  • 极其简单,零依赖
  • 性能最好
  • Tree-shaking 友好(没用到的常量不会被打包进去)

缺点:

  • 不是响应式的,不能用来存储会变化的应用状态

六、总结与选型建议

方法适用场景响应式复杂度TypeScript 支持
app.config.globalPropertiesVue2 迁移项目、选项式 API需手动实现需额外声明
Provide / Inject中小型项目、Composition API原生支持良好
Pinia大型项目、复杂状态管理原生支持中高优秀
JavaScript 模块静态配置、工具函数不支持极低优秀

选型建议:

  1. 静态配置和工具函数:优先使用 JavaScript 模块,简单高效
  2. 中小型项目:使用 Provide / Inject,官方推荐,与 Composition API 完美集成
  3. 大型复杂项目:使用 Pinia,功能强大,类型安全,易于维护
  4. Vue2 迁移项目:可以暂时使用 app.config.globalProperties,但建议逐步迁移到更现代的方案