Vuex的原理及使用--以登录为例

Vuex

Vuex是一个专为Vue开发的状态管理库,它采用集中式存储的方式管理应用的所有组件的状态,解决了多组件数据通信的问题,使数据操作更加简洁

 打个比方:

  1. Vue Components(Vue 组件):就是你写的页面、按钮这些能看见的东西,是用户交互的入口。
  2. Actions(动作):负责 “干活前的准备”,比如调用后端接口、处理异步操作(像请求数据)。组件通过 Dispatch(派发) 告诉它:“我要干这个事儿啦!”
  3. Mutations(变更):专门负责修改数据,Actions 干完准备活,会用 Commit(提交) 告诉它:“该改数据啦!”
  4. State(状态):就是存数据的地方,Mutations 通过 Mutate(变更) 来改这里的数据,数据一变,Vue 组件就能感知到,重新 Render(渲染) 页面,更新内容。
  5. Backend API(后端接口):如果需要从服务器拿数据、存数据,Actions 会和它打交道,比如 “去后端取用户信息”。
  6. Devtools(调试工具):辅助开发的,能看到数据怎么变的、操作咋执行的,方便调试找问题。

简单说就是:组件说要干啥 → Actions 处理逻辑、调用接口 → 让 Mutations 改数据 → State 数据变了 → 组件跟着更新,一套流程帮你管好 Vue 里的数据和交互 ,Devtools 还能盯着流程找 bug 。

我的一点理解:

tate (类似存储全局变量的数据)
getters (提供用来获取state数据的方法)
actions (提供跟后台接口打交道的方法,并调用mutations提供的方法)
mutations (提供存储设置state数据的方法)
我理解action中的方法,类似事件,只是在vuex中注册,需要通过dispatch触发这个方法

mutation中的方法,类似事件,只是在vuex中注册,需要通过commit触发这个方法

ps:(官网上说的几个概念)

It is a self-contained app with the following parts:

The state, the source of truth that drives our app;
The view, a declarative mapping of the state;
The actions, the possible ways the state could change in reaction to user inputs from the view.
This is an simple representation of the concept of "one-way data flow":

However, the simplicity quickly breaks down when we have multiple components that share a common state:

Multiple views may depend on the same piece of state.
Actions from different views may need to mutate the same piece of state.
Vuex 的内脏由五部分组成:State、Getter、Mutation、Action 和 Module。

Vuex 的 mutations 和 actions 两段式设计 主要是为了 让数据流向更清晰、便于追踪和调试。用大白话解释就是:

为什么要有这种设计?

想象一个公司里,数据(state)就像是公司的资产,所有员工(组件)都能查看资产,但不能随便修改。
如果每个员工都能直接改资产数据,公司就乱套了(比如有人偷偷转走钱,却没人知道是谁干的)。

所以公司规定:

  1. 修改资产必须通过财务部门(mutations)
    财务部门有严格的记录,每笔修改都要登记(比如谁改的、改了多少、为什么改)。
    这样一来,所有修改都是透明的,出问题可以直接查记录。

  2. 但员工不能直接找财务部门
    如果员工想修改数据(比如申请一笔预算),必须先写申请(dispatch action),交给经理(actions)审批。
    经理可能会先确认申请是否合理(比如查一下有没有足够的预算),然后再把申请转给财务部门执行。

对应到 Vuex:
  • mutations 就是 “财务部门”,它是唯一能直接修改数据的地方,而且必须同步执行(就像财务操作必须马上记录)。
  • actions 就是 “经理”,负责处理复杂的逻辑(比如异步操作、权限验证),然后再调用 mutations 修改数据。
  • 组件 就是 “员工”,不能直接改数据,只能通过 actions 提交申请。
这么设计的好处:
  1. 便于调试
    如果数据出了问题,可以直接查看 mutations 记录,知道是谁在什么情况下修改了数据。
    Vuex DevTools 甚至能实时追踪每个 mutation 的变化。

  2. 避免竞态条件
    同步的 mutations 保证了数据修改的顺序性,不会出现多个修改同时进行导致的数据冲突。

  3. 分离关注点

    • 组件只负责触发 actions(提申请),不用关心具体怎么修改数据。
    • actions 只负责处理逻辑(审批申请),不用关心数据怎么保存。
    • mutations 只负责修改数据(执行财务操作),不用关心数据从哪里来。
举个栗子:

