vuex 知识点梳理

为何会出现vuex

vuex的应用场景,你们可能或多或少知道,用来管理一些全局数据,或者多个页面都会使用到的数据,好比,咱们项目中常见的用户信息,一般使用vuex来管理。html

这里,咱们思考一个问题,如何没有vuex,还有哪些方式能够实现一些公共数据的管理?vue

  1. 把公共数据存储到根实例中,其余页面均可以经过 this.$root 来获取根实例中的数据。
  2. 把公共数据存储到根实例中,其余页面经过 provide/inject 机制来获取根实例中的数据。

以上两种方式,虽然能够用来管理咱们的全局数据,可是随着项目愈来愈复杂,全局数据愈来愈多,目前这样的管理方式成本仍是有些高,因此,咱们须要一套系统化的,流程化的机制来管理全局数据,而vuex就是为此而诞生的。vuex

vuex的优势:它不只仅是一个全局数据的管理器,同时它最大的特性是支持响应式的,也就是说使用vuex管理的数据若是发生改变,那么引入这些数据的组件都会自动刷新,vue-cli

运行机制

首先,咱们经过上图要明白如下几点:npm

  1. 整个流程依然是单项数据流
  2. 从State开始,这里存储着咱们所须要的全局变量,一样读取的时候,咱们也是从State里面读取数据。
  3. State里面的数据变动,Vue component也就是vue组件,即页面会进行响应式变化。
  4. 数据更新方式1: 经过commit操做,在Mutations中进行数据更新
  5. 数据更新方式2: 经过dispatch操做,在actions中进行数据更新(actions内部其实本质上也是经过commit在mutations中更新数据的)
  6. actions中一般能够进行一些异步操做,因此若是有一些数据须要经过后端接口获取,那么一般将相关代码逻辑放到actions去处理
  7. mutations中都是同步操做,若是只有同步操做,则相关逻辑直接在mutations中处理便可

以上基本覆盖了vuex的经常使用知识点,接下来,咱们来经过一个具体的代码案例来深刻理解一下。后端

vuex基本使用

咱们来实现一个简单的计数器,点击能够增长数字, 首先,本次代码都在基于vue-cli3生成的项目模版去实现的。api

第一步:安装vuex缓存

npm i vuex --save
复制代码

第二步:引入vuexbash

//main.js
import App from './App.vue'
Vue.config.productionTip = false
//vuex的使用
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
    //全部的数据都存放在state中
    state: {
        count: 0
    },
    //mutations实现同步操做
    mutations: {
        increment (state, params) {
            state.count += params;
        }
    },
    //actions中实现一些异步操做
    actions: {
        increment ({state}, params) {
            setTimeout(() => {
                state.count += params;
            }, 3000)
        }
    },
    getters: {
        doubleCount (state) {
            return state.count * 2;
        }
    }
});
new Vue({
    store,
    render: h => h(App),
}).$mount('#app')

复制代码
//app.vue
<template>
    <div id="app">
        <span>count: {{count}}</span>
        <button @click="addCount1">count++</button>
        <br>
        <span>doubleCount: {{doubleCount}}</span>
        <button @click="addCount2">count++</button>
    </div>
</template>

<script>

    export default {
        name: 'app',
        computed: {
            count () {
                return this.$store.state.count;
            },
            doubleCount () {
                return this.$store.getters.doubleCount;
            }
        },
        methods: {
            addCount1 () {
                this.$store.commit('increment', 2); //mutations经过commit触发
            },
            addCount2 () {
                this.$store.dispatch('increment', 2); //actions经过dispatchc触发
            }
        }
    }
</script>

复制代码

经过上面的代码,咱们要清楚如下几点:app

  1. State: this.$store.state.xxx 取值
  2. Getters: this.$store.getters.xxx 取值
  3. Mutations: this.$store.commit('xxx') 赋值
  4. Action:this.$store.dispatch('xxx') 赋值

也就是说:咱们一般能够经过state和getters来获取全局数据,也能够经过mutations和actions修改全局数据,mutations是同步操做,actions是异步操做。

1. store的建立与注入

  1. 建立store
import Vuex from 'vuex';
const store = new Vuex.Store({
    state: {},
    mutations: {}
    ...
});
复制代码
  1. 向vue实例中注入store
new Vue({
    store,
    render: h => h(App),
}).$mount('#app')
复制代码

问题:

  1. Vue.use(Vuex);//use方法的原理
  2. 将store注入到vue实例的原理

2. State

经过上面的学习,咱们知道了,全部状态变量都是定义在store中的state属性中,那么,如何获取state中的数据呢? //第一种方式:

export default {
    computed: {
        count () {
            return this.$store.state.count;
        },
        doubleCount () {
            return this.$store.getters.doubleCount;
        },
        number () {
            return this.$store.state.number;
        }
    },
}
即直接在computed计算属性中返回所需数据,缺点就是:若是状态树愈来愈多,每一个属性都须要咱们在computed中声明而且返回一次,不太友好,解决方案就是mapState
复制代码

//第二种方式:

