一、Vuex是什么

    Vuex是专门为Vuejs应用程序设计的状态管理工具。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生改变。它集中于MVC模式中的Model层,规定所有数据操作必须通过 action - mutation - statechange 的流程来进行,再结合Vue的数据视图双向绑定特效来实现页面的展示更新。

    1.Vuex框架运行流程

     

  如图我们可以看到Vuex为Vue Components建立起了一个完整的生态圈,包括开发中的api调用,vuex的简单分析:

   Vue组件接收交互行为,调用dispatch方法触发action相关处理,若页面状态需要改变,则调用commit方法提交mutation修改state,通过getters获取到state新值,提供了mapState、MapGetters、MapActions、mapMutations等辅助函数给开发在vm中处理store,重新渲染Vue Components,页面随之更新。

二、目录结构介绍

  

  Vuex提供了非常强大的状态管理功能,源码代码量却不多,目录结构的划分也很清晰。先大体介绍下各个目录文件的功能:

    module:提供module对象与module对象树的创建功能;

    plugins:提供开发辅助插件,如“时间穿梭”功能,state修改的日志记录功能等;

    helpers.js:提供action、mutation以及getters的查找API;

    index.js:是源码主入口文件,提供store的各module构建安装;

    mixin.js:提供了store在Vue实例上的装载注入;

    util.js:提供了工具方法如find、deepCopy、forEachValue以及assert等方法。

 三、初始化装载与注入

了解大概的目录及对应功能后,下面开始进行源码分析。

1.装载实例

 先看一个简单的:

   

   store.js文件中,加载Vue框架,创建并导出一个空配置的store对象实例。

   

   然后在index.js中,正常初始化一个页面根级别的Vue组件,传入这个自定义的store对象。

   那么问题来了,使用Vuex只需要Vue.use(Vuex),并在Vue的配置中传入一个store对象的实例,store是如何实现注入的?

2.装载分析

 index.js文件代码执行开头,定义局部变量,用于判断是否已经装载和减少全局作用域查找。

let Vue

 然后判断若处于浏览器环境下且加载过Vue,则执行install方法。

//auto install in dist mode
if(typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}

 install方法将Vuex装载到Vue对象上, Vue.use(Vuex) 也是通过它执行,先看下Vue.use方法实现:

function (plugin:Function | Object) {
  /* istanbul ignore if */
  if(plugin.installed) {
    return
  }
  // additional parameters
  const args = toArray(arguments,1)
  args.unshift(this)
  if(typeof plugin.install === 'function') {
    //实际执行插件的install方法
    plugin.install.apply(plugin , args)
  }else {
    plugin.apply(null , args)
  }
  plugin.installed = true
  return this
}

如果是首次加载,将局部Vue变量赋值为全局的Vue对象,并执行applyMixin方法,install实现如下:

