不少刚接触Vue2的同学,一碰到全局变量就犯难—到底啥是全局变量?怎么在项目里定义、调用?不同场景选哪种方式更顺手?今天就从基础概念到实操方法,把Vue2全局变量的门道拆明白,帮新手踩稳第一步
Vue2 里“全局变量”到底指啥?
你可以把“全局变量”理解成项目里所有组件都能访问、修改的变量,举个例子:用户登录后,“是否登录”这个状态要在导航栏、个人中心、购物车等多个组件里用;再比如App的主题颜色、全局的接口域名,这些跨组件共用的信息,就适合用“全局变量”来管理。
和组件里的data不一样,组件data里的变量只在当前组件生效,其他组件没法直接拿;但全局变量是“通用于整个项目”的,不管哪个组件,只要想访问,随时能拿到(当然得用对应的方法)。
Vue2 定义全局变量有哪些常用方法?
Vue2里实现全局变量的方式不少,核心是根据项目规模、变量是否需要“响应式”(修改后自动更新页面)、团队协作成本来选,下面这4种方法最常用:
Vue.prototype 挂载(最传统的“快捷方式”)
原理很简单:把变量/函数挂到Vue.prototype上,这样所有组件通过this.$xxx就能访问。
举个例子:全局工具函数
新建utils.js写工具函数:
export function formatTime(time) {
// 处理时间格式的逻辑
return new Date(time).toLocaleString()
}
在main.js里挂载到原型:
import Vue from 'vue'
import { formatTime } from './utils.js'
Vue.prototype.$formatTime = formatTime
之后任何组件里,直接this.$formatTime(1699999999999)就能调用。
优点:简单粗暴,一行代码就能全局可用;
缺点:① 变量名容易和组件内部变量冲突(比如多个开发者都用$util,就会覆盖);② 如果挂载的是基本类型(字符串、数字、布尔值),修改后其他组件不会自动更新(因为是“值传递”,不是引用传递)。
Vuex(官方推荐的“复杂状态管家”)
Vuex是Vue官方的状态管理库,专门解决“多组件共享且频繁修改”的状态问题(比如购物车数据、用户权限、全局loading状态),核心概念有state(存数据)、mutations(同步修改state)、actions(异步操作,再提交mutation)。
举个例子:管理用户登录状态
① 新建store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
userInfo: null // 存用户信息
},
mutations: {
// 同步修改state的方法
SET_USER(state, payload) {
state.userInfo = payload
}
},
actions: {
// 异步操作(比如调接口)后,提交mutation
async login({ commit }, user) {
// 模拟接口请求
const res = await api.login(user)
commit('SET_USER', res.data)
}
}
})
② 在main.js里引入并注册:
import store from './store'
new Vue({
store,
render: h => h(App)
}).$mount('#app')
③ 组件里用this.$store访问:
登录时调action:
methods: {
async handleLogin() {
await this.$store.dispatch('login', { username: 'xxx', pwd: 'xxx' })
// 登录成功后跳转
}
}
其他组件里取用户信息:
computed: {
userInfo() {
return this.$store.state.userInfo
}
}
优点:① 天生“响应式”,state一改,所有用了它的组件自动更新;② 有严格的修改流程(必须通过mutation提交),多人协作不容易乱;③ 有Vuex Devtools调试工具,能回溯状态变化;
缺点:小项目用着“太重”,配置和代码量多,增加学习成本。
provide / inject(“跨层级传值”的神器)
provide是“祖先组件提供数据”,inject是“子孙组件注入数据”——哪怕中间隔了N层组件,也能直接传值,不用层层props往下递。
举个例子:全局主题配置
在根组件(比如App.vue)里提供主题:
export default {
provide() {
return {
theme: this.themeConfig // 把data里的themeConfig提供出去
}
},
data() {
return {
themeConfig: { color: 'blue', size: 'medium' }
}
}
}
深层子组件里注入并使用:
export default {
inject: ['theme'],
computed: {
themeColor() {
return this.theme.color
}
}
}
注意:默认provide/inject不是“响应式”的!如果themeConfig里的属性被修改,子组件不会自动更新,要解决这个问题,可以用Vue.observable把对象变成响应式:
import Vue from 'vue'
export default {
provide() {
return {
theme: Vue.observable({ color: 'blue', size: 'medium' })
}
}
}
这样子孙组件修改theme.color = 'red',根组件和其他用了theme的组件都会更新。
优点:灵活解决“跨多层级传值”的痛点,不用把props从祖父组件传到孙子组件;
缺点:如果不做响应式处理,修改数据不会触发更新;且不像Vuex有明确的“修改规范”,多人协作时容易乱改数据。
单独的JS模块导出(“纯JS层面”的全局)
原理是:新建一个JS文件,用export导出变量/对象,其他组件通过import引入。
举个例子:全局静态配置(比如接口域名)
新建config.js:
export const BASE_URL = 'https://api.xxx.com' export const PAGE_SIZE = 10
组件里引入使用:
import { BASE_URL } from '@/config.js'
export default {
methods: {
async getList() {
const res = await axios.get(`${BASE_URL}/list`)
}
}
}
优点:和Vue完全解耦,纯JS语法,简单易维护;
缺点:修改后不会触发组件更新(因为不是响应式数据),所以只适合存“不会变的常量”(比如接口域名、枚举值),不适合存动态变化的数据(比如用户登录状态)。
不同场景下,选哪种方式更合适?
没有万能方法,得看项目规模、变量是否要响应式、团队协作成本:
| 场景描述 | 推荐方法 | 原因 |
|---|---|---|
| 小项目,只需全局工具函数/静态常量 | Vue.prototype + 单独JS模块 |
简单快捷,学习成本低 |
| 多组件共享且需“响应式”(如登录状态、购物车) | Vuex | 官方方案,响应式+严格修改流程,适合中大型项目 |
| 跨多层级传值,且数据修改少 | provide/inject + Vue.observable(做响应式) |
解决“层层传props”的麻烦,灵活度高 |
| 纯静态配置(接口域名、枚举值) | 单独JS模块导出 | 无Vue依赖,维护简单,适合不变的数据 |
实操避坑:这些细节新手容易踩雷!
知道了方法,还要避开“隐性陷阱”:
Vue.prototype 的“响应式”坑
如果挂载的是基本类型(比如数字、字符串),修改后其他组件拿不到新值!
let count = 0 Vue.prototype.$count = count count = 1 // 这里修改后,组件里this.$count还是0!
解决办法:挂载对象/数组(引用类型),修改内部属性:
const globalObj = { count: 0 }
Vue.prototype.$globalObj = globalObj
// 组件里修改:this.$globalObj.count = 1 → 所有组件能拿到新值
Vuex 里 mutation 必须“同步”
新手常犯的错:在mutation里写异步代码(比如调接口),但Vuex规定:mutation只能是同步函数(否则Vuex Devtools没法追踪状态变化)。
解决办法:异步操作(如调接口)放到action里,再由action提交mutation:
actions: {
async getGoods({ commit }) {
const res = await api.getGoods() // 异步调接口
commit('SET_GOODS', res.data) // 提交mutation改state
}
}
provide/inject 的“响应式”陷阱
默认情况下,provide传的普通对象,子组件修改后不会触发父组件更新,比如根组件provide了theme: { color: 'blue' },子组件改theme.color = 'red',根组件的theme不会变。
解决办法:用Vue.observable把对象变成响应式:
provide() {
return {
theme: Vue.observable({ color: 'blue' })
}
}
这样子孙组件修改theme.color,所有用了theme的组件都会更新。
单独JS模块的“更新”坑
用import引入的JS模块变量,修改后组件不会自动更新,比如config.js里的BASE_URL,如果后期要动态改域名,用这种方式完全没效果——因为它本质是“静态导入”,不是响应式数据。
:这种方式只适合存“永远不变的常量”,动态数据别用它。
从0到1:用Vuex搭建全局状态管理(实战案例)
光说不练假把式,用“用户登录状态”为例,带大家走一遍Vuex的实操流程:
步骤1:新建Vuex Store
在src/store/index.js里写:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 1. 存数据的地方
state: {
userInfo: null, // 初始为null,表示未登录
token: ''
},
// 2. 同步修改state的方法(必须同步)
mutations: {
SET_USER_INFO(state, payload) {
state.userInfo = payload
},
SET_TOKEN(state, payload) {
state.token = payload
}
},
// 3. 异步操作(调接口)的地方
actions: {
// 登录 action
async login({ commit }, userInfo) {
// 模拟接口请求(实际项目里换成axios调真实接口)
const res = await new Promise((resolve) => {
setTimeout(() => {
resolve({
code: 200,
data: { ...userInfo, token: 'xxx123' }
})
}, 1000)
})
if (res.code === 200) {
commit('SET_USER_INFO', res.data) // 提交mutation存用户信息
commit('SET_TOKEN', res.data.token) // 存token
return true // 登录成功
}
return false // 登录失败
},
// 退出登录 action
logout({ commit }) {
commit('SET_USER_INFO', null)
commit('SET_TOKEN', '')
}
}
})
步骤2:在main.js里注册Store
import Vue from 'vue'
import App from './App.vue'
import store from './store' // 引入Store
new Vue({
store, // 注册到Vue实例
render: h => h(App)
}).$mount('#app')
步骤3:登录组件里调用action
假设Login.vue是登录页面:
<template>
<div>
<input v-model="username" placeholder="用户名" />
<input v-model="password" type="password" placeholder="密码" />
<button @click="handleLogin">登录</button>
</div>
</template>
<script>
export default {
data() {
return {
username: '',
password: ''
}
},
methods: {
async handleLogin() {
const isSuccess = await this.$store.dispatch('login', {
username: this.username,
password: this.password
})
if (isSuccess) {
// 登录成功,跳转到首页
this.$router.push('/home')
} else {
alert('登录失败')
}
}
}
}
</script>
步骤4:任意组件里获取用户状态
比如Header.vue导航栏,显示用户昵称:
<template>
<div class="header">
<div v-if="userInfo">欢迎,{{ userInfo.username }}</div>
<div v-else>请登录</div>
</div>
</template>
<script>
export default {
computed: {
userInfo() {
return this.$store.state.userInfo
}
}
}
</script>
通过这个案例,能直观感受到Vuex“统一管理、响应式更新、流程规范”的优势——登录状态一改,所有用了userInfo的组件都会自动更新。
全局变量的核心是“场景匹配”
Vue2里的全局变量,没有“最好的方法”,只有“最适合当前场景的方法”:
- 小项目追求“快”,用
Vue.prototype挂工具函数,用单独JS模块存静态配置; - 中大型项目状态复杂,选Vuex,用“state+mutation+action”的规范流程管理;
- 跨层级传值又不想写一堆
props,用provide/inject,记得用Vue.observable做响应式; - 纯静态数据(接口域名、枚举),直接用JS模块导出,简单省心。
关键是理解每种方式的特性(是否响应式、修改是否规范、学习/维护成本),再结合项目规模、团队协作方式来选,把这些搞清楚,再碰到“全局变量”就不会慌啦~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网



发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。