vuex之state、Mutations、getters、actions、modules

本文详细介绍了Vue.js应用中使用Vuex进行状态管理的方法,包括状态(state)、提交(mutations)、获取器(getters)及操作(actions)的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

state
1.文件创建
当然,文件创建的位置自己可以定,本文例子都在src文件夹下进行创建。
main.js

import Vue from 'vue'
import store from './store'
import vuex_test from './vuex_test.vue'


new Vue({
  el:'#app',
  store,
  render:xx=>xx(vuex_test)
})

vuex_test.vue

<template>
  <div id="app">
    <h2>hello vuex</h2>
    <p>{{$store.state.count}}----------{{count}}</p>
  </div>
</template>
<script>
  export  default {
    name : 'app',
    data() {
      return {
        count : 0       // 计算属性名称不能与data中定义的数据相同 The computed property "count" is already defined in data.
      }
    }
  }
</script>

store.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

// 访问状态对象
const state = {
  count:0
}

export default new Vuex.Store({
  state

})

最基本的用法{{$store.state.count}}引用,显示结果:
在这里插入图片描述
其他 方式引用1:
vuex_test.vue中添加:

computed : {
      count(){
        return this.$store.state.count+2
      }
    }

哦豁,报错了,让我们看下错误:

vue.esm.js?c5de:628 [Vue warn]: The computed property "count" is already defined in data.
found in
---> <App> at src/vuex_test.vue
       <Root>

在这里插入图片描述
然后我百度了下原因:
Vue binds all properties in the data method to the root of the instance. It also does this for computed properties, and methods, so you must use different names to avoid naming conflicts.
The main problem in the code you posted is that you have naming conflicts with every data property. In fact, because you are using a Vuex store, you don’t need the data properties at all, just the computed properties.

翻译过来是:
VUE将数据方法中的所有属性绑定到实例的根。它还对计算的属性和方法执行此操作,因此您必须使用不同的名称来避免命名冲突。
您发布的代码中的主要问题是,您与每个数据属性都存在命名冲突。实际上,因为您使用的是Vuex存储,所以根本不需要数据属性,只需要计算的属性。

大概意思就是data中的count和computed中的count命名冲突了,咱们只用到计算属性了,所以data中去掉,他出现的本地也只是为了给{{count}}赋初值而已。

去掉后,看结果:

在这里插入图片描述
其他 方式引用2:
vuex_test.vue中添加:

computed:mapState({
      // count:state => state.count
      count:function (state) {
        return state.count
      }
    })

另外记得引用mapState

import { mapState } from 'vuex'

在这里插入图片描述
剩下一种其实类似于上面,适应于只显示不变更数据的情况:

//辅助函数,映射this.count到$stote.state.count
    // computed:mapState([
    //   "count"
    // ]),

    computed: {
      ...mapState([
        'count'
      ])
    }

注意上面第一种为直接数组的格式,第二种为标准语法,三个点去掉就报错滴
在这里插入图片描述

这就是获取store中的state数据并显示的几种情况,切记,多个computed时只有一个会生效,默认最后一个生效,本文例子记得将原来的覆盖或者注释掉

接下来便是Mutations触发状态的用法:
store.js文件中规定Mutations中几种规则并下方调用

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

// 访问状态对象
const state = {
  count:0
}

// 访问触发状态
const mutations = {
  addNumberByNumbert(state,n){
    state.count += n;
  },
  addNumberByObject(state,obj){
    state.count += (obj.a+obj.b);
  },
  addNumber(){
    state.count ++;
  },
  delNumber(state){
    state.count --;
  }
}

export default new Vuex.Store({
  state,
  mutations

})


vuex_test.vue中进行修改:

<template>
  <div id="app">
    <h2>hello vuex</h2>
    <p>{{count}}</p>
    <p>
      <button @click="$store.commit('addNumberByNumbert',5)">n增数 每次增加n(10)</button>
      <button @click="$store.commit('addNumberByObject',{a:10,b:20})">对象调用 每次增加a(10)+b(20)=30</button>
    </p>
    <p>
      <button @click="addNumber">addNumber</button>
      <button @click="delNumber">delNumber</button>
    </p>
  </div>
