vuex的深入学习[基于vuex3]----篇(二)

store对象的创建

store的传递图

创建语句索引

  1. 创建vuex的语句为new Vuex.Store({...})
  2. Vuex的入口文件是index.js,store是index.js导出的store类
  3. store类是store.js文件中定义的。

Store的构造函数constructor

  1. 判断vuex是否被注入,就是将vue挂载在window对象上,自动检测window.Vue,如果有挂载,而且没有被注册过,则调用注册方法
javascript 复制代码
if (!Vue && typeof window !== 'undefined' && window.Vue) {
      install(window.Vue)
}
  1. 断言
    断言是否被注册,是否支持promise,断言类有没有被正确的实例化,这些断言语句,在vue build出来以后,报错信息会被忽略
javascript 复制代码
if (process.env.NODE_ENV !== 'production') {
      assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
      assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
      assert(this instanceof Store, `store must be called with the new operator.`)
}
  • 支持promise语法
  • 已经执行安装函数进行装载
  • 在webpack配置的时候,npm build会将process.env.NODE_ENV 设置为true
javascript 复制代码
function assert(condition, msg) {
    if(!condition) throw new Error(`[vuex] ${msg}`)
}
  1. 部分重要的容器
javascript 复制代码
this._committing = false // 表示状态标识,在严格模式下,防止非commit操作下,state被修改
this._actions = Object.create(null) // action函数的数组的对象,保存所有action回调函数。从null中创建对象,object.create(null)没有继承任何原型方法,也就是说他的原型链没有上一层,从而定义纯粹的对象
this._actionSubscribers = [] // 订阅action操作的函数数组,里面的每个函数,将在action函数被调用之间被调用,该功能类似于插件,和主功能无关。
this._mutations = Object.create(null) // mutation函数的数组的对象,保存所有的mutations回调函数
this._wrappedGetters = Object.create(null) //保存getter函数的函数数组对象容器
this._modules = new ModuleCollection(options) // 解析并生成模块树,通过树结构,保存配置文件内容
this._modulesNamespaceMap = Object.create(null) // 保存命名空间的模块对象,以便在辅助函数createNamespacedHelpers中快速定位到待命名空间的模块。
this._subscribers = []    // 订阅mutation操作的函数数组,里面的每个函数,将在commit执行后被调用,类似于插件,和主功能无关。
this._watcherVM = new Vue() // 定义一个vue对象,vue类将在调用vuex安装函数,install 的时候,被传递进来。

简洁版

javascript 复制代码
this._committing = false // 是否在进行提交状态标识
this._actions = Object.create(null) // acitons操作对象
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) // 模块命名空间map
this._subscribers = [] // 订阅函数集合,Vuex提供了subscribe功能
this._watcherVM = new Vue() // Vue组件用于watch监视变化

ModuleCollection

javascript 复制代码
this._modules = new ModuleCollection(options)

ModuleCollection函数主要是讲传入的options对象构造为一个module对象,并循环调用(this.register[key], rawModul, false)为其中的modules属性进行模块注册,使得其称为module对象后,最后options对象被构成一个完整的组件树,ModuleCollection类还提供了modules的更替功能。

constructor
javascript 复制代码
export default class ModuleCollection {
    constructor(rawRootModule) {
        this.register([], rawRootModule, false)
    }
}
  • 形参: rawRootModule,在store中new ModuleCollection(options)传入,而options是store的构造函数参数,就是new Vuex.Store({...})传入的参数
javascript 复制代码
{
    state, 
    getters,
    actions,
    mutations  
}
register
javascript 复制代码
register (path, rawModule, runtime = true) {
    if (process.env.NODE_ENV !== 'production') {
        assertRawModule(path, rawModule)
    }
    const newModule = new Module(rawModule, runtime)
    if (path.length === 0) {
        this.root = newModule
    } else {
        const parent = this.get(path.slice(0, -1))
        parent.addChild(path[path.length - 1], newModule)
    }
    if (rawModule.modules) {
        forEachValue(rawModule.modules, (rawChildModule, key) => {
        this.register(path.concat(key), rawChildModule, runtime)
        })
    }
}
参数解析
  • path是注册模块的层级关系数组,保存当前模块及各个祖先模块的名字。[a, b, c, 当前模块] => {modules: {a, modules: {b, module:{当前模块} }}}
  • rawModule是模块的配置参数,在定义模块的时候,开发者定义的模块配置内容,比如state, getter, actions等等
  • runtime标识是否是运行状态,在运行状态下,不能进行某些特定的操作。
