详解vuex之store源码简单解析


Posted in Javascript onJune 13, 2019

关于vuex的基础部分学习于https://3water.com/article/163008.htm

使用Vuex的时候,通常会实例化Store类,然后传入一个对象,包括我们定义好的actions、getters、mutations、state等。store的构造函数:

export class Store {
 constructor (options = {}) {
  // 若window内不存在vue,则重新定义Vue
  if (!Vue && typeof window !== 'undefined' && window.Vue) {
   install(window.Vue)
  }

  if (process.env.NODE_ENV !== 'production') {
   // 断言函数,来判断是否满足一些条件
   // 确保 Vue 的存在
   assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
   // 确保 Promsie 可以使用
   assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
   assert(this instanceof Store, `store must be called with the new operator.`)
  }

  // 解构赋值,拿到options里的plugins和strict
  const {
   plugins = [],
   strict = false
  } = options

  // 创建内部属性
  // 标志一个提交状态,作用是保证对 Vuex 中 state 的修改只能在 mutation 的回调函数中,而不能在外部随意修改 state
  this._committing = false 
  // 用来存储用户定义的所有的actions
  this._actions = Object.create(null)
  this._actionSubscribers = []
  // 用来存储用户定义所有的mutatins
  this._mutations = Object.create(null)
  // 用来存储用户定义的所有getters 
  this._wrappedGetters = Object.create(null)
  // 用来存储所有的运行时的 modules
  this._modules = new ModuleCollection(options)
  this._modulesNamespaceMap = Object.create(null)
  // 用来存储所有对 mutation 变化的订阅者
  this._subscribers = []
  // 一个 Vue对象的实例,主要是利用 Vue 实例方法 $watch 来观测变化的
  this._watcherVM = new Vue()

  // 把Store类的dispatch和commit的方法的this指针指向当前store的实例上
  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)
  }

  // 是否开启严格模式
  this.strict = strict

  const state = this._modules.root.state

  // Vuex的初始化的核心,其中,installModule方法是把我们通过options传入的各种属性模块注册和安装;
  // resetStoreVM 方法是初始化 store._vm,观测 state 和 getters 的变化;最后是应用传入的插件。
  installModule(this, state, [], this._modules.root)

  resetStoreVM(this, state)
  plugins.forEach(plugin => plugin(this))

  const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
  if (useDevtools) {
   devtoolPlugin(this)
  }
 }

Vuex本身是单一状态树,应用的所有状态都包含在一个大对象内,随着我们应用规模的不断增长,这个Store变得非常臃肿。为了解决这个问题,Vuex允许我们把store分模块。每一个模块包含各自的state、mutations、actions和getters,甚至还可以嵌套模块。

接下来看installModule方法:

function installModule (store, rootState, path, module, hot) {
 // 通过path数组的长度判断是否为根
 const isRoot = !path.length
 const namespace = store._modules.getNamespace(path)

 // register in namespace map
 if (module.namespaced) {
  store._modulesNamespaceMap[namespace] = module
 }

 // 第一次调用时,path为空,不进入if
 // 递归调用installModule安装子模块时,将执行该代码块
 if (!isRoot && !hot) {
  const parentState = getNestedState(rootState, path.slice(0, -1))
  // 模块名
  const moduleName = path[path.length - 1]
  // 把当前模块的state添加到parentState中。具体解析见下
  store._withCommit(() => {
   Vue.set(parentState, moduleName, module.state)
  })
 }

 const local = module.context = makeLocalContext(store, namespace, path)

 // 对mutations、actions、getters进行注册
 module.forEachMutation((mutation, key) => {
  const namespacedType = namespace + key
  registerMutation(store, namespacedType, mutation, local)
 })

 module.forEachAction((action, key) => {
  const type = action.root ? key : namespace + key
  const handler = action.handler || action
  registerAction(store, type, handler, local)
 })

 module.forEachGetter((getter, key) => {
  const namespacedType = namespace + key
  registerGetter(store, namespacedType, getter, local)
 })

 // 遍历modules,递归调用installModule安装子模块
 module.forEachChild((child, key) => {
  installModule(store, rootState, path.concat(key), child, hot)
 })
}
store的_withCommit方法定义是这样的:

 _withCommit (fn) {
  const committing = this._committing
  this._committing = true
  fn()
  this._committing = committing
 }

