Vuex源码解析

Vuex源码解析

Vuex的注册

在文件中引入Vue,使用vue中的use方法进行注册

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

src/store.js

let Vue; // bind on install

export 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 // 通过Vue.use(Vuex)将Vue传进来,不需要额外引入vue
  applyMixin(Vue)
}

首先声明了Vue,在install方法中会被赋值,这样写可以不用引入import Vue from 'vue',避免在打包的时候一同将vue进行打包。

applyMixin()

export default function (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 // 获取组件上的所有options
    // store injection
    if (options.store) { // 判断组件是否存在store
      this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) { // 从父组件中获取store
      this.$store = options.parent.$store
    }
  }
}

在applyMixin方法中会区分vue的版本,1.x跟2.x的钩子函数不一样。在vue实例中加上store后,根组件就可以取得store,其他子组件通过其父组件获取store,逐层传递,最后所有的组件都存在$store属性了。

Store构造函数

定义了state、mutations、actions等,
先来看看Store的contructor中的属性

export class Store {
  constructor (options = {}) {
    // 如果是通过script标签直接引入vuex,则不需要调用Vue.use(Vuex),自动安装
    if (!Vue && typeof window !== 'undefined' && window.Vue) {
      install(window.Vue)
    }
	// 定义plugins和strict属性
    const {
      plugins = [],
      strict = false
    } = options
	
    // 定义内部属性
    // 提交状态的标志,在_withCommit中,当使用mutation是,会赋值为true,再执行mutation,修改state
    // 后再赋值为false,在这个过程中,会用watch监听state的变化是否_committing为true,从而保证了state
    // 只能通过mutation来改变
    this._committing = false
    
    // 用于保存所有的action
    this._actions = Object.create(null)

    // 用于保存订阅action的回调
    this._actionSubscribers = []

    // 保存所有的mutations
    this._mutations = Object.create(null)
    
    // 保存包装后的getters
    this._wrappedGetters = Object.create(null)
    
    // 收集module
    this._modules = new ModuleCollection(options)
    
    // 保存namespace模块
    this._modulesNamespaceMap = Object.create(null)
    
    // 订阅,用于监听mutation
    this._subscribers = []
    
    // vue实例,用于watch监听变化
    this._watcherVM = new Vue()

    this._makeLocalGettersCache = Object.create(null)

    // 绑定commit和dispatch到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)
    }

    // 在严格模式下,state只能通过mutations进行修改
    this.strict = strict

    const state = this._modules.root.state

    // 安装模块,用于处理module、action、mutation、getters等
    installModule(this, state, [], this._modules.root)
    
    // 重置state、getter等(采用vue实例)
    resetStoreVM(this, state)

    // 插件的注册
    plugins.forEach(plugin => plugin(this))
    const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
    if (useDevtools) {
      devtoolPlugin(this)
    }
  }
}
moduleCollection()

src/module/module-collection.js

export default class ModuleCollection {
  constructor (rawRootModule) {
    // register root module (Vuex.Store options)
    // 注册根模块(根模块的path是 [] )
    this.register([], rawRootModule, false)
  }

  get (path) {
    // 根据path获取module
    return path.reduce((module, key) => {
      return module.getChild(key)
    }, this.root)
  }
  register (path, rawModule, runtime = true) {
    if (__DEV__) {
      assertRawModule(path, rawModule)
    }
    // 创建一个module实例
    const newModule = new Module(rawModule, runtime)
    if (path.length === 0) {
      // 将根module实例绑定在root属性上
      this.root = newModule
    } else {
      // 把子module添加到父module的_children属性上
      const parent = this.get(path.slice(0, -1))
      parent.addChild(path[path.length - 1], newModule)
    }

    // register nested modules
    // 注册成一个树
    if (rawModule.modules) {
      forEachValue(rawModule.modules, (rawChildModule, key) => {
        this.register(path.concat(key), rawChildModule, runtime)
      })
    }
  }

  unregister (path) {
    const parent = this.get(path.slice(0, -1))
    const key = path[path.length - 1]
    const child = parent.getChild(key)
    if (!child) {
      if (__DEV__) {
        console.warn(
          `[vuex] trying to unregister module '${key}', which is ` +
          `not registered`
        )
      }
      return
    }
    if (!child.runtime) {
      return
    }
    parent.removeChild(key)
  }
}

模块例子

{
  modules:{
    m1:{
      state:{}
	},
	m2:{
      state:{}
	},
  }
}

子模块的path就是key,m1的path就是[‘m1’],m2的path就是[‘m2’]

module()
export default class Module {
  constructor (rawModule, runtime) {
    this.runtime = runtime
    // 存储子模块
    this._children = Object.create(null)
    // Store the origin module object which passed by programmer
    // 保存原module,在store的installModule中处理actions、mutations
    this._rawModule = rawModule
    const rawState = rawModule.state

    // Store the origin module's state
    // 保存state
    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
  }
  addChild (key, module) {
    this._children[key] = module
  }
}
installModule()

