Vuex由浅入深

1. vuex简介

vuex是专门用来管理vue.js应用程序中状态的一个插件。它的作用是将应用中的所有状态都放在一起,集中式来管理。需要声明的是,这里所说的状态指的是vue组件中data里面的属性。了解vue的人应该是明白data是怎么回事的吧,如果不懂的话,建议先学完vue的基础知识再看vuex。

2. vuex的组成结构示意图

vuex的特点是把数据单独隔离,形成一棵树状图。单独隔离就意味着它有自己的生态系统。输入和输出,其中action作为数据的输入,state作为数据的输出。如下图所示
vuex
注意:只能在mutaions里修改state,actions不能直接修改state
mutations即变化,修改state的数据,而且只能是同步的,不能存在异步的操作。如果需要异步怎么办呢?把异步操作放在actions里,拿到数据再通过mutations同步处理。vuex做的其实是把职权明确了,责任细分了。所以它文档里也说,小系统可以不用。状态数据少,没有细分的必要。

通过这个图,我们很容易就对vuex的组成部分,以及vuex与组件(components)之间的联系一目了然。其实这张图,有点迷惑人,暂时看不懂也没关系,只要有一个直观的认识就行了,用的多了自然也就懂了

3. vuex 的核心概念

3.1.1 创建store

import Vue from "vue";
import Vuex from "vuex";

import state from './state';
import actions from './actions';
import mutations from './mutations';
import getters from './getters';

Vue.use(Vuex);
export default new Vuex.Store({
    state,
    actions,
    mutations,
    getters
});

store是Vuex.Store这个构造函数new出来的实例。在构造函数中可以传一个对象参数。
这个参数中可以包含5个对象:

1.state – 存放状态

2.getters – state的计算属性

3.mutations – 更改状态的逻辑,同步操作

4.actions – 提交mutation,异步操作

5.mudules – 将store模块化

注意事项:
关于store,需要先记住两点:
1.store 中存储的状态是响应式的,当组件从store中读取状态时,如果store中的状态发生了改变,那么相应的组件也会得到更新;

2.不能直接改变store中的状态。改变store中的状态的唯一途径是提交(commit)mutations。这样使得我们可以方便地跟踪每一个状态的变化。

3.2 state

state上存放的,说的简单一些就是变量,也就是所谓的状态。没有使用 state 的时候,我们都是直接在 data 中进行初始化的,
但是有了 state 之后,我们就把 data 上的数据转移到 state 上去了。另外有些状态是组件私有的状态,称为组件的局部状态,
我们不需要把这部分状态放在store中去。

3.2.1 如何在组件中获取vuex状态

由于vuex的状态是响应式的,所以从store中读取状态的的方法是在组件的计算属性中返回某个状态。

  <Item v-for="(todo,index) in this.$store.state.todos" :key="index" :todo="todo" :index="index"></Item>

这样,组件中的状态就与store中的状态关联起来了。每当store.state.todos发生变化时,都会重新求取计算属性,从而更新DOM。

3.2.2 mapState
当一个组件获取多种状态的时候,则在计算属性中要写多个函数。为了方便,可以使用mapState辅助函数来帮我们生成计算属性。

<Item v-for="(todo,index) in todos" :key="index" :todo="todo" :index="index"></Item>
import { mapState } from "vuex";
computed: {
    ...mapState(["todos"]);
}

上面是通过mapState的对象来赋值的,还可以通过mapState的数组来赋值
这种方式很简洁,但是组件中的state的名称就跟store中映射过来的同名

3.3 getters

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数。此时可以用到getters,getters可以看作是store的计算属性,其参数为state。

扫描二维码关注公众号,回复: 11755878 查看本文章
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);
        }
    }
});
3.3.1 获取getters里面的状态,方法一
store.getters.doneTodos //  [{ id: 1, text: 'reading', done: true }]
//在组件中,则要写在计算属性中,
computed: {
    doneTodos() {
        return this.$store.getters.doneTodos;
    }
}
3.3.2 使用mapGetters获取getters里面的状态:方法二
import { mapState, mapGetters } from 'vuex';
computed: {
    ...mapState(['increment']),
    ...mapGetters(['doneTodos'])
}	

3.4 mutations

mutations里面是如何更改state中状态的逻辑。更改Vuex中的state的唯一方法是,提交mutation,即

store.commit(‘increment’)。

将常量放在单独的文件中,方便协作开发

import { 
	ADD_TODO, DELETETODO, 
	SELECT_ALL_TODOS,
	 CLEAR_ALL_COMPLETED 
} from './motatios-types';

更多示例

export default {
    [ADD_TODO](state, { todo }) {
        state.todos.unshift(todo);
    },
    [DELETETODO](state, { index }) {
        state.todos.splice(index, 1);
    },
    [SELECT_ALL_TODOS](state, { check }) {
        state.todos.forEach(todo => todo.complete = check);
    },
    [CLEAR_ALL_COMPLETED](state) {
        state.todos = state.todos.filter(todo => !todo.complete);
    },
}

注意:mutation必须是同步函数,不能是异步的,这是为了调试的方便

3.4.2 在组件中提交mutations

那么mutation应该在哪里提交呢? 因为js是基于事件驱动的,所以改变状态的逻辑肯定是由事件来驱动的,所以store.commit(‘increment’)是在组件的methods中来执行的。

方法1: 在组件的methods中提交

methods: {
    increment(){
        this.$store.commit('increment');
    }
}

方法2: 使用mapMutaions

用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用。

import { mapMutaions } from 'vuex';
export default {
    // ...
    methods: {
        ...mapMutaions([
            'increment' // 映射 this.increment() 为 this.$store.commit('increment')
        ]),
        ...mapMutaions([
            add: 'increment' // 映射 this.add() 为 this.$store.commit('increment')
        ])
    }
}
    // 因为mutation相当于一个method,所以在组件中,可以这样来使用
    < button @click="increment" > +</button>
3.5.3 组合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');
	        })
	    }
	}

4.mudules

module是为了将store拆分后的一个个小模块,这么做的目的是因为当store很大的时候,分成模块的话,方便管理。

4.1 每个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的状态

4.2 模块内部的状态

对于模块内部的mutation和getter,接受的第一个参数是模块的局部状态state。顺便说一下,根结点的状态为rootState。

const moduleA = {
    state: { count: 0 },
    getters: {
        doubleCount(state) {
            return state.count * 2;
        }
    },
    mutations: {
        increment(state) {
            state.count++;
        }
    },
    actions: { ...}
}

4.3 模块的动态注册

在模块创建之后,可以使用store.registerModule方法来注册模块。
	
	store.registerModule('myModule', {
	    // ...
	});
依然可以通过store.state.myModule来获取模块的状态。

可以使用store.unregisterModule(moduleName)来动态的卸载模块,
但是这种方法对于静态模块是无效的(即在创建store时声明的模块)。

5 含有vuex的项目的结构

5.1 应该遵循的规则

1. 应用层级的状态都应该集中在store中
2. 提交 mutation 是更改状态state的唯一方式,并且这个过程是同步的。

猜你喜欢

转载自blog.csdn.net/handsomezhanghui/article/details/105908419