[VUE]带你深入Vuex

483 阅读3分钟

前言

Vuex作为VUE生态全家桶的一员,相信大部分使用VUE的前端开发工程师都会用过Vuex来做状态管理。可是大家有没有了解过Vuex的实现原来是怎样的呢?

Vuex是做什么的?

用官方的话来说,Vuex是一个“状态管理”。其实所谓的状态管理就是一个全局的变量管理实例。只是状态管理会给这些数据的修改做一定的规范。

Vuex的设计是借鉴了Redux和Flux的,因此我们可以看到如果想要在Vuex中修改的数据的时候,我们需要通过mutation来操作。通过一系列的规范,可以使得状态(数据)的修改变得更容易追踪。

Vuex的优势是什么?

其实这个问题尤大本人是回应的。

可以看到,VUEX其实与如Reduex等状态管理库相似,最大的区别是它集成了Vue的双向绑定,以及在vue组件中的使用会更加便利。

Vuex的实现原理

介绍完Vuex的本质之后,接下来我们来深入一下Vuex的原理。首先我们来回顾一下,Vuex的使用方法:

import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store'; // 引入我们前面导出的store对象

Vue.use(Vuex);

const store = new Vuex.Store({
    state: {
        // ....管理的数据
    },
});

new Vue({
    el: '#app',
    router,
    store, // 把store对象添加到vue实例上
    components: { App },
    template: '<App/>'
});

可以理解成,使用VUEX的步骤分为2步

// 声明式调用Vuex
Vue.use(Vuex);
// 声明要管理的数据
new Vue({
	store:new Vuex.Store({
  	// ...
  })
})

Vue.use(Vuex)

先来看use的这部份,我们都知道Vue.use会调用入参对象的install方法。这里Vuex的install方法源码如下:

// src/store.js
export function install (_Vue) {
  if (Vue && _Vue === Vue) {
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}

接下来看一下这个applyMixin方法的源码

export default function (Vue) {
  const version = Number(Vue.version.split('.')[0])

  if (version >= 2) {
    // 这里做了一个全局的mixin
    Vue.mixin({ beforeCreate: vuexInit })
  } else {
    const _init = Vue.prototype._init
    Vue.prototype._init = function (options = {}) {
      options.init = options.init
        ? [vuexInit].concat(options.init)
        : vuexInit
      _init.call(this, options)
    }
  }

可以看到install里做的是一个全局的mixin,。目标是beforeCreate生命周期,接下来再看看vuexInit函数的内容。

function vuexInit () {
    const options = this.$options
    // 往当前实例挂入store
    if (options.store) {
      this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
    }
  }
}

可以看到通过上方一系列的通知之后,在每个vue组件实例实例化之后,在beforeCreate生命周期都会往当前实例挂入store。这个store是来自于$optiopn。也就是我们在VUE渲染入口时,定义的store实例对象。

这也解释了为什么我们可以在vue组件中通this.$store拿到store数据。

Vuex的双向绑定

当我们在组件中用到store的数据时,在数据修改后相关的逻辑(视图)会自动更新。这其实是因为Vuex在store实例中,帮我们实现了双休绑定。核心思路跟Vue中MVVM实现的套路是一致的,底层用的还是Object.defineProperty。更多相关的内容可以看我之前关于MVVM的那篇文章) —— [VUE] 模仿vue实现一个mvvm框架

在Vuex中这部分的源码如下:

// src/core/observer/index.js
Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function reactiveGetter () {
    const value = getter ? getter.call(obj) : val
    // Dep.target是一个是否进行加入订阅的标识,同时本身也是一个watcher实例。
    if (Dep.target) {
      dep.depend()
      if (childOb) {
        childOb.dep.depend()
      }
      if (Array.isArray(value)) {
        dependArray(value)
      }
    }
    return value
  },
  set: function reactiveSetter (newVal) {
    const value = getter ? getter.call(obj) : val
    if (newVal === value || (newVal !== newVal && value !== value)) {
      return
    }
    if (setter) {
      setter.call(obj, newVal)
    } else {
      val = newVal
    }
    childOb = !shallow && observe(newVal)
    // 每次当前属性被修改后,会调用dep.notify()。从而触发订阅更新
    dep.notify()
  }
})

总结

Vuex是Vue生态中的重要一员,今天我们深入了解了一下Vuex的实现原理。相信大家在了解过后都会发现,其实Vuex的实现并不复杂(当然具体的开发下肯定还会有很多的细节)。了解Vuex的原理,可以让我们在开发使用时,对Vuex会一个更加清晰的认识,在出现bug时可以有更多的处理思路。

参考

juejin.cn/post/684490…