详解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 相关文章推荐
javascript不同页面传值的改进版
Sep 30 Javascript
IE8 中使用加速器(Activities)
May 14 Javascript
Raphael一个用于在网页中绘制矢量图形的Javascript库
Jan 08 Javascript
基于BootStrap的图片轮播效果展示实例代码
May 23 Javascript
js实现浏览器倒计时跳转页面效果
Aug 12 Javascript
Vue.js每天必学之内部响应式原理探究
Sep 07 Javascript
js实现对table的增加行和删除行的操作方法
Oct 13 Javascript
js 点击a标签 获取a的自定义属性方法
Nov 21 Javascript
JAVA中截取字符串substring用法详解
Apr 14 Javascript
使用Ajax和Jquery配合数据库实现下拉框的二级联动的示例
Jan 25 jQuery
详解关于vue-area-linkage走过的坑
Jun 27 Javascript
详解Anyscript开发指南绕过typescript类型检查
Sep 23 Javascript
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
IIS6.0中配置php服务全过程解析
2013/08/07 PHP
PHP对象递归引用造成内存泄漏分析
2014/08/28 PHP
PHP制作登录异常ip检测功能的实例代码
2016/11/16 PHP
PHP面向对象程序设计OOP继承用法入门示例
2016/12/27 PHP
Yii2语言国际化自动配置详解
2018/08/22 PHP
Aster vs KG BO3 第三场2.18
2021/03/10 DOTA
jquery插件制作 图片走廊 gallery
2012/08/17 Javascript
jQuery中[attribute^=value]选择器用法实例
2014/12/31 Javascript
后台获取ZTREE选中节点的方法
2015/02/12 Javascript
jQuery实现contains方法不区分大小写的方法
2015/02/13 Javascript
JavaScript正则表达式中的ignoreCase属性使用详解
2015/06/16 Javascript
跟我学习javascript的执行上下文
2015/11/18 Javascript
js 弹出对话框(遮罩)透明,可拖动的简单实例
2016/07/11 Javascript
AngularJS动态加载模块和依赖的方法分析
2016/11/08 Javascript
详解基于javascript实现的苹果系统底部菜单
2016/12/02 Javascript
微信小程序技巧之show内容展示,上传文件编码问题
2017/01/23 Javascript
jQuery Pagination分页插件_动力节点Java学院整理
2017/07/17 jQuery
Node.js自定义实现文件路由功能
2017/09/22 Javascript
利用nvm管理多个版本的node.js与npm详解
2017/11/02 Javascript
JavaScript 反射和属性赋值实例解析
2019/10/28 Javascript
Vue ElementUI实现:限制输入框只能输入正整数的问题
2020/07/31 Javascript
python练习程序批量修改文件名
2014/01/16 Python
简单总结Python中序列与字典的相同和不同之处
2016/01/19 Python
在python3中使用shuffle函数要注意的地方
2020/02/28 Python
python 模拟登录B站的示例代码
2020/12/15 Python
H5 canvas中width、height和style的宽高区别详解
2018/11/02 HTML / CSS
html5 video标签屏蔽右键视频另存为的js代码
2013/11/12 HTML / CSS
宝拉珍选美国官网:Paula’s Choice美国
2018/01/07 全球购物
如何选择使用结构还是类
2014/05/30 面试题
机电一体化专业应届本科生求职信
2013/09/27 职场文书
2014年小学元旦活动方案
2014/02/12 职场文书
挂靠协议书范本
2014/04/22 职场文书
2016个人廉洁自律承诺书
2016/03/25 职场文书
基于Nginx实现限制某IP短时间访问次数
2021/03/31 Servers
python周期任务调度工具Schedule使用详解
2021/11/23 Python
Android自定义scrollview实现回弹效果
2022/04/01 Java/Android