vuex 使用总结(详解)

vuex 使用总结(详解)什么情况下我应该使用 Vuex Vuex 可以帮助我们管理共享状态 并附带了更多的概念和框架 这需要对短期和长期效益进行权衡 如果您不打算开发大型单页应用 使用 Vuex 可能是繁琐冗余的 确实是如此 如果您的应用够简单 您最好不要使用 Vuex 一个简单的 store 模式就足够您所需了 但是 如果您需要构建一个中大型单页应用 您很可能会考虑如何更好地在组件外部管理状态 Vuex 将会成

如果之前未使用过 vuex 请务必先看一下参考

参考:

  • vue中store存储store.commit和store.dispatch的区别及使用
  • vuex的安装和简单使用

什么情况下应该使用 Vuex?

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

如果不打算开发大型单页应用,应用够简单,最好不要使用 Vuex。一个简单的 store 模式就足够了。但是,如果需要构建一个中大型单页应用,就要考虑如何更好地在组件外部管理状态,Vuex 是不错的选择。

使用

在 Vue 的单页面应用中使用,需要使用Vue.use(Vuex)调用插件。将其注入到Vue根实例中。

import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ 
    state: { 
    count: 0 }, getter: { 
    doneTodos: (state, getters) => { 
    return state.todos.filter(todo => todo.done) } }, mutations: { 
    increment (state, payload) { 
    state.count++ } }, actions: { 
    addCount(context) { 
    // 可以包含异步操作 // context 是一个与 store 实例具有相同方法和属性的 context 对象 } } }) // 注入到根实例 new Vue({ 
    el: '#app', // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件 store, template: ' 
   ', components: { 
    App } }) 

然后改变状态:

this.$store.commit('increment') 

核心

State,Getter,Mutation,Action,Module,

Vuex 主要有四部分:

  1. state:包含了store中存储的各个状态。
  2. getter: 类似于 Vue 中的计算属性,根据其他 getter 或 state 计算返回值。
  3. mutation: 一组方法,是改变store中状态的执行者,只能是同步操作
  4. action: 一组方法,其中可以包含异步操作

State

Vuex 使用 state 来存储应用中需要共享的状态。为了能让 Vue 组件在 state更改后也随着更改,需要基于state 创建计算属性。

// 创建一个 Counter 组件 const Counter = { 
    template: ` 
    
{ { count }}
`
, computed: { count () { return this.$store.state.count // count 为某个状态 } } }

Getter

类似于 Vue 中的 计算属性(可以认为是 store 的计算属性),getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

Getter 方法接受state作为其第一个参数:

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 会暴露为 store.getters 对象,可以以属性的形式访问这些值:

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] 

Getter 方法也接受 state和其他getters作为前两个参数。

getters: { 
    // ... doneTodosCount: (state, getters) => { 
    return getters.doneTodos.length } } 
store.getters.doneTodosCount // -> 1 

我们可以很容易地在任何组件中使用它:

computed: { 
    doneTodosCount () { 
    return this.$store.getters.doneTodosCount } } 

注意: getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。

通过方法访问

也可以通过让 getter 返回一个函数,来实现给 getter 传参。在对 store 里的数组进行查询时非常有用。

getters: { 
    // ... getTodoById: (state) => (id) => { 
    return state.todos.find(todo => todo.id === id) } } 
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false } 

注意: getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。也就是说,前面两个都是状态值本身,mutations才是改变状态的执行者。

注意:mutations只能是同步地更改状态。

Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

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

调用 store.commit 方法:

store.commit('increment') 

提交载荷(Payload)

// ... mutations: { 
    increment (state, n) { 
    state.count += n } } 
this.$store.commit('increment', 10) 

其中,第一个参数是state,后面的参数是向 store.commit 传入的额外的参数,即 mutation 的 载荷(payload)

store.commit方法的第一个参数是要发起的mutation类型名称,后面的参数均当做额外数据传入mutation定义的方法中。

规范的发起mutation的方式如下:

// 以载荷形式 store.commit('increment'{ 
    amount: 10 //这是额外的参数 }) // 或者使用对象风格的提交方式 store.commit({ 
    type: 'increment', amount: 10 //这是额外的参数 }) 