</template>
<script>
  import { mapState,mapMutations } from 'vuex'
  export  default {
    name : 'app',
    computed: {
      ...mapState([
        'count'
      ])
    },
    // methods:mapMutations([
    //   'addNumber',
    //   'delNumber'
    // ])
    methods:{
        ...mapMutations([
                       'addNumber',
                       'delNumber'
                     ])
    }
  }
</script>

运行结果:
在这里插入图片描述

getters的用法:
store.js中修改添加:

const getters = {
  count:function(state){
    return state.count += 100
  }
}

export default new Vuex.Store({
  state,
  mutations,
  getters

})

vuex_test.vue中进行修改:

<template>
  <div id="app">
    <h2>hello vuex</h2>
    <p>{{count}}</p>
    <p>
      <button @click="$store.commit('addNumberByNumbert',5)">n增数 每次增加n(10)</button>
      <button @click="$store.commit('addNumberByObject',{a:10,b:20})">对象调用 每次增加a(10)+b(20)=30</button>
    </p>
    <p>
      <button @click="addNumber">addNumber</button>
      <button @click="delNumber">delNumber</button>
    </p>
  </div>
</template>
<script>
  import { mapState,mapMutations,mapGetters } from 'vuex'
  export  default {
    name : 'app',
    computed:{
        ...mapState([
          'count'
        ]),
        ...mapGetters([
          'count'
        ])
    },
    methods:{
        ...mapMutations([
                       'addNumber',
                       'delNumber'
                     ])
    }
  }
</script>

运行结果:
在这里插入图片描述

action异步加载
store.js中添加: getters中暂且设置为0

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

// 访问状态对象
const state = {
  count:0
}

// 访问触发状态
const mutations = {
  addNumberByNumbert(state,n){
    state.count += n;
  },
  addNumberByObject(state,obj){
    state.count += (obj.a+obj.b);
  },
  addNumber(){
    state.count ++;
  },
  delNumber(state){
    state.count --;
  }
}

const getters = {
  count:function(state){
    return state.count += 0
  }
}

const actions = {
  addNumberP(context){
    context.commit('addNumberByObject',{a:5,b:10}),
      setTimeout(() =>{
        context.commit('delNumber')
      },3000)
    console.log("本条语句先与计时器被执行")
  },
  delNumberP({commit}){
    commit('delNumber')
  }
}

export default new Vuex.Store({
  state,
  mutations,
  getters,
  actions

})

vuex_test.vue中进行修改:

<template>
  <div id="app">
    <h2>hello vuex</h2>
    <p>{{count}}</p>
    <p>
      <button @click="$store.commit('addNumberByNumbert',5)">n增数 每次增加n(10)</button>
      <button @click="$store.commit('addNumberByObject',{a:10,b:20})">对象调用 每次增加a(10)+b(20)=30</button>
    </p>
    <p>
      <button @click="addNumber">addNumber</button>
      <button @click="delNumber">delNumber</button>
    </p>
    <p>
      <button @click="addNumberP">actions-addNumberP</button>
      <button @click="delNumberP">actions-delNumberP</button>
    </p>
  </div>
</template>
<script>
  import { mapState,mapMutations,mapGetters,mapActions } from 'vuex'
  export  default {
    name : 'app',
    computed:{
        ...mapState([
          'count'
        ]),
        ...mapGetters([
          'count'
        ])
    },
    methods:{
        ...mapMutations([
                       'addNumber',
                       'delNumber'
                     ]),
        ...mapActions({
            addNumberP:'addNumberP',
            delNumberP:'delNumberP'
          })
      }
  }
</script>

在这里插入图片描述
执行结果:
先计数成为15,然后执行log输出,最后count-1变成14
在这里插入图片描述

附完整代码:
store.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

// 访问状态对象
const state = {
  count:0
}

// 访问触发状态
const mutations = {
  addNumberByNumbert(state,n){
    state.count += n;
  },
  addNumberByObject(state,obj){
    state.count += (obj.a+obj.b);
  },
  addNumber(){
    state.count ++;
  },
  delNumber(state){
    state.count --;
  }
}

const getters = {
  count:function(state){
    return state.count += 0
  }
}

const actions = {
  addNumberP(context){
    context.commit('addNumberByObject',{a:5,b:10}),
      setTimeout(() =>{
        context.commit('delNumber')
      },3000)
    console.log("本条语句先与计时器被执行"+new Date())
  },
  delNumberP({commit}){
    commit('delNumber')
  }
}

