Vue - vuex

Vue Vuex 是一种状态管理模式,用于解决多个组件间共享状态的问题。核心概念包括响应式的state、getters、mutations和actions。State用于存储共享数据,通过计算属性、mapState辅助函数等方式在组件中获取。Getters允许派生状态,避免重复代码。Mutations是同步更改状态的唯一方法,通常配合载荷使用。Actions则允许执行异步操作并提交mutations。通过这些机制,Vuex使状态管理变得高效且易于维护。

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

Vuex状态管理模式

当两个或两个以上的组件需要共用同一个数据源时,使用Vuex对数据的管理会变得更见简单轻松(相比localStorage、路由传参)

响应式

Vuex的状态存储是响应式的。当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件(视图)也会相应地得到高效更新。

核心概念

  • state
  • getters
  • mutations
  • actions

state

存放共享的数据,需要提前声明

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  }
})

// main.js,注入store,每个组件都可以通过this.$store访问store
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store/store'

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

如何在组件中获取state中的数据?

  • 直接调用
let count = this.$store.state.count
this.count = count

<span>{{count}}</span>
  • 计算属性
// 由于Vuex的状态存储是响应式的,从store实例中读取状态最简单的方法就是在计算属性中返回某个状态
// 每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM
computed: {
  count () {
    return this.$store.state.count
  }
}

<span>{{count}}</span>
  • mapState辅助函数
// 当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。
// 为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:
// 不用写那么多次this.$store.state.xxx
import { mapState } from 'vuex'

export default {
  // mapState函数传入一个对象
  computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,
    
    // 传字符串参数 'count' 等同于 `state => state.count`。取别名~
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

// mapState函数传一个字符串数组
// 数组中的变量名与state中设置时保持一致
computed: mapState([
  // 映射 this.count 为 this.$store.state.count
  'count'
])
  • 与局部计算属性混合使用
computed: {
  localComputed ()
  // 使用对象展开运算符将此对象混入到外部对象中,mapState可传入对象/数组的形式
  ...mapState({
    // ...
  })
}

Getter

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤/计数/查询

computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo => todo.done).length
  }
}

如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。(同一个功能多个组件中都要写,不利于维护)

Vuex 允许我们在store中定义“getter”(可以认为是 store 的计算属性),就像计算属性一样,getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    },
    // 也可以接受其他 getter 作为第二个参数
    doneTodosCount: (state, getters) => {
      return getters.doneTodos.length
    },
    // 传递参数
    getTodoById: (state) => (id) => {
      return state.todos.find(todo => todo.id === id)
    }
  }
})

调用方式

// 组件调用
this.$store.getters.doneTodos()
this.$store.getters.getTodoById(2)

// mapGetters辅助函数
// mapGetters可传入数组或者对象(对象的形式可以设置别名)
import { mapGetters } from 'vuex'

export default {
  computed: {
    // 使用对象展开运算符将 getter 混入 computed 对象中
    // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
    ...mapGetters({
      doneCount: 'doneTodosCount'
    })
  }
}

//
// mapGetters传入数组
...mapGetters([
  'doneTodosCount', // 通过this.doneTodosCount()调用
  'anotherGetter',  // 通过this.anotherGetter()调用
])

Mutation

更改Vuex的store中的状态的唯一方法是提交mutation。可以把mutation理解成函数,且必须是同步函数,因为devtools需要捕捉到前一状态和后一状态的快照,异步函数中的回调让这不可能完成

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    // 修改状态
    increment (state) {
      state.count++
    }
  }
})

// 组件调用
this.$store.commit('increment')

载荷(payload)

// 传递参数
mutations: {
  increment (state, n) {
    state.count += n
  }
}

// 组件调用
this.$store.commit('increment', 10)

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

// 组件调用
this.$store.commit('increment', {
  amount: 10
})

// 对象风格的提交方式,也写成下面这种形式
this.$store.commit({
  type: 'increment',
  amount: 10
})

mapMutations 辅助函数

方便调用,mapMutations函数可以对象或者数组形式传入参数

import { mapMutations } from 'vuex'

export default {
  methods: {
    // 传入数组
    ...mapMutations([
      // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
      'increment',
      // `mapMutations` 也支持载荷。将 `this.incrementBy(amount)` 映射 `this.$store.commit('incrementBy', amount)`
      'incrementBy'
    ]),
    // 传入对象(可设置别名)
    ...mapMutations({
      // 将 `this.add()` 映射为 `this.$store.commit('increment')`
      add: 'increment' 
    })
  }
}

// 组件中调用
this.increment()
this.incrementBy(amount)
this.add()

使用常量替代 Mutation 事件类型

用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'

// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

// home.js
import { SOME_MUTATION } from './../../store/mutation-types'

methods: {
  handle () {
    this.$sotre.commit({
      type: SOME_MUTATION,
      amount: 10
    })
  }
}

Action

  • Action 提交的是 mutation,而不是直接变更状态
  • Action 可以包含任意异步操作

mutation是不能进行异步操作的,比如根据一个接口的返回值来修改state,就需要用到action(action也是调用mutation来修改state,只是在action中可以进行异步操作,例如接口请求)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment') // this.$store.dispatch('increment ')
    }
  }
})

// ES2015 的 参数解构 来简化代码
actions: {
  increment ({ commit }) {
    commit('increment')
  }
}

// 异步操作
actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

调用方式

// 以载荷形式分发
this.$store.dispatch('incrementAsync', {
  amount: 10 // amount传入mutation
})

// 以对象形式分发
this.$store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

// mapActions辅助函数
import { mapActions } from 'vuex'

export default {
  methods: {
    ...mapActions([
      // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
      'increment', 
      // `mapActions`也支持载荷:
      // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
      'incrementBy' 
    ]),
    ...mapActions({
      // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
      add: 'increment'
    })
  }
}

组合 Action

处理异步action

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  },
  // 在一个action中调用另一个action
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  },
  // 使用await / async
  async actionC ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

// 组件中调用
this.$store.dispatch('actionA').then(() => {
  // ...
})

this.$store.dispatch('actionB')
this.$store.dispatch('actionC')

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值