比如用户登录时:

  1. 组件触发 login action(员工提交 “登录申请”)。
  2. action 先调用 API 验证用户信息(经理查征信),然后:
    • 如果成功,调用 SET_USER mutation 保存用户数据(通知财务打钱)。
    • 如果失败,显示错误信息(拒绝申请)。
  3. mutation 修改 state(财务更新账户余额)。

这样整个流程清晰可控,代码也更容易维护。如果没有 actions,组件就得自己处理 API 请求和错误,代码会变得混乱且难以复用。

基本用法:

项目中使用Vuex时,通常的做法是先在项目中创建一个store模块,然后导入并挂载store模块。store模块是用于管理状态数据的仓库。

编写store模块,创建src\store\index.js文件

例子:

// store/index.js
import { createStore } from 'vuex'
import createPersistedState from 'vuex-persistedstate'

// 状态定义
const state = {
  count: 5,           // 合并原 reactive 的 count
  user: {
    id: '',
    token: '',
    isAuth: false
  },
  isLogin: false
}

// 突变(修改状态的唯一方式)
const mutations = {
  // 计数器相关
  SET_COUNT(state, value) {
    state.count = value
  },
  INCREMENT(state) {
    state.count++
  },
  
  // 用户认证相关
  SET_LOGIN_STATUS(state, flag) {
    state.isLogin = flag
    state.user.isAuth = flag
  },
  SET_USER_INFO(state, userData) {
    state.user = { ...state.user, ...userData }
  },
  LOGOUT(state) {
    state.user = { id: '', token: '', isAuth: false }
    state.isLogin = false
  }
}

// 动作(处理异步操作,提交突变)
const actions = {
  setLoginStatus({ commit }, flag) {
    commit('SET_LOGIN_STATUS', flag)
  },
  
  async login({ commit }, credentials) {
    try {
      // 模拟登录请求
      const response = await fetch('/api/login', {
        method: 'POST',
        body: JSON.stringify(credentials)
      })
      
      const data = await response.json()
      commit('SET_USER_INFO', {
        id: data.userId,
        token: data.token,
        isAuth: true
      })
      commit('SET_LOGIN_STATUS', true)
      return true
    } catch (error) {
      console.error('登录失败:', error)
      return false
    }
  },
  
  logout({ commit }) {
    commit('LOGOUT')
  }
}

// 计算属性(类似 Vue 的计算属性)
const getters = {
  count: state => state.count,
  isLoggedIn: state => state.isLogin,
  userInfo: state => state.user
}

// 创建 store 并启用持久化插件  需要安装 vuex-persistedstate,这里暂时不启用 
// export default createStore({
//   state,
//   mutations,
//   actions,
//   getters,
//   plugins: [
//     createPersistedState({
//       key: 'vuex-store',
//       storage: localStorage,
//       paths: ['user', 'isLogin'] // 只持久化用户相关状态
//     })
//   ]
// })

设计 isLogin 和 user.isAuth 双状态的核心逻辑

将登录状态拆分为两个字段并非多余,而是基于 "身份验证(Authentication)" 与 "权限授权(Authorization)" 的本质区别,在复杂业务中能显著提升系统灵活性。以下是具体优势:

1. 职责分离:解决不同维度的问题
状态字段本质含义典型场景
isLogin是否登录(身份验证)控制用户能否进入系统(如:未登录则跳转登录页)。
user.isAuth是否有权限(授权)控制已登录用户能否操作特定功能(如:普通用户不能访问管理员后台)。

例子

  • 用户登录后,isLogin = true,但如果是普通用户访问管理员页面,user.isAuth = false,系统会提示 "无权限"。
  • 反之,若用户未登录,isLogin = false,无论user.isAuth是什么状态,都无法进入系统。
2. 应对复杂权限场景:状态解耦更灵活
  • 场景 1:登录状态正常,但权限过期
    例如:用户登录后 token 有效期为 1 小时,1 小时后user.isAuth变为false,但isLogin仍为true(避免用户重复登录),系统可提示 "权限过期,请刷新页面"。
  • 场景 2:多角色权限控制
    同一个登录用户可能有不同权限(如:普通员工可查看数据,但不能修改),此时isLogin = true,但user.isAuth可细化为多个权限标记(如canEdit: false)。
