资讯专栏INFORMATION COLUMN

vuex源码分析(二)

fizz / 1229人阅读

摘要:那后面的部分就容易理解了,使用方法提交。返回的是删除的。然后为定义属性设置为表示可枚举。前存储先前的设置为是用于取消所有的警告和提醒。这样工程量实在是太多了,下次的源码分析将会以我总结的方式来学习源码

继上面讲完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://www.ucloud.cn/yun/51439.html

相关文章

  • vuex源码分析

    摘要:那后面的部分就容易理解了,使用方法提交。返回的是删除的。然后为定义属性设置为表示可枚举。前存储先前的设置为是用于取消所有的警告和提醒。这样工程量实在是太多了,下次的源码分析将会以我总结的方式来学习源码 继上面讲完contructor函数,我们继续来讲后面的内容 get state () { return this._vm._data.$$state } set state (...

    jeffrey_up 评论0 收藏0
  • vuex源码分析

    摘要:那后面的部分就容易理解了,使用方法提交。返回的是删除的。然后为定义属性设置为表示可枚举。前存储先前的设置为是用于取消所有的警告和提醒。这样工程量实在是太多了,下次的源码分析将会以我总结的方式来学习源码 继上面讲完contructor函数,我们继续来讲后面的内容 get state () { return this._vm._data.$$state } set state (...

    hidogs 评论0 收藏0
  • Vue源码解析(五)-vuex

    摘要:提供了函数,它把直接映射到我们的组件中,先给出的使用值为值为让我们看看的源码实现规范当前的命名空间。在中,都是同步事务。同步的意义在于这样每一个执行完成后都可以对应到一个新的状态和一样,这样就可以打个存下来,然后就可以随便了。 Vue 组件中获得 Vuex 状态 按官网说法:由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态,本...

    calx 评论0 收藏0
  • 关于Vue2一些值得推荐的文章 -- 五、六月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    Terry_Tai 评论0 收藏0
  • 关于Vue2一些值得推荐的文章 -- 五、六月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    sutaking 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<