详解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 相关文章推荐
extjs grid设置某列背景颜色和字体颜色的实现方法
Sep 06 Javascript
js中数组排序sort方法的原理分析
Nov 20 Javascript
AspNet中使用JQuery boxy插件的确认框
May 20 Javascript
js为什么不能正确处理小数运算?
Dec 29 Javascript
类似于QQ的右滑删除效果的实现方法
Oct 16 Javascript
数组Array的一些方法(总结)
Feb 17 Javascript
原生JS实现不断变化的标签
May 22 Javascript
Vue精简版风格指南(推荐)
Jan 30 Javascript
JS实现判断数组是否包含某个元素示例
May 24 Javascript
微信小程序实现多选框全选与反全选及购物车中删除选中的商品功能
Dec 17 Javascript
angular8.5集成TinyMce5的使用和详细配置(推荐)
Nov 16 Javascript
JS代码实现页面切换效果
Jan 10 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
CakePHP去除默认显示的标题及图标的方法
2008/10/22 PHP
如何使用php判断服务器是否是HTTPS连接
2013/07/05 PHP
php结合ajax实现赞、顶、踩功能实例
2014/05/12 PHP
php 数据结构之链表队列
2017/10/17 PHP
23个超流行的jQuery相册插件整理分享
2011/04/25 Javascript
jquery应该如何来设置改变按钮input的onclick事件
2012/12/10 Javascript
js 字符串转换成数字的三种方法
2013/03/23 Javascript
Javascript的时间戳和php的时间戳转换注意事项
2013/04/12 Javascript
原生js操作checkbox用document.getElementById实现
2013/10/12 Javascript
javascript计时器详解
2015/02/28 Javascript
Node.js 去掉种子(torrent)文件里的邪恶信息
2015/03/27 Javascript
JS实现简单路由器功能的方法
2015/05/27 Javascript
JS验证IP,子网掩码,网关和MAC的方法
2015/07/02 Javascript
jquery实现的淡入淡出下拉菜单效果
2015/08/25 Javascript
浅谈Javascript中substr和substring的区别
2015/09/30 Javascript
用JS生成UUID的方法实例
2016/03/30 Javascript
JavaScript实现刷新不重记的倒计时
2016/08/10 Javascript
jQuery简单实现中间浮窗效果
2016/09/04 Javascript
AngularJS中关于ng-class指令的几种实现方式详解
2016/09/17 Javascript
微信小程序城市定位的实现实例(获取当前所在国家城市信息)
2017/05/17 Javascript
基于angular6.0实现的一个组件懒加载功能示例
2018/04/12 Javascript
使用vue-cli(vue脚手架)快速搭建项目的方法
2018/05/21 Javascript
JS检测浏览器开发者工具是否打开的方法详解
2020/10/02 Javascript
windows下python连接oracle数据库
2017/06/07 Python
Python判断字符串是否为字母或者数字(浮点数)的多种方法
2018/08/03 Python
Python访问MongoDB,并且转换成Dataframe的方法
2018/10/15 Python
python利用tkinter实现图片格式转换的示例
2020/09/28 Python
python实现移动木板小游戏
2020/10/09 Python
意大利体育用品网上商城:Nencini Sport
2016/08/18 全球购物
Nike墨西哥官网:Nike MX
2020/08/30 全球购物
服装采购员岗位职责
2014/03/15 职场文书
2015年健康教育工作总结
2015/04/10 职场文书
2017新年晚会开幕词
2016/03/03 职场文书
如何使用JavaScript策略模式校验表单
2021/04/29 Javascript
如何用threejs实现实时多边形折射
2021/05/07 Javascript
nginx常用配置conf的示例代码详解
2022/03/21 Servers