3. 代码可维护性:避免状态耦合引发的隐患
  • 反例:合并为单一状态的风险
    若将登录和权限合并为isValid,当需要单独处理权限时(如权限刷新),必须修改isValid,可能导致系统误判用户未登录(例如跳转到登录页),影响用户体验。
  • 正例:双状态的清晰逻辑

    // 登录页逻辑(只关心是否登录)
    if (!isLogin) {
      // 跳转登录
    }
    
    // 管理员页面逻辑(既关心登录,也关心权限)
    if (!isLogin || !user.isAuth) {
      // 无权限提示
    }
    
4. 符合认证体系的标准设计

在主流认证方案(如 OAuth 2.0、JWT)中:

  • 身份验证(登录):通过用户名密码、Token 等确认用户身份,对应 isLogin
  • 权限授权:通过角色、Scope 等控制操作权限,对应 user.isAuth(或更细化的权限字段)。

这种拆分方式与行业标准一致,便于团队协作和后续功能扩展(如添加 RBAC 权限系统)。

双状态设计的核心价值

  • 必要性:在简单项目中可能看似冗余,但在中大型项目(尤其是涉及权限分级、状态刷新、多角色管理)中,双状态是避免逻辑混乱的关键。
  • 建议:若项目暂时无复杂权限需求,可暂时让 user.isAuth 与 isLogin 保持同步,但保留字段以便后续扩展。

action 详解

实现逻辑:

1. setLoginStatus({ commit }, flag)

作用:直接设置登录状态(同步操作)
参数

  • { commit }:解构自 context 对象,用于提交 mutation
  • flag:布尔值(true 表示已登录,false 表示未登录)

核心逻辑

setLoginStatus({ commit }, flag) {
  commit('SET_LOGIN_STATUS', flag)  // 直接提交对应的 mutation
}

~
这是一个简单的 "中转函数",组件如果想直接修改登录状态(比如测试环境下快速切换),可以调用这个 action。
但实际项目中,登录状态通常是通过登录 API 验证后自动设置的,所以这个 action 用得较少。

 实际项目不可能这样的,这里只是为举例而举例。

这个 action 的核心是:不经过任何验证流程,直接修改登录状态
比如:

// 组件中调用(危险操作!)
this.$store.dispatch('setLoginStatus', true)

这相当于在系统里直接把 "未登录" 改成 "已登录",跳过了账号密码验证、token 获取等关键流程

2. 实际项目中,状态更新必须 "有依据"

在真实项目中,登录状态的变更必须满足以下条件:

  • 必须通过后端验证(如账号密码正确、token 有效)
  • 必须获取必要的用户信息(如用户 ID、权限范围)

而 setLoginStatus 只做了一件事:单纯修改布尔值,没有处理:

  • 后端 API 请求
  • token 的存储(如 localStorage)
  • 用户权限的初始化
3. 正确的状态更新方式:通过登录流程触发

实际项目中,登录状态应该由 login action 触发,它包含完整流程:

  1. 发送登录请求到后端
  2. 后端返回用户信息和 token
  3. 同时更新 Vuex 状态 + 本地存储(如 localStorage)
  4. 标记登录状态

// 正确做法:通过 login action 触发状态更新
async function handleLogin() {
  const isSuccess = await this.$store.dispatch('login', {
    username: this.form.username,
    password: this.form.password
  })
  
  if (isSuccess) {
    // 登录成功后的操作(如跳转页面)
  }
}

2. async login({ commit }, credentials)

作用:处理登录流程(异步操作)
参数

  • credentials:用户输入的登录凭证(如 { username: 'test', password: '123' }

核心逻辑

async login({ commit }, credentials) {
  try {
    // 1. 发送登录请求到后端 API
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(credentials)
    })
    
    // 2. 解析响应数据
    const data = await response.json()
    
    // 3. 提交 mutations 更新状态
    commit('SET_USER_INFO', {
      id: data.userId,
      token: data.token,
      isAuth: true
    })
    commit('SET_LOGIN_STATUS', true)
    
    return true  // 返回成功标志
  } catch (error) {
    console.error('登录失败:', error)
    return false  // 返回失败标志
  }
}

~
这是登录功能的核心流程,就像用户在现实中刷卡进门:

  1. 刷卡验证:把用户输入的账号密码发给后端(fetch 请求)。
  2. 检查门禁卡:如果后端返回成功(如 token 和用户信息),说明验证通过。
  3. 开门放行
    • 用 SET_USER_INFO 把用户信息(如 ID、token)存到 Vuex 里(相当于给用户发一张门禁卡)。
    • 用 SET_LOGIN_STATUS 标记为已登录(相当于在系统里登记 "这个人可以进来")。
  4. 反馈结果:返回 true 告诉组件 "登录成功了"。