import {mapState} from 'vuex';
export default {
    computed: mapState([
        'count',
        'doubleCount',
        'number'
    ]),
}
同时,若是想自定义别名,也能够给mapState传入一个对象,自定义处理,可是上面的写法,咱们就没法在computed声明当前vue实例下本身的计算属性了,解决方案就是rest运算符...
复制代码

//第三种方式:

import {mapState} from 'vuex';
export default {
    computed: {
        ...mapState([
            'count',
            'number'
        ]),
        ...mapGetters([
            'doubleCount'
        ]),
        //也能够自定义其余计算属性
    },
}
复制代码

经过上面,咱们知道了能够有不少种不一样的写法去读取state里面的数据,getters也相似,上面第三种方式也有体现,咱们须要知道各类写法的优缺点,同时,在实际项目中,咱们只须要采用最优写法便可,即第三种方式。

3. Getter

Getter与State的做用都是定义一些全局使用的数据,Getter和State的关系就相似于computed和data的关系,Getter的返回值会被缓存起来,只有它所依赖的state中的数据发生变化才会跟着从新计算。

咱们只须要明白Getter的做用以及与State的区别便可,写法和State相似,参考上面便可。

4. Mutation

上面咱们知道了,能够经过state和getter来定义和读取状态,那么接下来,如何修改状态呢?记住一句话:更改 Vuex 的 store 中的状态的惟一方法是提交 mutation.

首先,要说明一下mutation中的几个概念:

  1. 事件类型
  2. 载荷payload

咱们在具体的代码中说明:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    //increment就是在mutation中定义的事件类型
    increment (state, payload) {
        state.count += payload.amount
    }
  }
})

store.commit('increment', {
  amount: 10 // commit中传的参数就是载荷
})

复制代码

接下来,咱们要说一下在组件中mutation的几种写法: //第一种

export default {
    methods: {
        addCount () {
            this.$store.commit('increment', 2);           
        }
    }
}
复制代码

//第二种方式:

import {mapMutations} from 'vuex';
export default {
     methods: {
        ...mapMutations([
            'increment'
        ]),
        addCount () {
            this.increment(2);
        }
    }
}
复制代码

注意点:在mutation中都是同步事件,由于mutation的做用就是能够捕捉到每一次的提交或者修改记录,若是是异步事件,devtool很难捕捉到,这时就要用到Action了

5. Action

Action和Mutation都是用来修改状态的,区别就是action中一般是一些异步事件,而且经过dispatch去分发事件,固然,有一点,咱们要知道actions内部也是经过提交mutation去修改数据的。

这里,具体写法就不说了,和mutation相似。

6. 其余注意点:

经过上面的讲解,咱们都知道了,修改state的惟一路径就是 提交mutation,固然,若是咱们直接修改了state里面的值,在非严格模式下,也是能够生效的,可是不建议这么作,在严格模式下,vue会直接报错

还有一点:表单里使用v-model双向绑定的若是是state里面的数据,这时,若是在严格模式下,会直接报错的,由于数据修改完,v-model内部就默认直接修改了state里面的数据,具体解决方案,能够直接参考官网:vuex.vuejs.org/zh/guide/fo…

7. 总结

vuex其实自己内容是比较简单的,可是因为咱们引入mapState,mapGetter等map写法以后,可能会容易产生混淆,致使感受vuex相关概念较多,没法真正理清,这里咱们在总结一下:

首先vuex的核心就是store,也叫仓库,他就是一个容器,里面包含着state,getter,mutation,action等属性,

  1. 首先这些状态都定义在哪里呢?

即直接定义在state中就能够啦

  1. 定义完之后,怎么在组件中读取状态呢?

经过this.store.state 或者 this.store.getter的方式去读取,固然为了写法更加间接,咱们引入了mapState和mapGetter。

  1. 定义完之后,怎么在组件中修改状态呢?

vuex只支持一种方式:那就是commit mutation,action内部自己也是commit mutaition, 固然,依然是为了写法间接,咱们又引入了mapMutations和mapActions。

经过上面的总结,咱们知道了map系列api,都只是为了如何更简洁的读取和修改状态,暂时抛开这一部分,咱们发现,其实vuex的内容其实很简单。

原理解析

这一节,咱们主要看一下vuex的实现原理,同时,实现也能够简易版的vuex。

经过上面的介绍,咱们知道了vuex中的状态state和单纯的全局对象仍是不一样的,主要体如今:

  1. vuex的状态是响应式的
  2. vuex的状态不能够直接修改,必须经过提交mutation的方式去修改。

那,咱们接下来看看vuex具体是如何实现响应式的?其实内部就是经过vue的响应式机制去实现的。

下面,咱们能够手动实现一个简易版的vuex

import Vue from 'vue'
const Store = function Store (options = {}) {
  const {state = {}, mutations={}} = options
  this._vm = new Vue({
    data: {
      $$state: state
    },
  })
  this._mutations = mutations
}
Store.prototype.commit = function(type, payload){
  if(this._mutations[type]) {
    this._mutations[type](this.state, payload)
  }
}
Object.defineProperties(Store.prototype, { 
  state: { 
    get: function(){
      return this._vm._data.$$state
    } 
  }
});
export default {Store}
复制代码

经过上面的代码,咱们看到,其实vuex内部也是经过new Vue()的方式,将state存储在vue实例的data中,从而实现响应式的。