vuex源码

81 阅读3分钟

一、问题:

1、vuex是如何注入的

2、vuex是实现响应的

3、vuex中state, getters,mutations, actions的注册和触发

二、vuex的注入

vue中使用vuex, 实际上就是调用vuex中install方法

// 判断之前vue中是否注册过vuex,如果注册过,提示警告,如果没有调用applyMixin
function install (_Vue) {
  if (Vue && _Vue === Vue) {
    if (__DEV__) {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}
// applyMinxin中, 其实就是通过vue的mixin方法, 在beftoreCreate中注入$store
// new Vue({store}), 挂载在vue中组件就可以通过this.$option.store访问到store
// 子组件则可以通过访问父组件的$store得到, this.$options.parent.$store
function applyMixin(Vue) {
  const version = Number(Vue.version.split('.')[0])
​
  if (version >= 2) {
    Vue.mixin({ 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)
    }
  }
​
  /**
   * Vuex init hook, injected into each instances init hooks list.
   */
​
  function vuexInit () {
    const options = this.$options
    // store injection
    if (options.store) {
      this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
    }
  }
}

三、vuex中store

vue.use(vuex)只是给vuex注入给vuex, vuex的实现在store中, export default new vuex.Store({...})

vuex中store的基本构成

export class Store {
  constructor (options = {}) {
    // 如果之前没有使用Vue.use(Vux)并且window中挂载有Vue, 进行vuex的注册
    if (!Vue && typeof window !== 'undefined' && window.Vue) {
      install(window.Vue)
    }
    
    this._committing = false  // 提交状态  
    this._actions = Object.create(null) // actions的注册列表
    this._actionSubscribers = []  // actionSubScribers的注册列表
    this._mutations = Object.create(null) // mutations的注册列表
    this._wrappedGetters = Object.create(null) // getter的注册列表
    this._modules = new ModuleCollection(options) // modules的注册列表
    this._modulesNamespaceMap = Object.create(null) //含有namespaced的module注册列表
    this._subscribers = [] // subscriber的注册列表
    this._watcherVM = new Vue() //为watch提供监听方法this._watcherVM.$watch
    this._makeLocalGettersCache = Object.create(null) // namespaced的module中getters的代理缓存
​
    //绑定commit和dispatch到本身
    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)
    }
​
    // 注册根module, 循环注册子module, 收集模块的getter, mutation, action
    installModule(this, state, [], this._modules.root)
​
    // 初始化store、getters的数据,实现响应式, 
    resetStoreVM(this, state)
  }
​
  // 当访问store.state时候, 实际访问的为store._vm._data.$$state  
  get state () {
    return this._vm._data.$$state
  }
​
  // 当设置store.state时候, 提示报错, 不允许设置 
  set state (v) {
    if (__DEV__) {
      assert(false, `use store.replaceState() to explicit replace store state.`)
    }
  }
 
  // commit函数的实现:执行对应type的mutations函数,并执行subscribers函数
  commit (_type, _payload, _options) {}
​
  // dispatch函数的实现: 执行对应type的actions注册函数,并执行subscribeAction函数
  dispatch (_type, _payload) {}
​
   //注册 subscribers函数
  subscribe (fn, options) {}
​
   //注册 subscribeAction函数
  subscribeAction (fn, options) {}
   
   //注册 watch方法,响应式监听fn的返回, 当值改变时调用回调函数
  watch (getter, cb, options) {}  }
​
   //替换store根状态
  replaceState (state) {}
​
   //注册module模块
  registerModule (path, rawModule, options = {}) {}
​
  // 移除module模块
  unregisterModule (path) {}
​
  // module中是否含有该模块
  hasModule (path) {}
​
  // 热替换新的action和mutation
  hotUpdate (newOptions) {}
​
  // 合法更改store中的数据
  _withCommit (fn) {
    const committing = this._committing
    this._committing = true
    fn()
    this._committing = committing
  }
}

installModule的实现