用于处理根module、命名空间、action、mutation、getters和递归注册子module。
将module的namespace保存到store._modulesNamespaceMap中,判断是否为根module以及hot,获取对应的state(不是根module就获取父module的state)然后调用Vue.set方法将当前的state挂载到父state上,最后去设置module的上下文注册mutations等

function installModule (store, rootState, path, module, hot) {
  const isRoot = !path.length
  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('/')}`)
    }
    // 保存namespaced模块
    store._modulesNamespaceMap[namespace] = module
  }

  // 响应化
  if (!isRoot && !hot) {
    // 如果不是根module,就获取父state
    const parentState = getNestedState(rootState, path.slice(0, -1))
    // 获取当前module
    const moduleName = path[path.length - 1]
    
    store._withCommit(() => {
      if (__DEV__) {
        if (moduleName in parentState) {
          console.warn(
            `[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"`
          )
        }
      }
      // 将当前module的state设置成响应式
      Vue.set(parentState, moduleName, module.state)
    })
  }
  // 设置module上下文,(context)
  const local = module.context = makeLocalContext(store, namespace, path)

  // 注册mutation
  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })

  // 注册action
  module.forEachAction((action, key) => {
    const type = action.root ? key : namespace + key
    const handler = action.handler || action
    registerAction(store, type, handler, local)
  })
  // 注册getter
  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
  })
  // 注册子module
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
}
makeLocalContext()

通过判断namespace是否存在,不存在就使用原来store中dispatch跟commit,否则就使用module上的store。存在namespace,就会在type上加上namespace。如果声明了{root:true}也使用原来的type。同时采用了Object.defineProperties()对getters和state做了延迟处理.

