vuex源码分析(二)

继上面讲完contructor函数,我们继续来讲后面的内容

get state () {
    return this._vm._data.$$state
}

set state (v) {
    if (process.env.NODE_ENV !== 'production') {
          assert(false, `Use store.replaceState() to explicit replace store state.`)
    }
}

get和set方法都有es6里边的语法,get语法是将对象属性绑定到查询该属性时将被调用的函数,而set语法是查询对象属性将被调用的函数。当state属性发生变化时,就会返回state的值,set用于检测在我们设置state的时候是否是生产环境,如果不是生产环境,就进行断点。

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

    const mutation = { type, payload }
    const entry = this._mutations[type]
    if (!entry) {
      if (process.env.NODE_ENV !== 'production') {
        console.error(`[vuex] unknown mutation type: ${type}`)
      }
      return
    }
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })
    this._subscribers.forEach(sub => sub(mutation, this.state))

    if (
      process.env.NODE_ENV !== 'production' &&
      options && options.silent
    ) {
      console.warn(
        `[vuex] mutation type: ${type}. Silent option has been removed. ` +
        'Use the filter functionality in the vue-devtools'
      )
    }
}

commit函数接受三个参数,_type表示mutaion的类型,_payload表示额外的参数,options表示一些配置。在函数中有函数叫unfyObjectStyle(),来看看他的代码

function unifyObjectStyle (type, payload, options) {
  if (isObject(type) && type.type) {
    options = payload
    payload = type
    type = type.type
  }

  if (process.env.NODE_ENV !== 'production') {
    assert(typeof type === 'string', `Expects string as the type, but found ${typeof type}.`)
  }

  return { type, payload, options }
}

这个函数很简单,就是在type为对象的情况下,对传进来的参数进行各自获取。继续看commit函数,根据unifyObjectStyle()处理后的type去找到mutations,withCommient的代码如下

_withCommit (fn) {
    const committing = this._committing
    this._committing = true
    fn()
    this._committing = committing
}

这个函数最重要的部分在于设置this._committing = true为什么这么做呢。是因为我们在观测state的状态的时候,我们可以通过看this._commiting的值来确定state修改是否错误,如果为false那么是不是状态修改就出现了问题呢。
那commit后面的部分就容易理解了,使用this._withCommit方法提交mutation。
this._subscribers是什么内容呢,这在我们上一篇提过是用来存储所有对 mutation 变化的订阅者。遍历this._subscribers进行回调。

options && options.silent

这儿我也不太懂黄轶老师说的是静默模式:静默模式就是用户不干扰的情况下,计算机自我处理的情况。
下面是是dispatch的代码

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

    const action = { type, payload }
    const entry = this._actions[type]
    if (!entry) {
      if (process.env.NODE_ENV !== 'production') {
        console.error(`[vuex] unknown action type: ${type}`)
      }
      return
    }

    this._actionSubscribers.forEach(sub => sub(action, this.state))

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

前面几行代码跟commit函数非常相似,取值,然后找到对应action。

this._actionSubscribers
跟前面commit方法不同的是最后用的是这段代码:

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

如果获取到的action的数量不止一个那么就进行Promise.all()遍历每个action进行回调函数回调,前几天看到一道面试题如果一个进程需要消耗8s,
另外一个进程需要消耗3秒,如何才能让两个进行的时间最短,解决方法就是Promise.all([a,b])注意里边的必须是一个数组,这样才能让两个进程同时进行。如果长度为1,则调用entry0

 subscribe (fn) {
    return genericSubscribe(fn, this._subscribers)
 }

 subscribeAction (fn) {
    return genericSubscribe(fn, this._actionSubscribers)
 }
 function genericSubscribe (fn, subs) {
  if (subs.indexOf(fn) < 0) {
    subs.push(fn)
  }
  return () => {
    const i = subs.indexOf(fn)
    if (i > -1) {
      subs.splice(i, 1)
    }
  }
}

来看看genericSubscribe都做了什么,接受两个参数,就是对subs进行调整,如果subs里边没有fn,那么向subs.push(fn)。返回的是删除fn的subs。那么上面subscribe和subscribeAction就是对传入的fn然后经过
genericSubscribe()函数进行处理
watch (getter, cb, options) {

if (process.env.NODE_ENV !== 'production') {
      assert(typeof getter === 'function', `store.watch only accepts a function.`)
}
    return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)

}
watch作用是响应式的检测一个getter方法的返回值,当值反生变化的时候,发生回调。
函数首先首先断言watch的getter必须是一个方法,接着利用了vue的实例对象的this._watcherVM的watch方法

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

用于修改state,调用 this._withCommit 方法修改 Store 的 rootState

registerModule

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

    if (process.env.NODE_ENV !== 'production') {
      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)
}