Vuex中所有对state的修改都会用_withCommit函数包装,保证在同步修改state的过程中this._committing的值始终为true。这样当我们观测 state的变化时,如果this._committing的值不为true,则能检查到这个状态修改是有问题的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
IE6弹出“已终止操作”的解决办法
Nov 27 Javascript
window.open的页面如何刷新(父页面)上层页面
Dec 28 Javascript
js实现刷新iframe的方法汇总
Apr 27 Javascript
Jqgrid之强大的表格插件应用
Dec 02 Javascript
JavaScript ParseFloat()方法
Dec 18 Javascript
详解Weex基于Vue2.0开发模板搭建
Mar 20 Javascript
JavaScript该如何学习 怎样轻松学习JavaScript
Jun 12 Javascript
angular.js和vue.js中实现函数去抖示例(debounce)
Jan 18 Javascript
vue实现新闻展示页的步骤详解
Apr 11 Javascript
layer插件实现在弹出层中弹出一警告提示并关闭弹出层的方法
Sep 24 Javascript
vue导入.md文件的步骤(markdown转HTML)
Dec 31 Vue.js
Vue3 Composition API的使用简介
Mar 29 Vue.js
vue store之状态管理模式的详细介绍
Jun 13 #Javascript
微信小程序页面间跳转传参方式总结
Jun 13 #Javascript
微信小程序位置授权处理方法
Jun 13 #Javascript
json数据格式常见操作示例
Jun 13 #Javascript
微信小程序实现渐入渐出动画效果
Jun 13 #Javascript
微信小程序前端自定义分享的实现方法
Jun 13 #Javascript
javascript数组常见操作方法实例总结【连接、添加、删除、去重、排序等】
Jun 13 #Javascript
You might like
谏山创故乡大分县日田市水坝将设立《进击的巨人》立艾伦、三笠以及阿尔敏的铜像!
2020/03/06 日漫
树型结构列出指定目录里所有文件的PHP类
2006/10/09 PHP
PHP定时执行任务的3种方法详解
2015/12/21 PHP
理解PHP中的Session及对Session有效期的控制
2016/01/08 PHP
PHP数组的定义、初始化和数组元素的显示实现代码
2016/11/05 PHP
PHP面向对象程序设计方法实例详解
2016/12/24 PHP
PHP+JQUERY操作JSON实例
2017/03/23 PHP
基于laravel Request的所有方法详解
2019/09/29 PHP
网页右键ie不支持event.preventDefault和event.returnValue (需要加window)
2013/02/22 Javascript
多次注册事件会导致一个事件被触发多次的解决方法
2013/08/12 Javascript
浅析Javascript中bind()方法的使用与实现
2016/05/30 Javascript
JavaScript中日期函数的相关操作知识
2016/08/03 Javascript
JS中script标签defer和async属性的区别详解
2016/08/12 Javascript
浅谈jquery采用attr修改form表单enctype不起作用的问题
2016/11/25 Javascript
原生js实现焦点轮播图效果
2017/01/12 Javascript
JavaScript运行原理分析
2018/02/09 Javascript
Nodejs核心模块之net和http的使用详解
2019/04/02 NodeJs
koa+mongoose实现简单增删改查接口的示例代码
2019/05/13 Javascript
java和js实现的洗牌小程序
2019/09/30 Javascript
H5+css3+js搭建带验证码的登录页面
2020/10/11 Javascript
[14:20]刀塔大凶女神互压各路奇葩屌丝
2014/05/16 DOTA
[55:35]DOTA2-DPC中国联赛 正赛 CDEC vs Dragon BO3 第二场 1月22日
2021/03/11 DOTA
Python生成随机数的方法
2014/01/14 Python
python检测服务器是否正常
2014/02/16 Python
跟老齐学Python之重回函数
2014/10/10 Python
python编程开发之类型转换convert实例分析
2015/11/13 Python
python中字符串比较使用is、==和cmp()总结
2018/03/18 Python
Python3实现发送邮件和发送短信验证码功能
2020/01/07 Python
html5定位获取当前位置并在百度地图上显示
2014/08/22 HTML / CSS
使用phonegap克隆和删除联系人的实现方法
2017/03/31 HTML / CSS
艺术系大学生毕业个人自我评价
2013/09/19 职场文书
机电专业求职信
2014/06/14 职场文书
大学教师师德师风演讲稿
2014/08/22 职场文书
2015年全国助残日活动方案
2015/05/04 职场文书
详解使用 CSS prefers-* 规范提升网站的可访问性与健壮性
2021/05/25 HTML / CSS
MySQL普通表如何转换成分区表
2022/05/30 MySQL