3. logout({ commit })

作用:处理退出登录流程
参数:无

核心逻辑

logout({ commit }) {
  commit('LOGOUT')  // 提交 LOGOUT mutation
}

~
用户要离开系统时,调用这个 action:

  1. 收走门禁卡LOGOUT mutation 会清空用户信息(如 token)和登录状态(就像收回用户的门禁卡)。
  2. 登记离开:把 isLogin 和 user.isAuth 都设为 false(相当于在系统里登记 "这个人已经走了")。

这三个 actions 的协作流程

  1. 登录流程
    组件 → 调用 login action → 发送请求到后端 → 验证成功后 → 提交两个 mutations(存用户信息 + 标记已登录)。

  2. 退出流程
    组件 → 调用 logout action → 提交 LOGOUT mutation → 清空用户信息和登录状态。

  3. 直接修改状态(较少用)
    组件 → 调用 setLoginStatus action → 直接修改登录状态(不经过 API 验证)。

设计优势
  • 分离关注点
    组件只需要触发 action(如 login),不用关心具体的登录逻辑和状态修改细节。
    所有认证逻辑都集中在 actions 里,便于维护和复用。

  • 异步处理
    用 async/await 优雅地处理异步请求,避免回调地狱,代码更易读。

  • 再啰嗦一句:async/await 就是 “异步操作的语法糖”

  • 错误处理
    通过 try/catch 捕获登录失败的情况,并返回布尔值给组件,方便组件根据结果做不同处理(如显示错误提示)。

  • 可测试性
    每个 action 都可以单独测试(如模拟 API 响应,验证是否正确提交 mutations)。

Vuex 中用户认证相关 Mutations 详解

这段代码是 Vuex 中用于管理用户认证状态的核心 mutations,下面我们逐一拆解它们的作用和实现逻辑:

1. SET_LOGIN_STATUS(state, flag)

作用:设置用户的登录状态(登录 / 退出)
参数

  • state:Vuex 的状态对象(包含 isLogin 和 user 等数据)
  • flag:布尔值(true 表示已登录,false 表示未登录)

核心逻辑

SET_LOGIN_STATUS(state, flag) {
  state.isLogin = flag           // 直接修改登录状态标记
  state.user.isAuth = flag       // 同时更新用户对象中的认证状态
}

~
就像在系统里挂一个 "开门 / 关门" 的牌子:

  • 当 flag 是 true,相当于挂 "营业中",isLogin 变为 true,同时告诉用户对象 "你已通过认证"(user.isAuth = true)。
  • 当 flag 是 false,相当于挂 "打烊了",两个状态一起置为 false
2. SET_USER_INFO(state, userData)