// 设置module上下文,绑定dispatch、commit、getters、state
function makeLocalContext (store, namespace, path) {
  // 判断是否存在命名空间
  const noNamespace = namespace === ''
  const local = {
    // 不存在命名空间就使用原来的
    dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
      // 统一对象格式
      const args = unifyObjectStyle(_type, _payload, _options)
      const { payload, options } = args
      let { type } = args

      if (!options || !options.root) {
        // 类型上加上命名空间
        type = namespace + type
        if (__DEV__ && !store._actions[type]) {
          console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`)
          return
        }
      }
      // 触发action
      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
        if (__DEV__ && !store._mutations[type]) {
          console.error(`[vuex] unknown local mutation type: ${args.type}, global type: ${type}`)
          return
        }
      }
      store.commit(type, payload, options)
    }
  }

  // getters and state object must be gotten lazily
  // because they will be changed by vm update
  Object.defineProperties(local, {
    getters: {
      get: noNamespace
        ? () => store.getters
        : () => makeLocalGetters(store, namespace)
    },
    state: {
      get: () => getNestedState(store.state, path)
    }
  })
  return local
}
registerMutation()
function registerMutation (store, type, handler, local) {
  // 判断是否存在该mutation类型,不存在就提供一个空数组
  const entry = store._mutations[type] || (store._mutations[type] = [])
  // 可重复注册mutation
  entry.push(function wrappedMutationHandler (payload) {
    handler.call(store, local.state, payload)
  })
}

通过commit触发mutation

commit()

this.$store.commit(mutationType, payload)

  commit (_type, _payload, _options) {
    // check object-style commit
    const {
      type,
      payload,
      options
    } = unifyObjectStyle(_type, _payload, _options)

    const mutation = { type, payload }
    // 获取type对应mutations
    const entry = this._mutations[type]
    if (!entry) {
      if (__DEV__) {
        console.error(`[vuex] unknown mutation type: ${type}`)
      }
      return
    }
    // 在_withCommit中执行mutation,mutation是修改state的唯一方法
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })
    // 通知更新
    this._subscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .forEach(sub => sub(mutation, this.state))

    if (
      __DEV__ &&
      options && options.silent
    ) {
      console.warn(
        `[vuex] mutation type: ${type}. Silent option has been removed. ` +
        'Use the filter functionality in the vue-devtools'
      )
    }
  }
registerActions()
function registerAction (store, type, handler, local) {
  // 根据type获取actions
  const entry = store._actions[type] || (store._actions[type] = [])
  entry.push(function wrappedActionHandler (payload) {
    // 参数较多,用于解构获取commit,state等
    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的执行结果不是promise类型,将其包裹其promise,用于promise的链式调用
    if (!isPromise(res)) {
      res = Promise.resolve(res)
    }
    if (store._devtoolHook) {
      // 使用devtool处理一次error 
      return res.catch(err => {
        store._devtoolHook.emit('vuex:error', err)
        throw err
      })
    } else {
      return res
    }
  })
}
dispatch()

对参数统一处理后获取到action数组,数组长度大于一,用promise.all进行处理。

dispatch (_type, _payload) {
    // 统一格式
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload)
    // 组成action
    const action = { type, payload } 
    // 获取actions数组
    const entry = this._actions[type]
    // 不存在该action
    if (!entry) {
      if (__DEV__) {
        console.error(`[vuex] unknown action type: ${type}`)
      }
      return
    }
    // 在执行action前
    try {
      this._actionSubscribers
        .slice() 
        .filter(sub => sub.before)
        .forEach(sub => sub.before(action, this.state))
    } catch (e) {
      if (__DEV__) {
        console.warn(`[vuex] error in before action subscribers: `)
        console.error(e)
      }
    }

    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)

    return new Promise((resolve, reject) => {
      result.then(res => {
        // 在执行action后
        try {
          this._actionSubscribers
            .filter(sub => sub.after)
            .forEach(sub => sub.after(action, this.state))
        } catch (e) {
          if (__DEV__) {
            console.warn(`[vuex] error in after action subscribers: `)
            console.error(e)
          }
        }
        resolve(res)
      }, error => {
        try {
          this._actionSubscribers
            .filter(sub => sub.error)
            .forEach(sub => sub.error(action, this.state, error))
        } catch (e) {
          if (__DEV__) {
            console.warn(`[vuex] error in error action subscribers: `)
            console.error(e)
          }
        }
        reject(error)
      })
    })
  }
registerGetters()
function registerGetter (store, type, rawGetter, local) {
  // 不能重复定义getters
  if (store._wrappedGetters[type]) {
    if (__DEV__) {
      console.error(`[vuex] duplicate getter key: ${type}`)
    }
    return
  }
  // 包裹一层_wrappedGetters
  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()

重新设置一个新的vue实例,用于保存state和getter,使用computed来保存getters,同时给getters加上了一层代理,使得可以通过this.$store.getters.xx进行访问。通过判断是否存在oldVm,解除state引用,通过销毁旧的vue实例。

function resetStoreVM (store, state, hot) {
  // 保存旧vm
  const oldVm = store._vm
  store.getters = {}
  // reset local getters cache
  store._makeLocalGettersCache = Object.create(null)
  // 获取store中外层的getters
  const wrappedGetters = store._wrappedGetters
  const computed = {}
  // 遍历getters,通过Object.defineProperty()给getters对象建立属性,就可以通过this.$store.getters.xx访问
  forEachValue(wrappedGetters, (fn, key) => {
    computed[key] = partial(fn, store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
  })
  // silent为true,取消所有日志警告
  const silent = Vue.config.silent
  Vue.config.silent = true
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })
  Vue.config.silent = silent

  // enable strict mode for new vm
  if (store.strict) {
    enableStrictMode(store)
  }
  // 如果存在oldVm,解除对state的引用,等dom更新后把旧的vue实例销毁
  if (oldVm) {
    if (hot) {
      // dispatch changes in all subscribed watchers
      // to force getter re-evaluation for hot reloading.
      store._withCommit(() => {
        oldVm._data.$$state = null
      })
    }
    Vue.nextTick(() => oldVm.$destroy())
  }
}
enableStrictMode()

使用watch来观察state的变化,在mutation之后都会改变_committing,如果_committing没有改变说明没有通过mutation改变状态。

function enableStrictMode (store) {
  store._vm.$watch(function () { return this._data.$$state }, () => {
    if (__DEV__) {
      assert(store._committing, `do not mutate vuex store state outside mutation handlers.`)
    }
  }, { deep: true, sync: true })
}
watch()

用于监听getter的变化

watch (getter, cb, options) {
    if (__DEV__) {
      // 判断getter类型,必须为function类型
      assert(typeof getter === 'function', `store.watch only accepts a function.`)
    }
    // 调用vue中的watch方法
    return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)
  }
replaceState()

修改state

  replaceState (state) {
    this._withCommit(() => {
      this._vm._data.$$state = state
    })
  }
registerModule()

动态注册module

  registerModule (path, rawModule, options = {}) {
    if (typeof path === 'string') path = [path]

    if (__DEV__) {
      assert(Array.isArray(path), `module path must be a string or an Array.`)
      assert(path.length > 0, 'cannot register the root module by using registerModule.')
    }

    this._modules.register(path, rawModule)
    // 安装模块
    installModule(this, this.state, path, this._modules.get(path), options.preserveState)
    // reset store to update getters...
    // 更新
    resetStoreVM(this, this.state)
  }
_withCommit()

当改变mutations时会将_committing设置成true,执行完后重置。

  _withCommit (fn) {
    const committing = this._committing
    this._committing = true
    fn()
    this._committing = committing
  }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值