关于Vuex及其基本使用

194 阅读7分钟

Vuex

Vuex是什么?

  • Vuex 是一个专为 Vue.js(vue2) 应用程序开发的状态管理模式 + 库
  • 采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
  1. 状态管理模式+库
  2. 对组件状态的管理,方法是集中式存储
  3. 以对应规则保证状态以可测方式发生变化

注意:Vuex 4 支持 Vue 3,但 Vuex 主要还是为 Vue 2 设计的

为什么需要Vuex

  • 通信困难: 组件之间的通信在大型项目中困难,如果他们需要频繁交互。例如很经典的父子传递(利用props和事件),如果存在多层嵌套就很复杂
  • 状态混乱:如果每个组件之间通信的状态都不同,那么后期难于管理

Vuex里面有什么?

  • state:存储数据的地方
  • mutations:唯一修改state的地方,是同步的,不能直接对异步任务操作
  • actions:事件业务逻辑处理,能够执行异步函数
  • getters:store的计算属性,方便数据调用和复用

流程

  • 组件通过dispatch向vuex发送一个actions,组件调用 store.dispatch('actionName') 来触发某个 action,通常用于处理异步操作或复杂的逻辑。
  • 通过actions处理完事件逻辑后将派发一个commitmutations中,让mutations接收到要做修改的数据
  • mutations层获取到数据之后,修改仓库中的state状态,mutations是同步函数,负责直接修改state的数据。它接收当前state和负载数据,然后进行修改。
  • 组件可以通过this.$store.state进行获取
  • getters层则是将state的数据进行计算,最终返回一个计算属性,这样组件就可以直接通过对应的属性进行调用,不需要this.$store.state

vuex-1729764042600-4-1729764406420-6.png

简化计算属性访问

mapState

  • 将 Vuex 的state映射到组件的computed属性

mapGetters

  • 将Vuex的getters映射到组件的computed属性

mapActions

  • 将组件的methods映射为store.dispatch 调用

使用场景

  1. 多组件共享状态:如果需要多个组件共享数据状态的时候,可以采用Vuex通信,例如兄弟组件通信
  2. 异步操作:如果你的应用中有异步数据请求(如 API 调用),Vuex的 actions 允许你管理异步操作,确保数据状态在操作完成后被正确更新
  3. 大型应用:在中大型应用中,组件树会变得复杂,使用Vuex可以有效管理状态,减少在组件之间传递 props 和事件的复杂性。
  4. 数据持久化:如果你需要在刷新之后保持状态(例如用户的登录状态),也可以结合Vuex
  5. 中间层观测:结合Vue Router观察路由变化(可以结合多层级实现菜单栏的动态生成)

如何使用

存在项目结构:

VueProject
src
  | views
    | home.vue
  | srote
    | index.js     // 引用入口,将所有用到vuex仓库的组件都放在这里
    | home
      | home.js   // home组件用到store

index.js

// 注意Vue3和Vue2的引用方法不同

// Vue2
import Vue from 'vue'
import Vuex from 'vuex'
import home from './home/home'
Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    home,
  },
})

// Vue3
import { createStore } from 'vuex'
import Home from './home/home.js'

const store = createStore({
  modules: {
    Home,
  },
})

home.js

const state = {
  count: 0,
}

// 修改state的手段
const mutations = {
  COUNT(state, newCount) {
    state.count = newCount
  }
}

const actions = {
  conputedCount({commit}, count) {
    count++
    commit('COUNT', count)
  }
}

const getters = {
  getCount(state) {
    return state.count
  }
}

// 前面已经通过index引入组件,所以直接暴露home组件的状态就行了
export default {
  state,
  mutations,
  actions,
  getters,
}

home.vue

<template>
  <div>
    <span>{{ count }}</span>
    <button @click="incrementCount">Increment Count</button>
  </div>
</template><script setup>
// 普通请求
import { computed } from 'vue'
import { useStore } from 'vuex'const store = useStore()
​
// 使用 computed 来获取 Vuex 状态,存在getters计算属性,直接通过getter拿
// 如果没有就需要使用   this.$store.state.count
const count = computed(() => store.getters.getCount)
​
// 定义一个方法来触发 action
const incrementCount = () => {
  // 与actions的方法一直
  store.dispatch('computedCount', count.value)
}
​
​
// 使用mapState等方法请求// 映射到了state
const { count } = mapState(['count'])
// 这里映射到了getter,要调用直接用getCount就可以了
const { getCount } = mapGetters(['getCount'])
const { computedCount  } = mapActions(['computedCount'])
​
// 计算属性,使用getter
const countComputed = computed(() => getCount(store.state))
​
// 重新赋值 count
const nowCount  = computed(() => countComputed.value)
​
const incrementCount = () => {
  computedCount(nowCount.value) // 传递当前 count 的值,默认值从第一步拿到,是 0
}
</script>

