不少刚接触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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。