Vuex 快速使用

本文对 Vuex 官方文档重新组织编排,希望正在学习 Vue 的同学们,在阅读后可快速使用 Vuex。

开始使用 Vuex,把状态拿到应用外部管理,Vuex 管这个管理状态的玩意叫 Store,一个完全独立的应用,他只负责状态管理。尝试把 Vuex 应用和 Vue 应用划清界限,

  • 一个 Vuex 应用,做状态管理,可以理解是 Model 层
  • 一个 Vue 应用,仅负责数据展示,纯纯的 View 层

Vuex 应用

所谓状态管理,无非就是定义状态,修改状态

定义 state

在 Vuex 里定义状态,我们需要 new 一个 Store 出来,每一个 Vuex 应用的核心就是 store(仓库)。

// store.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0
  }
});

export default store;

以上代码创建了一个 store,store.state 里定义了状态。与在 Vue 里定义 data 没有任何区别,

下面修改状态。

直接修改

如果你想快点用上 Vuex,你可以在组件里直接修改 store 里的 state,(直接修改的意思就是,用点操作修改)

state.count = 2;

虽然这样可以正常工作,但在严格模式下会报错,更改 store 中的状态的唯一方法应该是提交 mutation

严格模式

开启严格模式,仅需在创建 store 的时候传入 strict: true

const store = new Vuex.Store({
  strict: true
});

在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。

mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。

Vuex.Store的构造器选项中,有一个 mutation 选项,这个选项就像是事件注册:key 是一个字符串表示 mutation 的类型(type),value 是一个回调函数(handler),

这个回调函数就是我们实际进行状态更改的地方,它有两个入参,第一个参数是 state,就是 store 里的 state;第二个参数是 Payload,这是提交 mutation 时候额外传入的参数。

定义 mutation

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

提交 mutation

上面定义了 mutation,要唤醒一个 mutation handler,唯一的接口是store.commit,如果你熟悉事件监听,commit 就类似于 Vue 的$emit,jquery 的trigger

可想而知,commit 方法传参必须至少有一个能区分 mutation 的唯一标识,这个标识就是 type

commit 参数是对象

当 commit 的参数是一个对象的时候,对象里必须要有 type 字段,除了 type 字段,你还可以添加额外任意字段为载荷(payload)。

// 对象风格的提交方式
store.commit({
  type: "increment",
  amount: 10
});

type 做第一参数

把 type 和 payload 分开也是个不错的选择,可以把 type 单独拿出来当第一个参数,以commit(type,[payload])的形式提交,官方称此为以载荷形式提交

store.commit("increment");
store.commit("increment", 10);
store.commit("increment", { count: 2 });

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

到这里我们已经知道如何定义 mutation 和提交 mutation 了,commit 接口很简单,但在哪里使用呢?有两个地方用

  • Vue 应用的组件里,在组件里调用store.commit
  • 还有 store 的 action 里。

Action

状态管理不过就是定义 state 和修改 state,mutation 已经可以修改 state 了,为什么还需要 action?

同步和异步

在 Vuex 中,mutation 都是同步事务,在 mutation 中混合异步调用会导致你的程序很难调试。

例如,当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道什么时候回调和哪个先回调呢?这就是为什么我们要区分这两个概念。

Action 确实和 mutation 很类似,不同在于:

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

注册 action:

注册 action 就跟定义 mutation 一样,除了 handler 的入参不同。

Action 函数的入参是一个与 store 实例具有相同方法和属性的 context 对象。因此可以

  • context.commit 提交一个 mutation,
  • context.state获取 state。
  • context.getters 获取 getters。
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    increment(context) {
      context.commit("increment");
    }
  }
});

分发 Action

Action 通过 store.dispatch方法触发:

// 以载荷形式分发
store.dispatch("incrementAsync", {
  amount: 10
});

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

组合 Action

store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise,因此,通过 async / await,很方便控制流程

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

表单处理

表单的问题在于使用v-model时候,v-model会试图直接修改 state,而 Vuex 在严格模式下是不允许直接修改 state 的。

很容易解决,只要我们把修改 state 的行为按 Vuex 的要求以commit mutation 方式修改即可。