function installModule (store, rootState, path, module, hot) {
  const isRoot = !path.length
  //getNamespace ,如果模块的namespaced为true, 则返回类似key/key格式,为false则返回 "" 
  const namespace = store._modules.getNamespace(path)
  
​
  // 判断是否存在相同namespace的模块
  if (module.namespaced) {
    if (store._modulesNamespaceMap[namespace] && __DEV__) {
      console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`)
    }
    store._modulesNamespaceMap[namespace] = module
  }
​
  // 子模块热更新注册state数据,  
  if (!isRoot && !hot) {
    // 找到子模块的父state,   
    const parentState = getNestedState(rootState, path.slice(0, -1))
    // 获取子模块的模块名
    const moduleName = path[path.length - 1]
    store._withCommit(() => {
      // 父模块的state注册上子模块的state,   
      // 例store.state.module1.count  
      // 父模块state.子模块名.子模块state中属性 
      Vue.set(parentState, moduleName, module.state)
    })
  }
​
  // 获取当前模块的getters, state, mutation, action, 
  const local = module.context = makeLocalContext(store, namespace, path)
​
  // 遍历模块的mutations中的方法, 按照type类型放入store._mutations中
  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })
​
  // 遍历模块的action中的方法, 按照type类型放入store._actions中 
  module.forEachAction((action, key) => {
    const type = action.root ? key : namespace + key
    const handler = action.handler || action
    registerAction(store, type, handler, local)
  })
​
  
  // 遍历模块中的getter的属性,放入store._wrappedGetters中
  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)
  })
}

makeLocalContext的实现

// 如果没有注册namespaced,则直接使用store.dispatch, store.commit, store.getter即可
function makeLocalContext (store, namespace, path) {
  const noNamespace = namespace === ''
​
  const local = {
    dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
      // 这里主要兼容处理参数, commit有两种传参方式, 
      // 1、commit("ADD_COUNT", 1);
      // 2、commit({type: "ADD_COUNT", num: 1});  
      const args = unifyObjectStyle(_type, _payload, _options)
      const { payload, options } = args
      let { type } = args
​
      // 非根节点在type添加前缀, 前缀namespace依赖模块的namespanced是否为true
      if (!options || !options.root) {
        type = namespace + type
      }
      // 底层还是调用store.dispatch
      return store.dispatch(type, payload)
    },
​
    // 同上  
    commit: noNamespace ? store.commit : (_type, _payload, _options) => {
      const args = unifyObjectStyle(_type, _payload, _options)
      const { payload, options } = args
      let { type } = args
​
      if (!options || !options.root) {
        type = namespace + type
      }
​
      store.commit(type, payload, options)
    }
  }
​
  // 当前模块的getters和state需要进行拦截处理
  Object.defineProperties(local, {
    getters: {
      get: noNamespace
        ? () => store.getters
        : () => makeLocalGetters(store, namespace)
    },
    state: {
      get: () => getNestedState(store.state, path)
    }
  })
​
  return local
}
​
// 问题: 为什么要进行拦截, 当namespace为true, 页面中使用getters还会把模块带上,store.getters["module/name"],无需代理
//  get答: 这里local的是为在子模块如调用自身getters无需加上模块名
// mutations: { ADD_NUM({getters}) { getters.key } }
// state在store的储存方式为store.state.子模块名 = 子模块.state
// getter在store的储存方式为store.getters[namespaced+key] = getter
function makeLocalGetters (store, namespace) {
  if (!store._makeLocalGettersCache[namespace]) {
    const gettersProxy = {}
    const splitPos = namespace.length
    Object.keys(store.getters).forEach(type => {
      // getters中可能有多个模块, 如果不是当前模块的, 则不处理
      if (type.slice(0, splitPos) !== namespace) return
​
      // 注册getters的时候,注册当前key的值如下,
      // const namespacedType = namespace + key
      // 移除namespacd,剩下的key,就是注册到当前模块getter的key值   
      const localType = type.slice(splitPos)
​
      // 设置代理,当子模块中访问当子模块的属性, 实际访问的是store.getters[type]
      Object.defineProperty(gettersProxy, localType, {
        get: () => store.getters[type],
        enumerable: true
      })
    })
    store._makeLocalGettersCache[namespace] = gettersProxy
  }
​
  return store._makeLocalGettersCache[namespace]
}
​
// 根据path返回对应的层级的store
function getNestedState (state, path) {
  return path.reduce((state, key) => state[key], state)
}

registerMutation、registerAction、registerGetter的实现

// 为什么格式为_mutations: {[type]: []}, 同个type类型可以有多个方法
function registerMutation (store, type, handler, local) {
  const entry = store._mutations[type] || (store._mutations[type] = [])
  entry.push(function wrappedMutationHandler (payload) {
    handler.call(store, local.state, payload)
  })
}
​
// _actions的格式同理
function registerAction (store, type, handler, local) {
  const entry = store._actions[type] || (store._actions[type] = [])
  entry.push(function wrappedActionHandler (payload) {
     // action中的参数有{dispatch、commit、getters、state、rootState、roomState}
    let res = handler.call(store, {
      dispatch: local.dispatch,
      commit: local.commit,
      getters: local.getters,
      state: local.state,
      rootGetters: store.getters,
      rootState: store.state
    }, payload)
    // action为异步方法, 
    if (!isPromise(res)) {
      res = Promise.resolve(res)
    }
  })
}
​
// getter一个类型只允许一个方法
function registerGetter (store, type, rawGetter, local) {
  store._wrappedGetters[type] = function wrappedGetter (store) {
    return rawGetter(
      local.state, // local state
      local.getters, // local getters
      store.state, // root state
      store.getters // root getters
    )
  }
}

resetStoreVM的实现

function resetStoreVM (store, state, hot) {
  store.getters = {}  
  store._makeLocalGettersCache = Object.create(null)
  const wrappedGetters = store._wrappedGetters
  const computed = {}
  forEachValue(wrappedGetters, (fn, key) => {
    //使用computed来利用它的延迟缓存机制  
    computed[key] = partial(fn, store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true 
    })
  })
  // 响应式的实现依赖于vue  
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })
}

四、vuex中的module

module模块

export default class Module {
  constructor (rawModule, runtime) {
    this.runtime = runtime
    this._children = Object.create(null)  // 储存模块下面的子模块
    this._rawModule = rawModule           // 储存原始对象
    const rawState = rawModule.state
​
    // Store the origin module's state
    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
  }
​
  // 判断是否设置namespaced  
  get namespaced () {
    return !!this._rawModule.namespaced
  }
​
  // 添加子模块  
  addChild (key, module) {
    this._children[key] = module
  }
​
   //删除子模块
  removeChild (key) {
    delete this._children[key]
  }
​
  // 获取所有子模块  
  getChild (key) {
    return this._children[key]
  }
​
    
 // 是否存在该子模块   
  hasChild (key) {
    return key in this._children
  }
​
  // 更新模块  
  update (rawModule) {
    this._rawModule.namespaced = rawModule.namespaced
    if (rawModule.actions) {
      this._rawModule.actions = rawModule.actions
    }
    if (rawModule.mutations) {
      this._rawModule.mutations = rawModule.mutations
    }
    if (rawModule.getters) {
      this._rawModule.getters = rawModule.getters
    }
  }
​
  // 遍历模块的子模块  
  forEachChild (fn) {
    forEachValue(this._children, fn)
  }
​
  // 遍历模块的getters方法  
  forEachGetter (fn) {
    if (this._rawModule.getters) {
      forEachValue(this._rawModule.getters, fn)
    }
  }
​
   // 遍历模块的action方法 
  forEachAction (fn) {
    if (this._rawModule.actions) {
      forEachValue(this._rawModule.actions, fn)
    }
  }
​
  // 遍历模块的mutation方法 
  forEachMutation (fn) {
    if (this._rawModule.mutations) {
      forEachValue(this._rawModule.mutations, fn)
    }
  }
}
​

module-collection, 在module的基本进行进一步的封装

export default class ModuleCollection {
  // store中_modules就是通过new ModuleCollection得来
  //  this._modules = new ModuleCollection(options) 
  constructor (rawRootModule) {
    // register root module (Vuex.Store options)
    this.register([], rawRootModule, false)
  }
​
  // 通过path获取对应的模块  
  get (path) {
    return path.reduce((module, key) => {
      return module.getChild(key)
    }, this.root)
  }
​
  // 获取namespace, 取决于模块是否设置namespaced为true, 如果为true, 则模块key/模块key, 为false, 则返回“”  
  getNamespace (path) {
    let module = this.root
    return path.reduce((namespace, key) => {
      module = module.getChild(key)
      return namespace + (module.namespaced ? key + '/' : '')
    }, '')
  }
​
  // 更新模块 
  update (rawRootModule) {
    update([], this.root, rawRootModule)
  }
​
 
  // 注册模块
  register (path, rawModule, runtime = true) {
    // 通过options封装module模块
    const newModule = new Module(rawModule, runtime)
    if (path.length === 0) {
      this.root = newModule // this.root为初始化的store
    } else {
      const parent = this.get(path.slice(0, -1))  // 获取当前节点父节点
      parent.addChild(path[path.length - 1], newModule) // 给父节点添加当前模块
    }
​
    // new moduleCollect(options)时候
    // 如果options中有modules, 那么遍历modules,收集下面子module为父module的children
    //  parent.addChild(path[path.length - 1], newModule)
    // 下面只需要调用module的forEachChild
    // 结构如下
    // - root(Module)
    // -- root._children[子module1(Module), 子module2(Module)]  
    if (rawModule.modules) {
      forEachValue(rawModule.modules, (rawChildModule, key) => {
        this.register(path.concat(key), rawChildModule, runtime)
      })
    }
  }
​
  
  // 移除module  
  unregister (path) {
    const parent = this.get(path.slice(0, -1))
    const key = path[path.length - 1]
    const child = parent.getChild(key)
    parent.removeChild(key)
  }
​
  // 判断是否存在该模块 
  isRegistered (path) {
    const parent = this.get(path.slice(0, -1))
    const key = path[path.length - 1]
​
    if (parent) {
      return parent.hasChild(key)
    }
​
    return false
  }
}
​
// 更新模块的实际实现
function update (path, targetModule, newModule) {  targetModule.update(newModule) //调用的是Module类的更新, 更新对应模块的actions、mutations、getters和namespaced
​
   // 更新模块的modules
  if (newModule.modules) {
    for (const key in newModule.modules) {
      if (!targetModule.getChild(key)) {
        return
      }
      update(
        path.concat(key),
        targetModule.getChild(key),
        newModule.modules[key]
      )
    }
  }
}

1、module构建了最基本的结构

  • _childrens
  • addChild
  • removeChild
  • getChild
  • hasChild
  • update(actions、mutations、getters、namespaced)

2、moduleCollect在module的基础添加了对store的处理, 主要通过path实现store的层级结构

  • root
  • get
  • getNamespace
  • update(actions、mutations、getters、namespaced、modules)
  • register (根节点注册为root, 非根节点则通过addChild注册到父节点上)
  • unregister
  • isRegistered

五、解答问题

1、vuex是如何注入的

  • vue.use(vuex)调用vuex的install方法
  • install执行了方法applyMixin
  • 在applyMixin中, 生命周期beforeCreate通过获取options.store,或者options.store, 或者option.parent.store插入

2、vuex是实现响应的

  • vuex中的data, getter都是存在代理的

  • 本质上通过new Vue实现的响应

    new Vue({
      data: {
        $$state: state
      },
      computed
    })
    
  • 后面访问state的时候

     get state () {
        return this._vm._data.$$state
      }
    
  • 访问getters的时候

     Object.defineProperty(store.getters, key, {
          get: () => store._vm[key],
          enumerable: true // for local getters
     })
    

3、vuex中state, getters,mutations, actions的注册和触发

如上文, 主要通过key构成path

参考源码

参考文章