function install (_Vue) {
  if(Vue) {
    console.error (
      '[vuex] already installed. Vue.use(Vuex) should be called only once.'
    )
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}

来看下applyMinxin方法内部代码。如果是2.x以上版本,可以使用hook的形式进行注入,或使用封装并替换Vue对象原型的_init方法,实现注入。

export default function (Vue) {
  //获取vue版本号
  const version = Number(Vue.version.split('.')[0])
  //判断版本是否大于2
  if(version >= 2) {
    const useInit = Vue.config._lifecycleHooks.indexOf('init') > -1
    Vue.mixin(useInit ? { init: vuexInit } : { beforeCreate: vuexInit })
  } else {
    // override init and inject vuex init procedure
    // for 1.x backwards compatibility.
    const _init = Vue.prototype._init
    Vue.prototype._init = function (options = {}) {
      options.init = options.init
      ? [vuexInit].concat(options.init)
      : vuexInit
      _init.call(this, options)
    }
  }
}
function vuexInit () {
  //通过 this.$options 对象获取任何数据和方法
  const options = this.$options
  //store injection
  if(options.store) {
    this.$store = options.store
  } else if (options.parent && options.parent.$store) {
    this.$store = options.parent.$store
  }
}

这段代码的作用就是在 Vue 的生命周期中的初始化(1.0 版本是 init,2.0 版本是 beforeCreated)钩子前插入一段 Vuex 初始化代码。这里做的事情很简单——将初始化Vue根组件时传入的store设置到this对象的$store属性上,子组件从其父组件引用$store属性,层层嵌套进行设置,在任意组件中执行this.$store都能找到装载的那个store对象。

几张图理解下store的传递。

  页面Vue结构:

      

  对应store流向:

      

四、store对象构造

  上面对Vuex框架的装载以及注入自定义store对象进行了分析,接下来详细分析store对象的内部功能和具体实现,那么问题又来了,为什么actions、getters、mutations中能从arguments[0]中拿到store的相关数据?

  store对象实现逻辑比较复杂,先看下构造方法的整体逻辑流程来帮助后面的理解:

  

4.1 环境判断

 开始分析store的构造函数,分小节逐函数逐行的分析其功能。

constructor (options = {}) {
  assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
  assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
}

 在store构造函数中执行环境判断,以下都是Vuex工作的必要条件:

  1.已经执行安装函数进行装载;

  2.支持Promise语法

 

 assert函数是一个简单的断言函数的实现,一行代码即可实现

function assert (condition, msg) {
  if(!condition) throw new Error(`[vuex] ${msg}`)
}

4.2 数据初始化、module树构造

 环境判断后,根据new构造传入的options或默认值,初始化内部数据。

const {
  state = {},
  plugins = [],
  strict = false
} = options

//store状态初始化
this._committing = false  // 是否在进行提交状态标识
this._actions = Object.create(null)  // actions操作对象
this._mutations = Object.create(null)  // mutations操作对象
this._wrappedGetters = Object.create(null)  // 封装后的getters集合对象
this._modules = new ModuleCollection(options)  // vuex支持store分模块传入,存储分析后的modules
this._modulesNamespaceMap = Object.create(null)  // 模块命名空间
this._subscribers = []  // 订阅函数集合,Vuex提供了subscribe功能
this._watcherVM = new Vue()  // Vue组件用于watch监视变化

调用 new Vuex.store(options)时传入的options对象,用于构造ModuleCollection类,下面看看其功能

constructor(rawRootModule) {
  // register root module (Vuex.store options)
  this.root = new Module(rawRootModule, false)

  // register all nested modules
  if(rawRootModule.modules) {
    forEachValue(rawRootModule.modules, (rawModule, key) => {
      this.register([key], rawModule, false)
    })
  }
}

ModuleCollection主要将传入的options对象整个构造为一个modele对象,并循环调用 this.register([key], rawModule, false) 为其中的modules属性进行模块注册,使其都成为module对象,最后options对象被构造成一个完整的组件树。ModuleCollection类还提供了modules的更替功能,详细实现可以查看源文件 module-collection.js

4.3 dispatch与commit设置

 继续回到store的构造函数代码。

// bind commit and dispatch to self
const store = this;
const { dispatch, commit } = this;

this.dispatch = function boundDispatch (type, payload) {
  return dispatch.call(store, type, payload)
}

this.commit = function boundCommit (type, payload, options) {
  return commit.call(store, type, payload, options)
}

 封装替换原型中的dispatch和commit方法,将this指向当前store对象。

 dispatch和commit方法具体实现如下:

dispatch (_type, _payload) {
  //check object-style dispatch
  const {
    type,
    payload
  } = unifyObjectStyle(_type, _payload)  // 配置参数处理
  
  // 当前type下所有action处理函数集合
  const entry = this._actions[type]
  if (!entry) {
    console.error(`[vuex] unknown action type: ${type}`)
    return
  }
  return entry.length > 1
    ? Promise.all(entry.map(handler => handler(payload)))
    : entry[0](payload)
}

 前面提到,dispatch的功能是触发并传递一些参数(payload)给对应的type的action。因为其支持2种调用方法,所以在dispatch中,先进行参数(payload)给对应type的action。因为其支持2种调用方法,所以在dispatch中,先进行参数的适配处理,然后判断action type是否存在,若存在就逐个执行(注:)

  抄的这里,还没抄完

   

Vuex原理详解的更多相关文章

  1. I2C 基础原理详解

    今天来学习下I2C通信~ I2C(Inter-Intergrated Circuit)指的是 IC(Intergrated Circuit)之间的(Inter) 通信方式.如上图所以有很多的周边设备都 ...

  2. Zigbee组网原理详解

    Zigbee组网原理详解 来源:互联网 作者:佚名2015年08月13日 15:57   [导读] 组建一个完整的zigbee网状网络包括两个步骤:网络初始化.节点加入网络.其中节点加入网络又包括两个 ...

  3. 块级格式化上下文(block formatting context)、浮动和绝对定位的工作原理详解

    CSS的可视化格式模型中具有一个非常重要地位的概念——定位方案.定位方案用以控制元素的布局,在CSS2.1中,有三种定位方案——普通流.浮动和绝对定位: 普通流:元素按照先后位置自上而下布局,inli ...

  4. SSL/TLS 原理详解

    本文大部分整理自网络,相关文章请见文后参考. SSL/TLS作为一种互联网安全加密技术,原理较为复杂,枯燥而无味,我也是试图理解之后重新整理,尽量做到层次清晰.正文开始. 1. SSL/TLS概览 1 ...

  5. 锁之“轻量级锁”原理详解(Lightweight Locking)

    大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意. 原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖 ...

  6. [转]js中几种实用的跨域方法原理详解

    转自:js中几种实用的跨域方法原理详解 - 无双 - 博客园 // // 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同 ...

  7. 节点地址的函数list_entry()原理详解

    本节中,我们继续讲解,在linux2.4内核下,如果通过一些列函数从路径名找到目标节点. 3.3.1)接下来查看chached_lookup()的代码(namei.c) [path_walk()> ...

  8. WebActivator的实现原理详解

    WebActivator的实现原理详解 文章内容 上篇文章,我们分析如何动态注册HttpModule的实现,本篇我们来分析一下通过上篇代码原理实现的WebActivator类库,WebActivato ...

  9. Influxdb原理详解

    本文属于<InfluxDB系列教程>文章系列,该系列共包括以下 15 部分: InfluxDB学习之InfluxDB的安装和简介 InfluxDB学习之InfluxDB的基本概念 Infl ...

随机推荐

  1. win10简单方法安装杜比v4音效!win10 1909适用!

    先下载这个! 链接: https://pan.baidu.com/s/1zAOOf-1aCJsjBgy36SiGWA 密码: s9n7 这个是杜比V4文件,257MB大小,适用32位64位系统!下 载 ...

  2. CS61A Homework: Church Numerals

    Church Numerals Nagging 南大的 SICP 实际上是 Berkeley CS61A 的 clone ,所以我有幸做到了这个 Homework02. 此外要感谢选课系统,让我一个工 ...

  3. Shell Scripting 笔记

    Shell Scripting Tutorial Variables in the Bourne shell do not have to be declared, as they do in lan ...

  4. logstash-配置文件详解

      kafka  将 kafka topic 中的数据读取为事件   kafka{ bootstrap_servers=> "kafka01:9092,kafka02:9092,kaf ...

  5. Python中while循环初识

    基本结构 ​ while 条件: ​ 循环体 基本原理: ​ 1.先判断条件是否为True ​ 2.如果是True进入循环体 ​ 3.执行到循环体的底部 ​ 4.继续判断条件,条件为True,再次进入 ...

  6. Java中try()...catch()用法

    在stackoverflow偶尔看到的一个关于try()...catch()的用法,通常我们使用try...catch()捕获异常的,如果遇到类似IO流的处理,要在finally部分关闭IO流,当然这 ...

  7. 人生第一篇博客 , 当然是经典的 "Hello World"

    package com.cqvie.Hello; public class FirstDemo {  public static void main(String[] args) {  System. ...

  8. redis SETBIT命令原理

    redis SETBIT命令原理 /* SETBIT key offset bitvalue */ bitset的使用位来替代传统的整形数字,标识某个数字对应的值是否存在 底层有一个byte[]来实现 ...

  9. 【4】TensorFlow光速入门-保存模型及加载模型并使用

    本文地址:https://www.cnblogs.com/tujia/p/13862360.html 系列文章: [0]TensorFlow光速入门-序 [1]TensorFlow光速入门-tenso ...

  10. K8S 使用 SideCar 模式部署 Filebeat 收集容器日志

    对于 K8S 内的容器日志收集,业内一般有两种常用的方式: 使用 DaemonSet 在每台 Node 上部署一个日志收集容器,用于收集当前 Node 上所有容器挂载到宿主机目录下的日志 使用 Sid ...