作用:更新用户的详细信息(如 ID、Token、权限等)
参数

  • state:Vuex 状态对象
  • userData:包含用户信息的对象(如 { id: 123, token: 'abc', role: 'admin' }

核心逻辑

SET_USER_INFO(state, userData) {
  state.user = { ...state.user, ...userData }  // 合并新旧用户数据
}

~
比如用户刚登录时,后端返回了用户信息:

  • 原来的 state.user 可能是空对象 {},现在要填入数据。
  • { ...state.user, ...userData } 相当于把新旧数据摊开拼在一起:
    • 如果 userData 有新字段(如 token),就直接添加;
    • 如果有重复字段(如 isAuth),就用 userData 的值覆盖旧值。
  • 最终 state.user 会变成最新的用户信息,比如 { id: 123, token: 'abc', isAuth: true }
3. LOGOUT(state)

作用:用户退出登录,清空认证状态
参数state(Vuex 状态对象)

核心逻辑

LOGOUT(state) {
  state.user = { id: '', token: '', isAuth: false }  // 重置用户对象为初始值
  state.isLogin = false                             // 标记为未登录
}

~
相当于用户离开系统时的 "清场操作":

  • 把用户对象里的敏感信息(ID、Token)全部清空,只保留初始的空字符串和 isAuth: false
  • 同时把全局的 isLogin 标记为 false,相当于告诉整个系统:"现在没有用户登录了"。
这三个 mutations 的协作逻辑
  1. 登录流程
    组件 → 触发 login action → action 调用后端接口 → 成功后调用 SET_USER_INFO(存用户数据)和 SET_LOGIN_STATUS(true)(标记已登录)。

  2. 退出流程
    组件 → 触发 logout action → action 直接调用 LOGOUT mutation → 一次性清空用户数据和登录状态。

  3. 数据更新
    当用户信息变更(如修改个人资料),通过 SET_USER_INFO 只更新变化的字段,保留未修改的字段(如 token 不会被清空)。

设计优势
  • 单一职责:每个 mutation 只做一件事,代码更清晰。
  • 可追踪性:所有状态修改都通过 mutation 记录,方便用 Vuex DevTools 调试。
  • 安全性:避免组件直接修改 state,防止数据被意外篡改。

Vuex Getters :状态派生与缓存

Vuex 的 getters 就像是 Vue 组件中的 计算属性(computed),主要用于从 state 中派生出新的数据简化状态访问。下面用大白话解释它们的作用、解决的问题和使用方式:

一、Getters 是什么?解决什么问题?
1. 简化复杂状态访问

假设你的 state 里有个嵌套很深的对象:

state = {
  user: {
    profile: {
      name: '张三',
      age: 25,
      address: {
        city: '北京',
        street: '朝阳区'
      }
    }
  }
}

每次在组件里访问 city 都要写 this.$store.state.user.profile.address.city,很麻烦。
getters 可以简化这个过程

const getters = {
  userCity: state => state.user.profile.address.city
}

在组件里直接用 this.$store.getters.userCity 就能拿到值。

2. 状态派生(计算新值)

比如你有个购物车 state:

state = {
  cart: [
    { id: 1, name: '手机', price: 5000, quantity: 1 },
    { id: 2, name: '耳机', price: 500, quantity: 2 }
  ]
}

如果你想计算购物车总价,每次都在组件里写循环计算很麻烦,而且重复。
getters 可以封装这个计算逻辑

const getters = {
  cartTotal: state => {
    return state.cart.reduce((total, item) => total + (item.price * item.quantity), 0)
  }
}

在组件里直接用 this.$store.getters.cartTotal 就能拿到总价,而且自动响应 state 的变化。

3. 缓存计算结果

和 Vue 的计算属性一样,getters 的结果会缓存,只有依赖的 state 变化时才会重新计算。
比如上面的 cartTotal,只要购物车内容不变,多次访问都不会重新计算,提高性能。

二、如何使用 Getters?
1. 在 Vuex 中定义 Getters

const getters = {
  // 1. 直接返回 state 中的值(简化访问)
  count: state => state.count,
  isLoggedIn: state => state.isLogin,
  userInfo: state => state.user,
  
  // 2. 派生新值(计算属性)
  isAdmin: state => state.user.role === 'admin',
  
  // 3. 接受参数(返回函数)
  getUserById: (state) => (id) => {
    return state.users.find(user => user.id === id)
  }
}

export default createStore({
  state,
  getters,  // 别忘了在 store 中注册 getters
  // ...
})
2. 在组件中使用 Getters

有两种方式:

方式一:直接访问 $store.getters
export default {
  computed: {
    // 直接访问
    count() {
      return this.$store.getters.count
    },
    isAdmin() {
      return this.$store.getters.isAdmin
    }
  }
}
方式二:使用 mapGetters 辅助函数(推荐)

3. 使用带参数的 Getters

// 定义
const getters = {
  getUserById: (state) => (id) => {
    return state.users.find(user => user.id === id)
  }
}

// 使用
export default {
  methods: {
    fetchUser() {
      const user = this.$store.getters.getUserById(123)
      console.log(user)
    }
  }
}
三、Getters vs State vs 组件计算属性
类型作用示例
state存储原始数据state.count = 5
getters从 state 派生数据,类似 “计算属性”,有缓存getters.doubleCount = state.count * 2
组件计算属性从组件内的数据或 Vuex state/getters 派生,仅在组件内使用computed: { total() { return this.items.length } }
四、总结:Getters 的主要作用
  1. 代码复用:避免在多个组件中重复写相同的状态访问逻辑。
  2. 简化代码:用更短的语法访问深层嵌套的 state。
  3. 性能优化:缓存计算结果,避免不必要的重复计算。
  4. 关注点分离:将状态派生逻辑集中在 getters 中,组件只负责使用结果。

vue+element UI 学习总结笔记(十七)_vuex在项目中的应用_vue elment getters user-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值