assertRawModule
javascript 复制代码
if (process.env.NODE_ENV !== 'production') {
      assertRawModule(path, rawModule)
}
  • 对模块的配置信息进行断言
  • 判断了getters,mtations, actions等配置的数据类型是否正确。
const newModule = new Module(rawModule, runtime),找到module.js文件找到其构造函数
javascript 复制代码
  constructor (rawModule, runtime) {
    this.runtime = runtime
    this._children = Object.create(null) // 定义子模块对象容器
    this._rawModule = rawModule // 保存配置参数本身
    const rawState = rawModule.state // 从配置参数中获得了state这个参数
    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {} // 解析state,如果是函数,则运用工厂模式产生配置对象
  }
根据path模块判断当前是否为根模块
  1. 如果是根模块,则通过this.root进行保存

  2. 如果不是根模块

    • get方法传入的是路径数组,slice(0,-1)是为了去除本模块名,保留所有祖先模块的路径,从而获取夫模块。
    • get函数内部,使用了数组的reduce方法
    javascript 复制代码
    get (path) {
        return path.reduce((module, key) => {
          return module.getChild(key)
    }, this.root)
    }
    javascript 复制代码
      getChild (key) {
        return this._children[key]
      }
      addChild (key, module) {
        this._children[key] = module
      }
if(rawModule.modules)判断模块配置信息中,有没有子模块的配置

如果有,则递归调用注册函数register本身,传入的参数加上了子模块名的path模块层级数组,rawchildmodule是子模块配置文件,runtime是继承而来的运行标识符

获取dispatch和commit函数,并复写该函数

javascript 复制代码
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值绑定到vuex实例本身上,防止因为this的指向修改而被修改

这两个函数可以通过mapMutationsmapActions辅助函数转换为Vue中的普通函数,这时this指向Vue组件,而不是Vuex实例

后续代码

javascript 复制代码
this.strict = strict
const state = this._modules.root.state //根据根模块的state变量索引
installModule(this, state, [], this._modules.root)//安装根模块,设置commit,dispatch函数的重载,根据是否设置命名空间,设置参数前缀,将getter,commit,action放到相对应的容器中保存起来。
resetStoreVM(this, state)// 借助vue的watch功能和computed功能,实现数据的响应式
plugins.forEach(plugin => plugin(this)) // 插件的注册,和主功能无关
const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
if (useDevtools) { // vue调试工具的处理
    devtoolPlugin(this)
}
installModule(模块安装函数)
参数介绍
  • store: store对象实例,new Vuex.store({...})生成的对象
  • rootState: 根模块的state对象
  • path: 当前模块所处的层级数组
  • module: 模块对象
  • hot
实例代码
javascript 复制代码
  function installModule (store, rootState, path, module, hot) {
  const isRoot = !path.length // 判断当前模块是否为根模块
  const namespace = store._modules.getNamespace(path) //  获取模块的命名空间
  if (module.namespaced) {
    store._modulesNamespaceMap[namespace] = module
  }
  if (!isRoot && !hot) {
    const parentState = getNestedState(rootState, path.slice(0, -1))
    const moduleName = path[path.length - 1]
    store._withCommit(() => {
      Vue.set(parentState, moduleName, module.state)
    })
  }
}

补充
getNamespace

javascript 复制代码
//从跟模块中出发,根据给出的路径数组,递归每一个层级的模块
getNamespace(path) {
    let module = this.root
    return path.reduce((namespace, key) => {
        module = module.getChild(key)
        return namespace + (module.namespaced ? key + "/" : '')
    }, '')
}
json 复制代码
{
  moduleA:{
    namespaced:true,
    modules:{
      moduleB:{
          namespaced:false,
          modules:{
            moduleC:{
              namespaced:true,
            }
          }
        }
    }
  }
}
相关推荐
量子-Alex24 分钟前
【多模态聚类】用于无标记视频自监督学习的多模态聚类网络
学习·音视频·聚类
轻口味26 分钟前
命名空间与模块化概述
开发语言·前端·javascript
吉大一菜鸡29 分钟前
FPGA学习(基于小梅哥Xilinx FPGA)学习笔记
笔记·学习·fpga开发
前端小小王1 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀2 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
yg_小小程序员2 小时前
vue3中使用vuedraggable实现拖拽
typescript·vue
爱吃西瓜的小菜鸡3 小时前
【C语言】判断回文
c语言·学习·算法