有两种方式。

用“Vuex 的思维”解决

抛弃v-model指令,自己去实现v-model双向绑定,非常简单,

  • input标签的value属性绑定对应从 store 中映射来的计算属性,
  • 监听 input 事件,用 commit mutation 的方式,修改 store 里的 state。
<input :value="message" @input="updateMessage" />
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},

methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}

store 中的 mutation 函数:

mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}

用 Vue 计算属性解决

如果坚持使用v-model,可以在 Vue 里对v-model绑定的计算属性设置 set 行为。

<input v-model="message" />
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}

使用 Vuex 进行状态管理,到此结束。

从 flux 架构来看,三个核心,StateMutationAction,已经足够了,总结一下其实很简单,同步 commit mutation,异步 dispatch action

核心原则

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

记住这些原则,开始动手把 Vuex 集成到 Vue 项目中吧,至于一些更多的概念,都是些锦上添花的东西。

Vue 应用使用 Vuex

Vuex 的常用 api 上面都涉及到了,使用思路就是

  • 定义状态:通过new Vuex.Store({})创建一个 store 实例,定义 state,getters,actions,mutations。
  • 改变状态:使用store.commit提交 mutation,使用store.dispatch分发actions

现在状态管理的部分已经全部由 store 实例去管理了,如何在 Vue 组件中使用 store,非常简单,一点也不神奇。

store.js文件里我们创建了 store 实例并将其导出了(export),按照 js 模块化的知识,我们只要在需要的地方导入它就可以直接使用了。

组件引入 store

可以在需要的组件里直接引入,就像引入一个普通的 js 一样。

import store from "path/to/store.js";

组件中引入了 store,就可以直接通过store.state引用 state,以及直接使用 store 实例简洁的 api,真的就是这么简单。

store.state.count = 2; // 直接修改,并不建议
store.commit(); // 在 store 中调用 mutation
store.dispatch("increment"); // 在 store 中分发 action

我们往往需要在 Vue 组件的template中展示状态,由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:

import store from "path/to/store.js";
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count() {
      return store.state.count;
    }
  }
};

每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。

其实到现在为止,对于在 Vue 中使用 Vuex 就已经足够了。下面的东西其实可以不用看了,但 Vuex 还是提供了一些方便我们使用的方式,锦上添花。

组件中引入的方式,缺点:这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用 state 的组件中需要频繁地导入。

全局引入 store

Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中:

// app.js
import Vue from "vue";

import store from "path/to/store.js";

const app = new Vue({
  store // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
});

通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。这样我们就不用每个组件单独引入了。

this.$store.state.count = 2; // 直接修改,并不建议
this.$store.commit("", {}); // 直接在组件中提交 mutation
this.$store.dispatch("increment"); // 在组件中分发 action

组件绑定的辅助函数

  • mapState
  • mapGetters
  • mapMutations
  • mapActions

既然是辅助,就不是必须的东西,辅助函数可以方便的把 Vuex 中的 state,getter,mutation,action 映射到组件,方便调用。

更多概念

Getter

如果你很喜欢 Vue 提供的计算属性(computed),Vuex 允许在 store 中定义“getter”(可以认为是 store 的计算属性)。

但对状态管理来说,Getter 其实并不是必须的,如果你需要的话,可以查看文档使用。

Module

模块化并不是状态管理的概念,也不是必须的,但如果应用十分复杂,将 store 分割成模块(module)会是一个必经之路,Vuex 提供了模块化选项,需要可查看文档。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容

  • 安装 npm npm install vuex --save 在一个模块化的打包系统中,您必须显式地通过Vue.u...
    萧玄辞阅读 2,932评论 0 7
  • Vuex 概念篇 Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式...
    Junting阅读 3,073评论 0 43
  • State 单一状态树 Vuex使用单一状态树——用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据...
    oWSQo阅读 1,091评论 0 0
  • Vuex是什么? Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件...
    萧玄辞阅读 3,113评论 0 6
  • 上一章总结了 Vuex 的框架原理,这一章我们将从 Vuex 的入口文件开始,分步骤阅读和解析源码。由于 Vuex...
    你的肖同学阅读 1,785评论 3 16