本文对 Vuex 官方文档从新组织编排,但愿正在学习 Vue 的同窗们,在阅读后可快速使用 Vuex。html
开始使用 Vuex,把状态拿到应用外部管理,Vuex管这个管理状态的玩意叫 Store,一个彻底独立的应用,他只负责状态管理。尝试把 Vuex 应用和 Vue 应用划清界限,vue
所谓状态管理,无非就是定义状态,修改状态。jquery
在 Vuex 里定义状态,咱们须要 new 一个 Store 出来,每个 Vuex 应用的核心就是 store(仓库)。git
// 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 没有任何区别,github
下面修改状态。vuex
若是你想快点用上 Vuex,你能够在组件里直接修改 store 里的 state,(直接修改的意思就是,用点操做修改)api
state.count = 2;
复制代码
虽然这样能够正常工做,但在严格模式下会报错,更改 store 中的状态的惟一方法应该是提交 mutation。架构
开启严格模式,仅需在建立 store 的时候传入 strict: true
:app
const store = new Vuex.Store({
strict: true
});
复制代码
在严格模式下,不管什么时候发生了状态变动且不是由 mutation 函数引发的,将会抛出错误。异步
更改 Vuex 的 store 中的状态的惟一方法是提交 mutation。
在Vuex.Store的构造器选项中,有一个 mutation 选项,这个选项就像是事件注册:key 是一个字符串表示 mutation 的类型(type),value 是一个回调函数(handler),
这个回调函数就是咱们实际进行状态更改的地方,它有两个入参,第一个参数是 state,就是 store 里的 state;第二个参数是 Payload,这是提交 mutation 时候额外传入的参数。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment(state) {
state.count++; // 变动状态
}
}
});
复制代码
上面定义了 mutation,要唤醒一个 mutation handler,惟一的接口是store.commit
,若是你熟悉事件监听,commit 就相似于 Vue 的$emit
,jquery 的trigger
。
可想而知,commit 方法传参必须至少有一个能区分 mutation 的惟一标识,这个标识就是 type
,
当 commit 的参数是一个对象的时候,对象里必需要有 type 字段,除了 type 字段,你还能够添加额外任意字段为载荷(payload)。
// 对象风格的提交方式
store.commit({
type: "increment",
amount: 10
});
复制代码
把 type 和 payload 分开也是个不错的选择,能够把 type 单独拿出来当第一个参数,以commit(type,[payload])
的形式提交,官方称此为以载荷形式提交。
store.commit("increment");
store.commit("increment", 10);
store.commit("increment", { count: 2 });
复制代码
在大多数状况下,payload 应该是一个对象,这样能够包含多个字段而且记录的 mutation 会更易读。
到这里咱们已经知道如何定义 mutation 和提交 mutation 了,commit 接口很简单,但在哪里使用呢?有两个地方用
store.commit
。状态管理不过就是定义 state 和修改 state,mutation 已经能够修改 state 了,为何还须要 action?
在 Vuex 中,mutation 都是同步事务,在 mutation 中混合异步调用会致使你的程序很难调试。
例如,当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道何时回调和哪一个先回调呢?这就是为何咱们要区分这两个概念。
Action 确实和 mutation 很相似,不一样在于:
注册 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 经过 store.dispatch
方法触发:
// 以载荷形式分发
store.dispatch("incrementAsync", {
amount: 10
});
// 以对象形式分发
store.dispatch({
type: "incrementAsync",
amount: 10
});
复制代码
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 方式修改便可。
有两种方式。
抛弃v-model
指令,本身去实现v-model
双向绑定,很是简单,
input
标签的value
属性绑定对应从 store 中映射来的计算属性,<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
}
}
复制代码
若是坚持使用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 架构来看,三个核心,State,Mutation,Action,已经足够了,总结一下其实很简单,同步 commit mutation,异步 dispatch action。
记住这些原则,开始动手把 Vuex 集成到 Vue 项目中吧,至于一些更多的概念,都是些锦上添花的东西。
Vuex 的经常使用 api 上面都涉及到了,使用思路就是
new Vuex.Store({})
建立一个 store 实例,定义 state,getters,actions,mutations。store.commit
提交 mutation
,使用store.dispatch
分发actions
。如今状态管理的部分已经所有由 store 实例去管理了,如何在 Vue 组件中使用 store,很是简单,一点也不神奇。
在store.js
文件里咱们建立了 store 实例并将其导出了(export),按照 js 模块化的知识,咱们只要在须要的地方导入它就能够直接使用了。
能够在须要的组件里直接引入,就像引入一个普通的 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 的组件中须要频繁地导入。
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
复制代码
既然是辅助,就不是必须的东西,辅助函数能够方便的把 Vuex 中的 state,getter,mutation,action 映射到组件,方便调用。
若是你很喜欢 Vue 提供的计算属性(computed),Vuex 容许在 store 中定义“getter”(能够认为是 store 的计算属性)。
但对状态管理来讲,Getter 其实并非必须的,若是你须要的话,能够查看文档使用。
模块化并非状态管理的概念,也不是必须的,但若是应用十分复杂,将 store 分割成模块(module)会是一个必经之路,Vuex 提供了模块化选项,须要可查看文档。
原文连接:欢迎github来颗star github.com/liangzai92/…