1

认识Vuex

背景

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
Vuex是一个提供在多个组件间共享状态的插件,且状态是响应式的。

单页面状态管理

State:状态。(可以当做就是data中的属性)
View:视图层,可以针对State的变化,显示不同的信息。
Actions:主要是用户的各种操作:点击、输入等等,会导致状态的改变。
单页面.png

<template>
    <div class="test">
        <div>当前计数: {{counter}}</div>
        <button @cli ck="counter+=1"> +1</button>
        <button @click="counter-=1">-1</button>
    </div>
</template>
<script>
export default {
    name: 'Helloworld',
    data () {
        return {
            counter: 0
        }
    }
}
</script>

多页面状态管理(vuex)

不同界面的Actions都想修改同一个状态,现在要做的就是将共享的状态抽取出来,交给vuex。之后,每个组件,按照规定好的规定,进行访问和修改等操作。

Vuex状态管理图例

vuex.png
Vue components可以直接到mutations(但是不建议这样做)。Mutations中的操作都是同步操作,要是有异步操作就在actions中操作,操作完成之后再提交到mutations中进行同步操作。Devtools跟踪不到异步操作。
Devtools是Vue开发的一个浏览器插件,可以帮助记录每次修改state的记录

Vuex基本使用

Vuex核心概念

State

Vuex使用了单一状态树来管理应用层级的全部状态。单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新。

Getters

类似于组件中的计算属性。当数据必须经过一系列变化之后再在页面上使用时就用到计算属性。
同理,如果是在state中的数据,需要进行操作后在页面显示,就用到getters属性。

const store = new Vuex . Store({
    state: {
        students: [
            {id: 110,name:'why',age:18},
            {id: 111,name:'kobe',age:21},
            {id: 112,name:'lucy',age:25},
            {id: 113,name:'lilei',age:30},
        ]
    },
    getters: {
        //获取学生年龄大于20的个数。
        greateragesCount: state =>{
            return state. students. filter(s => s.age >= 20) . length
        }
    }
})
Getters作为参数和传递参数

如果我们已经有了一个获取所有年龄大于20岁学生列表的getters, 那么代码可以这样来写

getters: {
    greaterAgesstus: state => {
        return state.students.filter(s => s.age >= 20)
    },
    greateragesCount: (state, getters) => {
        return getters.greaterAgesstus.length
    }
}

getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.

Mutation

Vuex的store状态的更新唯一方式:提交Mutation
Mutation主要包括两部分:字符串的事件类型(type);一个回调函数(handler),该回调函数的第一个参数就是state。

Mutation基本使用

mutation的定义方式:

mutations: {
    increment(state) {
        state.count++
    }
}

通过mutation更新

increment:function() {
    this.$store.commit('increment')
}
Mutation传递参数

在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数。参数被称为是mutation的载荷(Payload)
当参数只有一个的时候,可以在mutation中使用参数

decrement(state,n) {
    state.count -= n;
}
decrement:function() {
    this.$store.commit('decrement',2);
}

如果参数不是一个,通常会以对象的形式传递, 也就是payload是一个对象。再从对象中取出相关的信息。

changeCount(state.pay1oad) {
    state.count = payload.count;
}
changeCount:function () {
    this.$store.commit('changeCount',{count: 0});
}
Mutation提交风格

Vue还提供了另外一种风格, 它是一个包含type属性的对象

this.$store.commit({
    type: 'changeCount',
    count: 100
})

Mutation中的处理方式是将整个commit的对象作为payload使用, 所以代码没有改变, 依然如下:

changeCount(state,pay1oad) {
    state.count = pay1oad.count
}
Mutation响应规则

提前在state中初始好的属性是响应式的。之后若要修改(添加)state中的数据是其为响应式。有两种方式:
方式一: 使用Vue.set(obj, 'newProp', 123);
方式二: 用新对象给旧对象重新赋值

App.vue
<template>
    <div id="app">
        <p>我的个人信息: {{info}}</p>
        <button @click="updateInfo">更新信息</button>
    </div>
</template>
<script>
    export default {
        name:'App',
        components: {},
        computed: {
            info() {
                return this.$store.state.info;
            }
        },
        methods: {
            updateInfo() {
                this.$store.commit('updateInfo', {height: 1.88})
            }
        }
    }
</script>
store-->index.js
const store = new Vuex.Store({  
    state: {
        info:{
            name:'why',age: 18
        }
    },
    mutations: {
        updateInfo(state, payload) {
            //在学习教程中说这样修改不是响应式的,但是实践后是响应式的。
            state.info['height'] = payload.height;
            //方式一: Vue.set()
            Vue.set(state.info, 'height', payload.height);
            //方式二:给info赋值一个新的对象
            state.info = {...state.info, 'height': payload.height}
        }
    }
});
Mutation常量类型
  • 背景

