一、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. C语言/C++编程学习:送给考计算机二级的同学:公共基础知识总结!

    数据结构与算法 1.算法 算法:是指解题方案的准确而完整的描述. 算法不等于程序,也不等计算机方法,程序的编制不可能优于算法的设计. 算法的基本特征:是一组严谨地定义运算顺序的规则,每一个规则都是有效 ...

  2. package wang/test is not in GOROOT (/usr/local/go/src/wang/test)

    如果要用 gopath模式 引入包 从src目录下开始引入 需要关闭 go mod 模式 export GO111MODULE=off 如果使用go mod 模式 export GO111MODULE ...

  3. lumen 添加配置

    app同级目录新建config目录 添加配置文件 bootstrap/app.php里面加载 $app->configure('options');使用 $router->get('/', ...

  4. python爬虫获取下一页

    from time import sleep import faker import requests from lxml import etree fake = faker.Faker() base ...

  5. Linux运维学习第二周记

    1 梨花淡白柳深青, 2 柳絮飞时花满城. 3 惆怅东栏一株雪, 4 人生看得几清明. 老牛望春满地绿! 第二周直接要起飞了! 仍然是最基础最基础的东西,但也是特别多,的别重要! 第二周学记 1.Li ...

  6. Linux基础命令之.命令

    . 点命令 .命令等同source 可以让配置文件被读到进程中,立刻生效

  7. ORACL-重做日志详解

    作用:oracle重做日志主要目的是数据恢复及数据修改效率提升 前言:oracle重做日志主相关视图: 1.v$log记录当前数据库重做日志组 2.v$logfie记录当前数据库重做日志组状态.类型等 ...

  8. node.js操作MySQL数据库

    MySQL数据库作为最流行的开源数据库.基本上是每个web开发者必须要掌握的数据库程序之一了. 基本使用 node.js上,最受欢迎的mysql包就是mysql模块. npm install mysq ...

  9. GPRS DTU工作的原理与应用场景

    GPRS DTU是属于物联网无线数据终端设备的中一种,它主要是利用公用运营商的GPRS网络(又称G网)来为用户提供无线长距离数据传输的功能.一般都是采用的高性能工业级8/16/32位通信处理器和工业级 ...

  10. SpringBoot第五集:整合监听器/过滤器和拦截器(2020最新最易懂)

    SpringBoot第五集:整合监听器/过滤器和拦截器(2020最新最易懂) 在实际开发过程中,经常会碰见一些比如系统启动初始化信息.统计在线人数.在线用户数.过滤敏/高词汇.访问权限控制(URL级别 ...