在单页应用的开发中,Vue全家桶的Vuex和React全家桶的Redux都是统一的状态管理工具/模式。它们是全局的,简单来讲,就是在store中定义一个数据,就能够在整个项目中获取、修改,而且修改会获得全局的响应变动;html
这些状态管理工具适用于中大型项目,在小型项目会使得项目变得繁杂;vue
先上一张Vuex官网的架构图:vuex
Vuex使用的是单一状态树,即state是一个对象,包含了全部定义的数据。redux
在单一的模块module.js中,咱们能够这样定义一个数据:api
const person = {
state: {
name: "tom",
age: 20
}
};
export default person;
复制代码
而后由index.js统一引入,实例化一个新的store:缓存
import Vue from "vue";
import Vuex from "vuex";
import person from "@/store/modules/module";
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
person
}
});
export default store;
复制代码
最后在main.js中注入全局:架构
import Vue from "vue";
import store from "@/store";
new Vue({
store
});
复制代码
咱们须要person的信息时,使用this.$store.state.person
便可获取。异步
单当统一组件屡次须要person、person1的信息时,这时候使用mapState函数就很很方便。其中mapState中的函数接收的参数是该模块局部状态对象state。async
// vuex自带的mapState api
import { mapState } from "vuex";
export default {
// ...
computed: {
getTest() { return "test" },
mapState({
// 箭头函数可以使代码更简练
person: state => state.person,
// 传字符串参数 'person' 等同于 `state => state.person`
personAlias: 'person',
// 为了可以使用 `this` 获取局部状态,必须使用常规函数
personName(state) {
return state.person.name + this.localPerson.name
}
})
// 对象展开运算符更方便
// ...mapState({
// person: state => state.person
// })
// 对象展开运算符加名称缩写究极方便
// ...mapState([
// "person"
// ])
}
}
复制代码
有时候咱们不单单须要state的值,多个组件须要这个值的衍生值,如将数值由人民币换算成美圆,这时候就是getter出场的时候了,这一点它有点相似compute,getters返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被从新计算。函数
getter接受 state
做为其第一个参数,接受 getters
做为其第二参数,对于模块内部的getter来讲,接受rootState
做为第三个参数,为根节点的state。
const person = {
state: {
name: "tom",
age: 20,
money: 100
},
getters: {
getAge(state) {
return state.age / 10;
}
convert(state, getters, rootState) => {
// 第二个参数getters返回该store.getters对象
return state.money * 100 * getters.getAge;
},
// getter也能够返回函数,以达到传参的目的
// 1:人民币转美圆 0:反之
convertByRate: (state) => (type) => {
const rate = type === 1 ? 6.8925 : 0.1451;
return state.money * rate;
}
}
};
复制代码
// getter传参
// 转美圆
this.$store.getters.convertByRate(1);
复制代码
用法和mapState相同,将 store 中的 getter 映射到局部计算属性
mapGetters中方法接受一个参数:context对象,等同于store实例
// 组件中:
import { mapGetters } from "vuex";
// ...
computed: ...mapGetters({
convert: context => context.getters.convert
});
// computed: ...mapGetters([
// "convert"
// ]);
复制代码
Vuex中,在组件中直接改变state的值是不行的,惟一改变状态的方法是提交 mutation 。每一个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler),这是官方的说法,我以为其实就是事件名和事件本体啦,这样更好理解。事件第一个参数是state,第二个参数是载荷(payload),可选,通常状况下是一个对象。(载荷payload这个名字也是颇有逼格,redux中也是这样叫的,vuex参考了redux)
仍是以前的例子:
const person = {
state: {
name: "tom",
age: 20,
money: 100
},
getters: {
getAge(state) {
return state.age / 10;
}
},
mutations: {
setName(state, payload) {
state.name = payload.name;
}
// 或者简单点,不传对象直接传值
// setName(state, val) {
// state.name = val;
// }
}
};
复制代码
那么怎么调用这个mutation来改变state的值呢?
this.$store.commit("setName", { name: "rachael" });
// 对象的形式调用
this.$store.commit({
type: "setName",
name: "rachael"
});
复制代码
同mapState 与 mapGetters用法,咱们将mutation的方法映射到本地组件中;
import { mapMutations } form "vuex";
export default {
methods: {
...mapMutations([
// 将this.setName(payload) 映射为 this.$store.commit("setName", payload)
"setName"
])
}
}
复制代码
在mutations中只支持同步事件,须要处理一些http请求等异步事件时就须要actions了
在action中,不是直接改变状态的值,而是提交mutation来改变。action中能够包含任意的异步操做。
仍是以前的例子:
const person = {
state: {
name: "tom",
age: 20,
money: 100
},
getters: {
getAge(state) {
return state.age / 10;
}
},
mutations: {
setName(state, payload) {
state.name = payload.name;
}
},
actions: {
setName(context) {
// context参数与 store 实例具备相同方法和属性,当不等同于store实例,其还有rootState属性,即根节点state
setTimeout(() => {
context.commit("setName", { name: "rachael" });
}, 1000)
},
// 参数解构简写
// setName({ commit, rootState }) {
// rootState为该模块根节点state值
// commit("setName", { name: "rachael" });
// }
}
};
复制代码
this.$store.dispatch('setName', { name: "rachael" });
复制代码
一样也有mapActions函数,用法和mapMutations相同;
当一个action函数须要调用其余异步的action1时,将action1返回一个Promise并结合async / await
调用便可。
默认状况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的。在调用时,直接全局调用:
this.$store.getters.getAge();
this.$store.commit("setName", { name: "rachael" });
this.$store.dispatch("setName", { name: "rachael" });
复制代码
注册为模块命名空间的就不讨论了,通常用默认的全局就能够了,模块命名空间会麻烦不少。
使用常量代替方法名,再把这些常量放在统一的一个文件:types.js中,有助于咱们维护和理解。
types.js:
export const SET_NAME = "SET_NAME";
export const GET_NAME = "GET_NAME";
复制代码
在module.js中就能够改成:
import {
SET_NAME,
GET_NAME
} form "@/store/types";
const person = {
state: {
name: "tom",
age: 20,
money: 100
},
getters: {
[GET_NAME](state) {
return state.age / 10;
}
},
mutations: {
[SET_NAME](state, payload) {
state.name = payload.name;
}
},
actions: {
[GET_NAME]({ commit, rootState }) {
// context参数与 store 实例具备相同方法和属性,当不等同于store实例,其还有rootState属性,即根节点state
setTimeout(() => {
commit("setName", { name: "rachael" });
}, 1000)
}
}
};
复制代码
this.$store.getters.[GET_NAME]();
this.$store.commit("SET_NAME", { name: "rachael" });
this.$store.dispatch("SET_NAME", { name: "rachael" });
复制代码
再回到这张图,是否是就好理解了~
首发于本人的博客:柴犬工做室