export default new Vuex.Store({
  state,
  mutations,
  getters,
  actions

})

vuex_test.vue

<template>
  <div id="app">
    <h2>hello vuex</h2>
    <p>{{count}}</p>
    <p>
      <button @click="$store.commit('addNumberByNumbert',5)">n增数 每次增加n(10)</button>
      <button @click="$store.commit('addNumberByObject',{a:10,b:20})">对象调用 每次增加a(10)+b(20)=30</button>
    </p>
    <p>
      <button @click="addNumber">addNumber</button>
      <button @click="delNumber">delNumber</button>
    </p>
    <p>
      <button @click="addNumberP">actions-addNumberP</button>
      <button @click="delNumberP">actions-delNumberP</button>
    </p>
  </div>
</template>
<script>
  import { mapState,mapMutations,mapGetters,mapActions } from 'vuex'
  export  default {
    name : 'app',
    computed:{
        ...mapState([
          'count'
        ]),
        ...mapGetters([
          'count'
        ])
    },
    methods:{
        ...mapMutations([
                       'addNumber',
                       'delNumber'
                     ]),
        ...mapActions({
            addNumberP:'addNumberP',
            delNumberP:'delNumberP'
          })
      }
  }
</script>

模版的简单用法:
在这里插入图片描述
在这里插入图片描述

运行结果:
在这里插入图片描述