额外的参数会封装进一个对象,作为第二个参数传入mutation定义的方法中。

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

Action

想要异步地更改状态,就需要使用actionaction并不直接改变state,而是发起mutation

注册一个简单的 action:

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

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。

实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit 很多次的时候):

actions: { 
    increment ({ 
    commit }) { 
    commit('increment') } } 

在action内部执行异步操作:

actions: { 
    incrementAsync ({ 
    commit }) { 
    setTimeout(() => { 
    commit('increment') }, 1000) } } 

发起action的方法形式和发起mutation一样,只是换了个名字dispatch

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

Actions 支持同样的载荷方式和对象方式进行分发

Action处理异步的正确使用方式

想要使用action处理异步工作很简单,只需要将异步操作放到action中执行(如上面代码中的setTimeout)。

要想在异步操作完成后继续进行相应的流程操作,有两种方式:

  1. store.dispatch返回相应action的执行结果,而action的处理函数返回的就是Promise,所以store.dispatch仍然返回一个Promise。
    actions: { 
          actionA ({ 
          commit }) { 
          return new Promise((resolve, reject) => { 
          setTimeout(() => { 
          commit('someMutation') resolve() }, 1000) }) } } 

    现在可以写成:

    store.dispatch('actionA').then(() => { 
          // ... }) 

    在另外一个 action 中也可以:

    actions: { 
          // ... actionB ({ 
          dispatch, commit }) { 
          return dispatch('actionA').then(() => { 
          commit('someOtherMutation') }) } } 
  2. 利用async/await 进行组合action。代码更加简洁。
    // 假设 getData() 和 getOtherData() 返回的是 Promise actions: { 
          async actionA ({ 
          commit }) { 
          commit('gotData', await getData()) }, async actionB ({ 
          dispatch, commit }) { 
          await dispatch('actionA') // 等待 actionA 完成 commit('gotOtherData', await getOtherData()) } } 

    一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。

Action与Mutation的区别

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作,而Mutation只能且必须是同步操作。

Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

这时我们可以将 store 分割为模块(module),每个模块拥有自己的 stategettersmutationsactions 、甚至是嵌套子模块——从上至下进行同样方式的分割。

代码示例:

const moduleA = { 
    state: { 
    ... }, mutations: { 
    ... }, actions: { 
    ... }, getters: { 
    ... } } const moduleB = { 
    state: { 
    ... }, mutations: { 
    ... }, actions: { 
    ... } } const store = new Vuex.Store({ 
    modules: { 
    a: moduleA, b: moduleB } }) store.state.a // -> moduleA 的状态 store.state.b // -> moduleB 的状态 

嵌套子模块

首先创建子模块的文件:

// products.js // initial state const state = { 
    added: [], checkoutStatus: null } // getters const getters = { 
    checkoutStatus: state => state.checkoutStatus } // actions const actions = { 
    checkout ({ 
    commit, state }, products) { 
    } } // mutations const mutations = { 
    mutation1 (state, { 
    id }) { 
    } } export default { 
    state, getters, actions, mutations } 

然后在总模块中引入:

import Vuex from 'vuex' import products from './modules/products' //引入子模块 Vue.use(Vuex) export default new Vuex.Store({ 
    modules: { 
    products // 添加进模块中 } }) 

各个模块与 Vue 组件结合

stategetter结合进组件需要使用计算属性

computed: { 
    count () { 
    return this.$store.state.count // 或者 return this.$store.getter.count } } 

mutationaction结合进组件需要在methods中调用this.$store.commit()或者this.$store.commit():

methods: { 
    changeDate () { 
    this.$store.commit('change'); }, changeDateAsync () { 
    this.$store.commit('changeAsync'); } } 

为了简便起见,Vuex 提供了四个辅助函数方法用来方便的将这些功能结合进组件。

  1. mapState
  2. mapGetters
  3. mapMutations
  4. mapActions

示例代码:

import { 
    mapState, mapGetters, mapMutations, mapActions } from 'vuex' export default { 
    // ... computed: { 
    localComputed () { 
    /* ... */ }, // 使用对象展开运算符将此对象混入外部对象中 ...mapState({ 
    // 为了能够使用 `this` 获取局部状态,必须使用常规函数 count(state) { 
    return state.count + this.localCount } }), ...mapGetters({ 
    getterCount(state, getters) { 
    return state.count + this.localCount } }) } methods: { 
    ...mapMutations({ 
    // 如果想将一个属性另取一个名字,使用以下形式。注意这是写在对象中 add: 'increment' // 将 `this.add()` 映射为`this.$store.commit('increment')` }), ...mapActions({ 
    add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')` }) } } 

如果结合进组件之后不想改变名字,可以直接使用数组的方式。

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

为何使用展开运算符:mapState 等四个函数返回的都是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是有了对象展开运算符,我们就可以进行简化写法。

Vuex的使用差不多就是这样。还有命名空间的概念,大型应用会使用。可以点这里查看。

附:项目结构

Vuex 不限制代码结构。但是规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。

只要你遵守以上规则,如何组织代码随你便。如果 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/221101.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午6:54
下一篇 2026年3月17日 下午6:55


相关推荐

  • 继电器驱动电路(各种单片机、CD4013触发器驱动电路图)

    继电器驱动电路(各种单片机、CD4013触发器驱动电路图)继电器工作原理详解(附3种驱动电路图)2019-09-1216:10继电器继电器是一种电子控制器件,它具有控制系统(又称输入回路)和被控制系统(又称输出回路),通常应用于自动控制电路中,它实际上是用较小的电流去控制较大电流的一种“自动开关”。故在电路中起着自动调节、安全保护、转换电路等作用。继电器的继电特性继电器的输入信号x从零连续增加达到衔铁开始吸合时的动作值xx,继电器的输出信号立刻从y=0跳跃y=ym,即常开触点从断到通。一旦触点闭合,输入量x继续增大,输出信号.

    2022年6月24日
    31
  • 500元上门安装,还是大厂创企一键部署?OpenClaw催生的中间人经济

    500元上门安装,还是大厂创企一键部署?OpenClaw催生的中间人经济

    2026年3月13日
    1
  • CIDR的特殊性

    CIDR的特殊性

    2022年3月8日
    49
  • 关于sql和MySQL的语句执行顺序(必看)「建议收藏」

    关于sql和MySQL的语句执行顺序(必看)「建议收藏」今天遇到一个问题就是mysql中insertinto和update以及delete语句中能使用as别名吗?目前还在查看,但是在查阅资料时发现了一些有益的知识,给大家分享一下,就是关于sql以及MySQL语句执行顺序:sql和mysql执行顺序,发现内部机制是一样的。最大区别是在别名的引用上。一、sql执行顺序(1)from(3)join(2)on(4)where…

    2022年6月21日
    22
  • ISAPI详细分析

    ISAPI详细分析ISAPI 包括扩展和过滤器两种形式 都可以利用来开发动态动态 Web 内容 ISAPI 扩展和过滤器都以 DLL 形式实现 供 IIS 进程调用 扩展按规范必须实现两个函数接口 GetExtension HttpExtensio 和一个可选函数接口 TerminateExt 扩展和 Web 服务器中特定虚拟目录下的文件类型关联 可以和特定的文件后缀 比如 txt 关联 也

    2026年3月17日
    2
  • A星算法说明「建议收藏」

    A星算法说明「建议收藏」A*算法说明文章目录前言原理说明如何构造h(n)h(n)h(n)一、欧氏距离二、曼哈顿距离三、其他关于g(n)g(n)g(n)路况设置如何实现完整的流程核心代码a_star.ha_star.cppmap_matrix.hmap_matrix.cpp代码使用示例GUI程序下载链接GUI程序使用说明前言  因为最近要写一个毕业设计,有用到自动寻路的功能,因为我要在一个机器里跑算法然后控制机器人自动按照路线到达目的地,所以用Python等解释型语言或Unity等游戏引擎写这个算法都不太合适,我使用的机器要尽

    2022年10月6日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号