字面意思能看出动态注册Module的意思,动态的意思是什么呢。就是有些异步操作,我们需要使用这个函数就能够动态注册module了。
这儿有一个函数resetStoreVM,我们来看看他的实现方式

function resetStoreVM (store, state, hot) {
  const oldVm = store._vm

  // bind store public getters
  store.getters = {}
  const wrappedGetters = store._wrappedGetters
  const computed = {}
  forEachValue(wrappedGetters, (fn, key) => {
    // use computed to leverage its lazy-caching mechanism
    computed[key] = () => fn(store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
  })

  // use a Vue instance to store the state tree
  // suppress warnings just in case the user has added
  // some funky global mixins
  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)
  }

  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())
  }
}

这个函数的意思在于监听数据的变化,首先进行store.getters的初始化。forEachValue循环所有处理过的getters,并且新建computed对象进行存储,通过Object.defineProperty。然后为store.getter定义属性key,enumerable设置为true,表示可枚举。
前存储先前的Vue.config.slient,Vue.config.slient 设置为true是用于取消所有的警告和提醒。

store._vm = new Vue({
    data: {
        $$state: state
    },
    computed
})

这个就很简单了,创建一个Vue的实例,然后把state放到data里边去,computed里边的值用getters填充。

 if (store.strict) {
    enableStrictMode(store)
  }

如果是严格模式的话,则调用enableStrictMode()
下面是enableStrictMode的代码

 function enableStrictMode (store) {
  store._vm.$watch(function () { return this._data.$$state }, () => {
    if (process.env.NODE_ENV !== 'production') {
      assert(store._committing, `Do not mutate vuex store state outside mutation handlers.`)
    }
  }, { deep: true, sync: true })
}

这个函数做了什么了,看assert写的意识就是,如果设置了严格模式,就不允许用户在mutaion以外的方式进行修改state

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())
}

如果oldVm存在,并且hot为True的时候,将原有的vm中的state设置为空,所有原来的getters都会重新计算一遍,为什么会这样呢,这在前面我们创建一个vue的实例的时候,将getters作为了computed,我们知道在Vue中,值一旦发生变化,那么computed就会重新变化。

unregisterModule

unregisterModule (path) {
    if (typeof path === 'string') path = [path]

    if (process.env.NODE_ENV !== 'production') {
      assert(Array.isArray(path), `module path must be a string or an Array.`)
    }

    this._modules.unregister(path)
    this._withCommit(() => {
      const parentState = getNestedState(this.state, path.slice(0, -1))
      Vue.delete(parentState, path[path.length - 1])
    })
    resetStore(this)
}

跟函数registerModule相似,用于动态卸载module。讲讲这里的this._withCommit的作用,
把当前的模块的state对象从父state上删除,最后调用resetStore函数,下面是resetStore函数

function resetStore (store, hot) {
  store._actions = Object.create(null)
  store._mutations = Object.create(null)
  store._wrappedGetters = Object.create(null)
  store._modulesNamespaceMap = Object.create(null)
  const state = store.state
  // init all modules
  installModule(store, state, [], store._modules.root, true)
  // reset vm
  resetStoreVM(store, state, hot)
}

这个方法的作用就是重置store, installModule就是重新安装_aciton,_mutations,_wrappedGetters这些属性,resetStoreVM然后重置store的_vm对象。

hotUpdate (newOptions) {
    this._modules.update(newOptions)
    resetStore(this, true)
}

函数的作用就是热加载新的action和mutation。先更新modules,然后更新Store。
vuex里边最重要的class Store就讲完了。我们在讲讲其他重要的函数。

export function deepCopy (obj, cache = []) {
  // just return if obj is immutable value
  if (obj === null || typeof obj !== 'object') {
    return obj
  }

  // if obj is hit, it is in circular structure
  const hit = find(cache, c => c.original === obj)
  if (hit) {
    return hit.copy
  }

  const copy = Array.isArray(obj) ? [] : {}
  // put the copy into cache at first
  // because we want to refer it in recursive deepCopy
  cache.push({
    original: obj,
    copy
  })

  Object.keys(obj).forEach(key => {
    copy[key] = deepCopy(obj[key], cache)
  })

  return copy
}

这个函数的原理就是构造一个新的对象,遍历原对象或者数组,递归调用deepCopy。这儿加了一个cache,为什么呢。这是为了提高性能,比如说在Facinaciate中,我们就可以使用这种方法来提高性能,不用每计算一次,又重新计算已经计算过的东西
这次就讲到这里,下次再讲其他重要的函数

下次我决定不采用这种什么函数都讲清楚的方式了。这样工程量实在是太多了,下次的源码分析将会以我总结的方式来学习源码

    原文作者:云中歌
    原文地址: https://segmentfault.com/a/1190000011832973
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