整个过程做了什么?

普通请求

  1. 定义了一个总store(总仓库),并且把他暴露出去,然后把有需要用到store的组件逐个导入
  2. 对于home自己的仓库,对home组件的数据状态进行管理
  3. 定义一个home组件
    • 首先拿到存放在仓库的默认值count:0
    • 当我点击button之后,派发一个叫computedCount名称的异步函数,将拿到的默认值也穿进去
    • 根据actions中的computedCount函数对数据处理,执行count++,提交commit方法,通知mutations现在这个count要更新了
    • mutations修改state中的count值
    • 由于getters被用来计算并返回 count,它会获取到更新后的值
    • 组件中的count通过computed自动更新,重新渲染DOM,显示最新的count值。

使用对应方法请求(简化书写):

  1. mapState:将store中的state映射到组件的computed属性中,也就是将store中的count映射到home组件中的count
  2. mapGetters:将getCount映射 getters,使得组件可以访问计算后的值
  3. mapActions:将store的 increment action映射为组件中的方法
  4. 当用户点击按钮时,调用 increment 方法,触发 store.dispatch('increment')
  5. increment action 中,调用 commit('INCREMENT'),提交一个 mutation
  6. mutations 中的 INCREMENT 函数执行,更新 state.count 的值
  7. getters 会自动重新计算并返回最新的count
  8. 由于组件中的 count 是通过 computed 自动绑定到store的状态,DOM 会重新渲染,显示最新的 count

组件通信

现在组件A和组件B之间存在通信需求

  1. 组件A通过发送请求更新状态库,派发actions处理,将组件A的数据存在store中
  2. 组件B直接从store中直接拿到组件A存进去的数据,操作后更新数据,又放到store
  3. 组件A从新拿到组件B更新的数据

登录状态保持

  1. 初始化状态

    • 在 Vuex 的 state 中,设置isLoggedInuser的初始值。
    • 在应用启动时,从localStoragesessionStorage读取用户登录状态和信息,初始化Vuex状态。
  2. 登录操作

    • 组件:用户在登录组件中输入凭据。
    • 派发 Action:通过 Vuex 的 login action,进行 API 请求验证用户信息。
    • 更新 Store:成功后,使用 SET_LOGIN_STATE mutation 更新 isLoggedInuser
    • 存储信息:同时,将用户信息存入 localStoragesessionStorage,确保数据持久化。
  3. 登出操作

    • 派发 Action:在组件中调用 logout action。
    • 清理状态:清除 Vuex 中的登录状态和用户信息。
    • 清除存储:同时清除 localStoragesessionStorage 中的用户信息。
  4. 组件之间通信

    • 组件 A:通过 Vuex 更新登录状态,派发 action,触发用户信息更新。
    • 组件 B:直接从 Vuex store 中获取用户信息。
    • 更新 Store:如果组件 B 修改了用户信息,更新 Vuex store,再存储到 localStorage
    • 组件 A:通过 Vuex 重新获取最新的用户信息,实现数据同步。
  5. 应用刷新

    • 在应用启动时,检查 localStoragesessionStorage 中是否有用户信息。
    • 如果存在,则自动更新 Vuex 状态,保持用户登录状态。

Vuex与pinia的区别

以下是 Vuex 和 Pinia 的主要区别,以表格形式呈现:

特性VuexPinia
设计理念以模块化为中心,适合大型应用更加简洁,适合中小型应用
API较为复杂,需要使用 mapStatemapGetters直接使用 store,更加直观
状态管理通过 mutationsactions 管理状态通过 actions 直接修改状态
类型支持需要额外的类型定义和支持内置 TypeScript 支持
插件支持插件生态丰富仍在发展中
性能对于大型应用可能稍显繁琐更加轻量化,性能更好
状态持久化需要手动实现持久化逻辑内置支持持久化
热重载需手动配置内置热重载支持
Vue 版本Vue 2 和 Vue 3Vue 3 专属

写在最后

Vuex能够帮助开发者更方便的管理组件中数据的流向以及状态,也能够利用其特点实现不同的功能。有什么不足或错误的地方欢迎指点。