一、vuex的简介
Vuex是一个专为Vue开发的应用程序的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简而言之:Vuex采用类似全局对象的形式来管理所有组件的公用数据,如果想修改这个全局对象的数据名得按照Vuex提供的方式来修改(不能自己随意用自己的方式来修改),核心就是store(仓库),仓库是用来干什么的?你就当它是用来存储东西的。
二、vuex的优点是什么?
Vuexx状态管理使用传统全局变量的不同之处:
1.Vuex的状态存储是响应式:就是当你的组件使用到了在这个Vuex的状态,一旦它改变了,所有关联的组件都会自动更新相对应的数据,这样开发者省事很多。
2.不能直接修改Vuex的状态:如果是个全局对象变量,要修改很容易,但是在Vuex中不能这样做,想修改就得使用Vuex提供得唯一途径:显示地提交(commit)mutations来实现修改。这样做的好处就是方便我们跟踪每一个状态得变化,在开发过程中调试的时候,就非常使用啦
vuex的结构示意图如下:
三、我们什么时候应该用到Vuex呢?
1、小应用不建议使用Vuex,因为小项目使用Vuex可能会比较繁琐冗余;
2、中大型单页应用,因为要考虑如何更好地在组件外部管理状态,Vuex将会成为自然而然地选择
四、vuex核心组成模块
1、State: 定义了应用状态的数据结构,可以在这里设置默认的初始状态(存放状态)
2、Getter:允许组件从store中去获取数据,mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性(state的计算属性)
3、Mutation:是唯一更改store中状态的方法,且必须是同步函数(更改状态的逻辑,同步操作)
4、Action:用于提交mutation,而不是直接变更状态,可以包含任意异步操作。(提交mutation,异步操作)
5、Module:可以将store分割成模块(module)。每个模块拥有自己的state、mutation、action、getter甚至是嵌套子模块。(将store模块化)
关于store,需要先记住两点:
- 1、store中存储的状态是为响应的,当组件从store中读取状态时,如果store中的状态发生了改变,那么响应的组件也会得到更新。
- 2、不能直接改变store中的改变。改变store中的状态的唯一途径是提交(commit)mutations.这样使我们方便跟踪到每一个状态。
五、一个完整的Store的结构✔
const store = new Vuex.Store({
state:{
//存放状态
},
getters:{
//state的计算属性
},
mutations: {
//更改state中状态的逻辑,同步操作
},
actions: {
//提交mutation,异步操作
},
//如果将store分成一个个的模块的话,则需要用到modules.
//然后在每一个module中的state,getters,mutations,actions等
modules: {
a: moduleA,
b: moduleB,
//...
}
});
六、如何在组件中获取vuex
vuex状态是响应式的,所以从store中读取状态的方法是在组件的计算属性中返回某个状态!!!
import store from 'store';
const Counter = {
template:`<div>{{ count }}`,
computed: {
count () {
//去获取store中的状态
return store.state.count;
}
}
}
根据以上的步骤组件中的状态就与store的状态关联起来了。每当store.state.count发生改变时,都会重新求取计算属性,从而去更新DOM。
每个组件都需要反复倒入store。可以将store注入到vue实例对象中去,这样每一个组件中都需要直接去获取store中的状态,而不需要反复导入store了
const app = new Vue({
el: '#app',
//把store对象注入到vue中去了
store,
components: { Counter},
template: `<div>
<counter></counter>
<div>
`
});
上面的代码可以在组件中使用this.$store.count访问到state里面的count这个状态
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
//获取store中的状态
return this.$store.state.count;
}
}
}
七、mapState的使用
当一个组件去获取多种的状态时,则在计算属性中要去写多个函数,这里为了方便,可以使用mapState辅助函数来帮我们生成计算属性
import { mapState } from 'vuex';
export default {
//...
data (){
localState: 1
}
computed: mapState({
//此处的state即为store里面的state状态
count: state => state.count,
//当计算属性的名称与state的状态名称一样时,可以进行省写
//映射this.count1为store.state.count1
count1,
//'count'等同于‘state => state.count’
countAlias: 'count',
countPlus (state){
//使用普通函数是为了保证this.指向组件对象
rerurn state.count + this.localState;
}
})
}
//上面是通过mapState的对象来赋值的,还可以通过mapState的数组来赋值
computed: mapState(['count']);
//这种方式很简洁,但是组件中的state的名称就跟store中映射过来的同名!
对象扩展运算符
mapState 函数返回的是一个对象,为了将它里面的计算属性与组件本身的局部计算属性组合起来,需要用到对象扩展运算符。
computed: {
localState (){
...mapState ({
})
}
}
以上的操作…mapState的计算属性就与localState计算属性全部混合在一起了.
八、getters的使用
有很多时候我们需要从store中的state中派生出一些状态,例如对列表进行过滤并计数。此时可以用到getters,getters可以看作是store的计算属性,其参数为state。
const store = new Vuex.Store({
state: {
todos: [
{id:1,text: 'reading',done: true},
{id:2,text: 'playBastKetball',done: false}
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done);
}
}
});
获取getters里面的状态,方法一:
store.getters.doneTodos //[{id:1,text: 'reding',done: true}]
//在组件中,则要写在计算属性中去
computed: {
doneTods (){
return this.$store.getters.doneTodos;
}
}
使用mapGetters获取getters里面的状态,方法二:
import {mapState,mapGetters} from 'vuex';
computed: {
...mapState({'increment'}),
...mapGetters({'doneTodos'})
}
九、mutations
mutations里面是如何更改state中状态的逻辑。更改Vuex中的state的唯一方法是,提交mutation,即store.commit(‘increment’).
九.一 提交载荷(payload)
可以向commit传入额外的参数,即mutation的载荷
mutations: {
increment(state, n){
state.count +=n;
}
}
store.commit('increment', 10);
payload还可以是一个对象
mutations: {
increment(state, payload)
state.count +=payload.amount;
}
}
store.commit('increment', {amount:10});
还可以使用type属性来提交mutation
store.commit({
type: 'increment',
amount: 10
});
//mutations保持不变
mutations: {
increment(state, payload)
state.count +=payload.amount;
}
注意:mutation必须是同步函数,不能是异步的,这是为了后期调试的方便。
九.二 在组件中提交mutations
mutation应该在哪里进行提交呢???因为是js是基于事件驱动的,所以改变状态的逻辑肯定是由事件来驱动的,所以store.commit(‘increment’)是在组件的methods中来执行的。
方法一:在组件的methods中进行提交。
methods: {
increment(){
this.$store.commit('increment');
}
}
方法二:使用mapMutaions
用mapMutations辅助函数将组件中的methods映射为store.commit调用。
import { mapMutations } from 'vuex';
export default {
//...
methods: {
...mapMutations([
'increment' //映射this.increment()为this.$store.commit('increment')
]),
...mapMutations([
add: 'increment' //映射this.add()改为this.$store.commit('increment')
])
}
}
//因为mutation相当于一个method,所以在组件中,可以这样来使用
<button @click="increment">+</button>
十.actions
因为mutations中只能是同步操作,但是在实际的项目中,会有异步操作,那么action中提交mutation,然后在组件的methods中去提交action。只是提交actions的时候使用的是dispatch函数,而mutations则是用commit函数。
十.一 一个简单的action
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state){
state.count++;
}
},
actions: {
increment(context){
context.commit('increment');
}
/*可以用参数结构的方法来写action
increment({commit}){
commit('increment');
}
*/
}
});
//action函数接受一个context参数,这个context具有与store实例相同的方法和属性
action同样支持payload和对象方式来分发,格式跟commit是一样的,不再赘述
十.二 在组件中分发action
方法1:在组件的methods中,使用this.$store.dispatch(‘increment’)。
方法2:使用mapActions,跟mapMutations是类似的
import {mapActions} from 'vuex'
export default {
//...
methods: {
...mapActions([
'increment' //映射this.increment() 为this.$store.dispatch('increment')
]),
...mapActions({
add: 'increment' //映射this.add()为this.$store.dispatch('increment') })
}
}
//同样在组件中,可以这样来使用
<button @click="increment">+</button>
十一.一 组合actions
因为action是异步的,那么我们需要知道这个异步函数什么时候结束,以及等到执行后,会利用某个action的结果。这个可以使用promise来实现。在一个action中返回一个promise,然后使用then()回调函数来处理这个action返回的结果。
actions:{
actionA({commit}){
return new Promise(resolve,reject) => {
setTimeout(() => {
commit('someMutation');
resolve();
},1000);
})
}
}
//这样就可以操作actionA返回的结果了
store,dispatch('actionA').then(() => {
//dosomething...
});
//也可以选另一个action中使用actionA的结果
actions: {
//...
actionB({ dispatch,commit}){
return dispatch('actionA').then(() => {
commit('someOtherMutation');
})
}
}
十一.二 mudules
module是为了将store拆分后的一个个小模块,这么做的目的是因为当store很大的时候,分成模块的话,方便管理。
十一、三 每个module拥有自己的state, getters, mutation, action
const moduleA = {
state: {...},
getters: {...},
mutations: {....},
actions: {...}
}
const moduleB = {
state: {...},
getters: {...},
mutations: {....},
actions: {...}
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
});
store.state.a // 获取moduleA的状态
store.state.b // 获取moduleB的状态
十一.四 模块内部的状态
对于模块内部的mutation和getter,接受的第一个参数是模块的局部状态state。顺便说一下,根结点的状态为rootState。
const moduleA = {
state: { count: 0},
getters: {
doubleCount(state){
return state.count * 2;
}
},
mutations: {
increment(state){
state.count ++ ;
}
},
actions: {...}
}
十一.五 模块内部的状态
对于模块内部的mutation和getter,接受的第一个参数是模块的局部状态state。顺便说一下,根结点的状态为rootState。
const moduleA = {
state: { count: 0},
getters: {
doubleCount(state){
return state.count * 2;
}
},
mutations: {
increment(state){
state.count ++ ;
}
},
actions: {...}
}
十一.六 模块的动态注册
在模块创建之后,可以使用store.registerModule方法来注册模块。
store.registerModule('myModule', {
// ...
});
可以通过store.state.myModule来获取模块的状态。
可以使用store.unregisterModule(moduleName)来动态的卸载模块,但是这种方法对于静态模块是无效的(即在创建store时声明的模块)。
十二、 含有vuex的项目的结构
十二.一 应该遵循的规则
-
应用层级的状态都应该集中在store中
-
提交 mutation 是更改状态state的唯一方式,并且这个过程是同步的。
-
异步的操作应该都放在action里面