Code前端首页关于Code前端联系我们

不少刚接触Vue2的同学,一碰到全局变量就犯难—到底啥是全局变量?怎么在项目里定义、调用?不同场景选哪种方式更顺手?今天就从基础概念到实操方法,把Vue2全局变量的门道拆明白,帮新手踩稳第一步

terry 20小时前 阅读数 11 #Vue
文章标签 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传的普通对象,子组件修改后不会触发父组件更新,比如根组件providetheme: { 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前端网发表,如需转载,请注明页面地址。

发表评论:

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

热门