<think>好的,我现在需要回答用户关于Vuexmutationsactions的区别的问题,并且需要从使用、原理、场景等多个方面进行描述。用户之前已经询问过简单的store模式,现在转向Vuex,可能是在项目复杂度增加后遇到了状态管理的问题。我需要详细解释两者的区别,以及为什么Vuex要分成这两个概念。 首先,我需要回忆Vuex的基本结构。Vuex的状态管理包括stategettersmutationsactionsmodules。其中,mutationsactions是核心部分,但它们的职责不同。用户可能混淆了它们的用途,所以需要明确各自的角色。 从使用上看,mutations是同步的,用于直接修改state,而actions可以包含异步操作,通过提交mutations来间接修改state。这个区别很重要,因为直接修改state在严格模式下会报错,而异步操作如果在mutations中进行会导致追踪问题。需要举例说明,比如在actions中调用API,完成后提交mutation。 原理方面,Vuex内部通过提交mutation来记录状态变更,每个mutation都有一个类型和一个处理函数,这使得状态变更加上可追踪,如时间旅行调试。而actions则是封装业务逻辑,处理异步操作,然后通过commit触发mutations。这里需要解释为什么异步操作不能放在mutations中,可能是因为mutation必须是同步的,以确保状态变化的可追踪性。 场景方面,当需要执行异步操作(如API请求)或复杂的业务逻辑时,应该使用actions。而直接修改state的同步操作则使用mutations。比如,用户点击按钮触发一个动作,这个动作需要从服务器获取数据,这时候应该用action,获取数据后再提交mutation来更新state。 可能用户还会疑惑,为什么需要分成两个步骤,直接在一个步骤里处理异步然后修改state不行吗?这时候需要解释Vuex的设计哲学,强调可维护性和调试能力。分开mutationsactions可以让状态变化更加清晰,每个变化都有记录,便于追踪和调试。 另外,需要提到在组件中如何调用,比如通过this.$store.commit触发mutation,或者通过this.$store.dispatch触发action。同时,可以提到mapMutations和mapActions的辅助函数,方便在组件中使用。 可能还需要对比其他状态管理库,比如Redux中的reducers和actions,说明Vuex的设计与其他库的相似之处和差异,帮助用户理解其设计思路。 最后,总结为什么分成两部分:为了分离同步和异步操作,保持状态变更加可预测和可追踪,同时处理异步逻辑时不破坏状态的同步更新机制。这样设计有利于大型应用的维护和调试,尤其是在需要跟踪状态变化或使用devtools时。 需要检查是否有遗漏点,比如严格模式下的限制,或者在actions中可以处理多个mutations的情况。同时,要确保语言通俗易懂,避免过于技术化的术语,但又要准确无误。</think>在 Vuex 中,`mutations` 和 `actions` 是状态管理的两个核心概念,它们的区别主要体现在以下方面: -- ### 🔧 **核心区别总结** | | Mutations | Actions | |----------------|------------------------------------|------------------------------------| | **职责定位** | 唯一修改 State 的方式 | 处理业务逻辑和异步操作 | | **同步性** | 必须同步执行 | 可以异步操作 | | **触发方式** | `commit` | `dispatch` | | **调试支持** | 在 DevTools 中记录完整变更轨迹 | 不直接记录状态变更 | | **代码组织** | 关注 **怎么改数据** | 关注 **什么时候改数据** | -- ### 🛠 **使用方式对比** #### **1. Mutations** ```javascript // store.js mutations: { SET_COUNT(state, value) { state.count = value // 直接修改状态 } } // 组件中调用 this.$store.commit('SET_COUNT', 10) ``` #### **2. Actions** ```javascript // store.js actions: { fetchData({ commit }) { axios.get('/api/data').then(res => { commit('SET_COUNT', res.data.count) // 提交 mutation }) } } // 组件中调用 this.$store.dispatch('fetchData') ``` --- ### 🔬 **原理层面差异** #### **Mutations** - **原子操作**:每个 mutation 都是最小化的状态修改单元 - **同步保证**:通过同步执行确保 DevTools 能准确捕捉状态快照 - **直接修改**:通过闭包直接访问 state 对象进行修改 #### **Actions** - **逻辑容器**:可包含任意异步操作和复杂逻辑 - **间接修改**:通过 `commit` 间接触发 mutation - **上下文访问**:可获取完整的 store 上下文(`commit`, `state`, `getters` 等) --- ### 🎯 **适用场景** #### **使用 Mutations 的场景** - 简单的同步状态修改(如计数器增减) - 需要 DevTools 完整追踪状态变更历史 - 需要保证状态修改的原子性和可预测性 #### **使用 Actions 的场景** - 需要调用 API 或异步操作(如数据请求) - 需要处理复杂业务逻辑(如多个 mutation 的顺序调用) - 需要访问其他模块的状态或操作 - 需要防抖/节流等控制逻辑的场景 --- ### 💡 **为什么要分两个层?** #### 1. **职责分离原则** - **Mutations**:专注于 **状态修改**(What to change) - **Actions**:专注于 **业务逻辑**(When/How to change) #### 2. **调试能力保障** - Mutation 的同步特性使得 Vuex DevTools 可以生成准确的状态快照 - 如果异步操作放在 mutation 中,会导致状态变更无法被正确追踪 #### 3. **可维护性提升** ```javascript // 典型工作流程 Component → Dispatch Action → Commit Mutation → Modify State → Update View ``` 这种分层使代码更容易维护: - 视图层不直接操作状态 - 异步逻辑与状态修改解耦 - 业务逻辑集中管理 #### 4. **异步处理标准化** ```javascript // 错误示例:在 mutation 中写异步 mutations: { async BAD_PRACTICE(state) { const data = await fetchData() // ❌ 破坏状态追踪 state.data = data } } ``` --- ### 🌰 **真实场景示例** #### 电商购物车流程 ```javascript actions: { async checkout({ commit, state }) { commit('SET_LOADING', true) // 同步修改加载状态 try { const res = await api.checkout(state.cart) commit('CLEAR_CART') // 清空购物车 commit('ADD_ORDER', res.data) // 添加订单记录 } catch (error) { commit('SET_ERROR', error.message) // 错误处理 } commit('SET_LOADING', false) } } ``` --- ### ⚠️ **注意事项** 1. 严格模式下直接修改 state 会报错(必须通过 mutation) 2. Action 中可以返回 Promise 实现链式调用 ```javascript this.$store.dispatch('actionA').then(() => { ... }) ``` 3. 大型项目建议使用 `mapMutations` 和 `mapActions` 辅助函数 4. 在 Vue 3 的 Pinia 中这两个概念被合并为统一的 `actions` --- ### 📚 **总结建议** | **维度** | **选择依据** | |--|----------------------------------------------------------------------------| | **何时用 Mutation** | 只需要简单同步修改状态时 | | **何时用 Action** | 需要处理异步、复杂逻辑或组合多个 mutation 时 | | **性能考量** | 频繁的简单操作直接用 mutation(减少中间层开销) | | **代码规范** | 强制通过 action 调用 mutation 是更好的实践(即使当前是同步操作) | 这种分层设计体现了软件工程的 **单一职责原则** 和 **关注点分离**,虽然增加了初期学习成本,但显著提升了中大型应用的维护性和可调试性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值