当项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多。方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况。
一种很常见的解决方案就是使用常量替代Mutation事件的类型。可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.

  • 具体操作

可以创建一个文件: mutation-types.js, 并且在其中定义常量。定义常量时, 可以使用ES2015中的风格, 使用一个常量来作为函数的名称。

mutation-types.js
export const UPDATE_INFO = 'UPDATE_INFO';
store-->index.js
import Vuex from 'vuex'
import Vue from 'vue'
import * as types from './mutation-types'

Vue.use(Vuex);

const store = new Vuex.Store({ 
    state: {
        info: {
            name:'why',age:18
        }
    },
    mutations:{
        [types.UPDATE_INFO](state, payload) {
            state.info = {...state.info, 'height': payload.height}
        }
    }
});
App.vue
<script>
import {UPDATE_INFO} from "./store/mutation-types";
export default {
    name:App',
    components: {},
    computed: {
        info() {
            return this.$store.state.info;
        }
    },
    methods: {
        updateInfo() {
            this.$store.commit(UPDATE_INFO, {height: 1.88});
        }
    }
}
</script>
Mutation同步函数

Vuex要求Mutation中的方法必须是同步方法。原因是当使用devtools时, devtools可以帮助我们捕捉mutation的快照。如果是异步操作, devtools不能很好的追踪这个操作什么时候会被完成。通常情况下, 不要再mutation中进行异步的操作。

Action

Action基本定义

Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.
Action的基本使用代码如下:

const store = new Vuex.Store({
    state:{
        count: 0
    },
    mutations:{
        increment(state) {
            state.count++;
        }
    }
    actions: {
        increment(context) {
            context.commit('increment');
        }
    }
});

context是和store对象具有相同方法和属性的对象.可以通过context去进行commit相关的操作, 也可以获取context.state等。定义了actions, 如果在Vuex中有异步操作, 那么我们就可以在actions中完成了.

Action的分发

如果调用action中的方法, 那么就需要使用dispatch。它也支持传递payload

//App.vue
methods:{
    increment() {
        this.$store.dispatch('increment',{cCount: 5});
    }
}

//store-->index.js
mutations: {
    increment(state, payload) {
    state.count += payload.cCount;
},
actions: {
    increment(context, payload) {
        setTimeout(() => {
            context.commit('increment', payload)
        },5000);
    }
}
Action返回的Promise

Action中, 我们可以将异步操作放在一个Promise中, 并且在成功或者失败后, 调用对应的resolvereject

store-->index.js
actions: {
    increment(context) {
        return new Promise((resolve) => {
            setTimeout(() => {
                context.commit('increment');
                resolve()
            },1000);
        })
    }
}
App.vue
methods: {
    increment() {
        this.$store.dispatch('increment').then(res => {
            console.log('完成了更新操作');
        })
    }
}

Module

认识Vuex的Module

Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理。当应用变得非常复杂时,store对象就有可能变得相当臃肿。为了解决这个问题, Vuex允许将store分割成模块(Module), 而每个模块拥有自己的statemutationactiongetters等。

const moduleA = {
    state:{ ... },
    mutations:{ ... },
    actions:{ ... },
    getters: { ... }
const moduleB = {
    state: { ... },
    mutations: { ... },
    actions: { ... }
}

const store = new Vuex.store({
    modules:{
        a:moduleA,
        b:moduleB
    }
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
Module的局部状态

mutation和getters接收的第一个参数是局部状态对象。

store-->index.js
const module = (
    state:{
        count:0
    },
    mutations:{
        increment(state) {
            state.count++;
        },
    }
    getters: {
        doubleCount(state) {
            return state.count*2;
        }
    }
}

const store = new Vuex.store({
    state:{
        gCount:111
    },
    modules:{
        a:moduleA,
        b:moduleB
    }
})
App.vue
<script>
    export default {
        name:'App',
        components: {},
        computed: {
            count() {
                return this.$store.getters.doubleCount
            }
        },
        methods: {
            increment() {
                this.$store.commit('increment');
            }
        }
    }
</script>

注意:虽然, 我们的doubleCountincrement都是定义在对象内部的。但是在调用的时候, 依然是通过this.$store来直接调用的

Module的Actions写法

局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

const moduleA = {
    actions: {
    incrementIfoddonRootSum ( {state, commit,rootState} ) { //context 对象的解构
        if ((state.count + rootState.count) % 2 === 1) {
            commit('increment');
        }
    }
}

项目组织结构

官方建议:state不抽离。
vuex组织.png


梁柱
135 声望12 粉丝

« 上一篇
6.vue-router