成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

vuex源碼閱讀分析

airborne007 / 3369人閱讀

摘要:繼續(xù)看后面的首先遍歷的,通過(guò)和首先獲取然后調(diào)用方法,我們來(lái)看看是不是感覺(jué)這個(gè)函數(shù)有些熟悉,表示當(dāng)前實(shí)例,表示類型,表示執(zhí)行的回調(diào)函數(shù),表示本地化后的一個(gè)變量。必須是一個(gè)函數(shù),如果返回的值發(fā)生了變化,那么就調(diào)用回調(diào)函數(shù)。

這幾天忙啊,有絕地求生要上分,英雄聯(lián)盟新賽季需要上分,就懶著什么也沒(méi)寫,很慚愧。這個(gè)vuex,vue-router,vue的源碼我半個(gè)月前就看的差不多了,但是懶,哈哈。
下面是vuex的源碼分析
在分析源碼的時(shí)候我們可以寫幾個(gè)例子來(lái)進(jìn)行了解,一定不要閉門造車,多寫幾個(gè)例子,也就明白了
在vuex源碼中選擇了example/counter這個(gè)文件作為例子來(lái)進(jìn)行理解
counter/store.js是vuex的核心文件,這個(gè)例子比較簡(jiǎn)單,如果比較復(fù)雜我們可以采取分模塊來(lái)讓代碼結(jié)構(gòu)更加清楚,如何分模塊請(qǐng)?jiān)趘uex的官網(wǎng)中看如何使用。
我們來(lái)看看store.js的代碼:

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

const state = {
  count: 0
}

const mutations = {
  increment (state) {
    state.count++
  },
  decrement (state) {
    state.count--
  }
}

const actions = {
  increment: ({ commit }) => commit("increment"),
  decrement: ({ commit }) => commit("decrement"),
  incrementIfOdd ({ commit, state }) {
    if ((state.count + 1) % 2 === 0) {
      commit("increment")
    }
  },
  incrementAsync ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit("increment")
        resolve()
      }, 1000)
    })
  }
}

const getters = {
  evenOrOdd: state => state.count % 2 === 0 ? "even" : "odd"
}

actions,

export default new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})

在代碼中我們實(shí)例化了Vuex.Store,每一個(gè) Vuex 應(yīng)用的核心就是 store(倉(cāng)庫(kù))。“store”基本上就是一個(gè)容器,它包含著你的應(yīng)用中大部分的狀態(tài) 。在源碼中我們來(lái)看看Store是怎樣的一個(gè)構(gòu)造函數(shù)(因?yàn)榇a太多,我把一些判斷進(jìn)行省略,讓大家看的更清楚)

Stor構(gòu)造函數(shù)

src/store.js

export class Store {
  constructor (options = {}) {
    if (!Vue && typeof window !== "undefined" && window.Vue) {
      install(window.Vue)
    }

    const {
      plugins = [],
      strict = false
    } = options

    // store internal state
    this._committing = false
    this._actions = Object.create(null)
    this._actionSubscribers = []
    this._mutations = Object.create(null)
    this._wrappedGetters = Object.create(null)
    this._modules = new ModuleCollection(options)
    this._modulesNamespaceMap = Object.create(null)
    this._subscribers = []
    this._watcherVM = new Vue()

    // bind commit and dispatch to self
    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)
    }

    // strict mode
    this.strict = strict

    const state = this._modules.root.state

    installModule(this, state, [], this._modules.root)

    resetStoreVM(this, state)

    plugins.forEach(plugin => plugin(this))

    if (Vue.config.devtools) {
      devtoolPlugin(this)
    }
  }

  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.`)
    }
  }

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

  dispatch (_type, _payload) {
    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)
  }

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

  subscribeAction (fn) {
    return genericSubscribe(fn, this._actionSubscribers)
  }

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

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

  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)
    resetStoreVM(this, this.state)
  }

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

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

首先判斷window.Vue是否存在,如果不存在就安裝Vue, 然后初始化定義,plugins表示應(yīng)用的插件,strict表示是否為嚴(yán)格模式。然后對(duì)store
的一系列屬性進(jìn)行初始化,來(lái)看看這些屬性分別代表的是什么

_committing 表示一個(gè)提交狀態(tài),在Store中能夠改變狀態(tài)只能是mutations,不能在外部隨意改變狀態(tài)

_actions用來(lái)收集所有action,在store的使用中經(jīng)常會(huì)涉及到的action

_actionSubscribers用來(lái)存儲(chǔ)所有對(duì) action 變化的訂閱者

_mutations用來(lái)收集所有action,在store的使用中經(jīng)常會(huì)涉及到的mutation

_wappedGetters用來(lái)收集所有action,在store的使用中經(jīng)常會(huì)涉及到的getters

_modules 用來(lái)收集module,當(dāng)應(yīng)用足夠大的情況,store就會(huì)變得特別臃腫,所以才有了module。每個(gè)Module都是多帶帶的store,都有g(shù)etter、action、mutation

_subscribers 用來(lái)存儲(chǔ)所有對(duì) mutation 變化的訂閱者

_watcherVM 一個(gè)Vue的實(shí)例,主要是為了使用Vue的watch方法,觀測(cè)數(shù)據(jù)的變化

后面獲取dispatch和commit然后將this.dipatch和commit進(jìn)行綁定,我們來(lái)看看

commit和dispatch
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))
  }

commit有三個(gè)參數(shù),_type表示mutation的類型,_playload表示額外的參數(shù),options表示一些配置。unifyObjectStyle()這個(gè)函數(shù)就不列出來(lái)了,它的作用就是判斷傳入的_type是不是對(duì)象,如果是對(duì)象進(jìn)行響應(yīng)簡(jiǎn)單的調(diào)整。然后就是根據(jù)type來(lái)獲取相應(yīng)的mutation,如果不存在就報(bào)錯(cuò),如果有就調(diào)用this._witchCommit。下面是_withCommit的具體實(shí)現(xiàn)

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

函數(shù)中多次提到了_committing,這個(gè)的作用我們?cè)诔跏蓟疭tore的時(shí)候已經(jīng)提到了更改狀態(tài)只能通過(guò)mutatiton進(jìn)行更改,其他方式都不行,通過(guò)檢測(cè)committing的值我們就可以查看在更改狀態(tài)的時(shí)候是否發(fā)生錯(cuò)誤
繼續(xù)來(lái)看commit函數(shù),this._withCommitting對(duì)獲取到的mutation進(jìn)行提交,然后遍歷this._subscribers,調(diào)用回調(diào)函數(shù),并且將state傳入??傮w來(lái)說(shuō)commit函數(shù)的作用就是提交Mutation,遍歷_subscribers調(diào)用回調(diào)函數(shù)
下面是dispatch的代碼

dispatch (_type, _payload) {

    const action = { type, payload }
    const entry = this._actions[type]

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

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

和commit函數(shù)類似,首先獲取type對(duì)象的actions,然后遍歷action訂閱者進(jìn)行回調(diào),對(duì)actions的長(zhǎng)度進(jìn)行判斷,當(dāng)actions只有一個(gè)的時(shí)候進(jìn)行將payload傳入,否則遍歷傳入?yún)?shù),返回一個(gè)Promise.
commit和dispatch函數(shù)談完,我們回到store.js中的Store構(gòu)造函數(shù)
this.strict表示是否開啟嚴(yán)格模式,嚴(yán)格模式下我們能夠觀測(cè)到state的變化情況,線上環(huán)境記得關(guān)閉嚴(yán)格模式。
this._modules.root.state就是獲取根模塊的狀態(tài),然后調(diào)用installModule(),下面是

installModule()
function installModule (store, rootState, path, module, hot) {
  const isRoot = !path.length
  const namespace = store._modules.getNamespace(path)

  // register in namespace map
  if (module.namespaced) {
    store._modulesNamespaceMap[namespace] = module
  }

  // set state
  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)
    })
  }

  const local = module.context = makeLocalContext(store, namespace, path)

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

  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
}

installModule()包括5個(gè)參數(shù)store表示當(dāng)前store實(shí)例,rootState表示根組件狀態(tài),path表示組件路徑數(shù)組,module表示當(dāng)前安裝的模塊,hot 當(dāng)動(dòng)態(tài)改變 modules 或者熱更新的時(shí)候?yàn)?true
首先進(jìn)通過(guò)計(jì)算組件路徑長(zhǎng)度判斷是不是根組件,然后獲取對(duì)應(yīng)的命名空間,如果當(dāng)前模塊存在namepaced那么就在store上添加模塊
如果不是根組件和hot為false的情況,那么先通過(guò)getNestedState找到rootState的父組件的state,因?yàn)閜ath表示組件路徑數(shù)組,那么最后一個(gè)元素就是該組件的路徑,最后通過(guò)_withComment()函數(shù)提交Mutation,

Vue.set(parentState, moduleName, module.state)

這是Vue的部分,就是在parentState添加屬性moduleName,并且值為module.state

const local = module.context = makeLocalContext(store, namespace, path)

這段代碼里邊有一個(gè)函數(shù)makeLocalContext

makeLocalContext()
function makeLocalContext (store, namespace, path) {
  const noNamespace = namespace === ""

  const local = {
    dispatch: noNamespace ? store.dispatch : ...
    commit: noNamespace ? store.commit : ...
  }

  Object.defineProperties(local, {
    getters: {
      get: noNamespace
        ? () => store.getters
        : () => makeLocalGetters(store, namespace)
    },
    state: {
      get: () => getNestedState(store.state, path)
    }
  })

  return local
}

傳入的三個(gè)參數(shù)store, namspace, path,store表示Vuex.Store的實(shí)例,namspace表示命名空間,path組件路徑。整體的意思就是本地化dispatch,commit,getter和state,意思就是新建一個(gè)對(duì)象上面有dispatch,commit,getter 和 state方法
那么installModule的那段代碼就是綁定方法在module.context和local上。繼續(xù)看后面的首先遍歷module的mutation,通過(guò)mutation 和 key 首先獲取namespacedType,然后調(diào)用registerMutation()方法,我們來(lái)看看

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

是不是感覺(jué)這個(gè)函數(shù)有些熟悉,store表示當(dāng)前Store實(shí)例,type表示類型,handler表示mutation執(zhí)行的回調(diào)函數(shù),local表示本地化后的一個(gè)變量。在最開始的時(shí)候我們就用到了這些東西,例如

const mutations = {
  increment (state) {
    state.count++
  }
  ...

首先獲取type對(duì)應(yīng)的mutation,然后向mutations數(shù)組push進(jìn)去包裝后的hander函數(shù)。

module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })

遍歷module,對(duì)module的每個(gè)子模塊都調(diào)用installModule()方法

講完installModule()方法,我們繼續(xù)往下看resetStoreVM()函數(shù)

resetStoreVM()
function resetStoreVM (store, state, hot) {
  const oldVm = store._vm
  store.getters = {}
  const wrappedGetters = store._wrappedGetters
  const computed = {}
  forEachValue(wrappedGetters, (fn, key) => {
    computed[key] = () => fn(store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
  })

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

  if (oldVm) {
    if (hot) {
     store._withCommit(() => {
        oldVm._data.$$state = null
      })
    }
    Vue.nextTick(() => oldVm.$destroy())
  }
}

resetStoreVM這個(gè)方法主要是重置一個(gè)私有的 _vm對(duì)象,它是一個(gè) Vue 的實(shí)例。傳入的有store表示當(dāng)前Store實(shí)例,state表示狀態(tài),hot就不用再說(shuō)了。對(duì)store.getters進(jìn)行初始化,獲取store._wrappedGetters并且用計(jì)算屬性的方法存儲(chǔ)了store.getters,所以store.getters是Vue的計(jì)算屬性。使用defineProperty方法給store.getters添加屬性,當(dāng)我們使用store.getters[key]實(shí)際上獲取的是store._vm[key]。然后用一個(gè)Vue的實(shí)例data來(lái)存儲(chǔ)state 樹,這里使用slient=true,是為了取消提醒和警告,在這里將一下slient的作用,silent表示靜默模式(網(wǎng)上找的定義),就是如果開啟就沒(méi)有提醒和警告。后面的代碼表示如果oldVm存在并且hot為true時(shí),通過(guò)_witchCommit修改$$state的值,并且摧毀oldVm對(duì)象

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

因?yàn)槲覀儗tate信息掛載到_vm這個(gè)Vue實(shí)例對(duì)象上,所以在獲取state的時(shí)候,實(shí)際訪問(wèn)的是this._vm._data.$$state。

 watch (getter, cb, options) {
    return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)
  }

在wacher函數(shù)中,我們先前提到的this._watcherVM就起到了作用,在這里我們利用了this._watcherVM中$watch方法進(jìn)行了數(shù)據(jù)的檢測(cè)。watch 作用是響應(yīng)式的監(jiān)測(cè)一個(gè) getter 方法的返回值,當(dāng)值改變時(shí)調(diào)用回調(diào)。getter必須是一個(gè)函數(shù),如果返回的值發(fā)生了變化,那么就調(diào)用cb回調(diào)函數(shù)。
我們繼續(xù)來(lái)將

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

    this._modules.register(path, rawModule)
    installModule(this, this.state, path, this._modules.get(path), options.preserveState)
    resetStoreVM(this, this.state)
  }

沒(méi)什么難度就是,注冊(cè)一個(gè)動(dòng)態(tài)模塊,首先判斷path,將其轉(zhuǎn)換為數(shù)組,register()函數(shù)的作用就是初始化一個(gè)模塊,安裝模塊,重置Store._vm。

因?yàn)槲恼聦懱L(zhǎng)了,估計(jì)沒(méi)人看,所以本文對(duì)代碼進(jìn)行了簡(jiǎn)化,有些地方?jīng)]寫出來(lái),其他都是比較簡(jiǎn)單的地方。有興趣的可以自己去看一下,最后補(bǔ)一張圖,結(jié)合起來(lái)看源碼更能事半功倍。這張圖要我自己通過(guò)源碼畫出來(lái),再給我看幾天我也是畫不出來(lái)的,所以還需要努力啊

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107088.html

相關(guān)文章

  • vuex源碼閱讀分析

    摘要:繼續(xù)看后面的首先遍歷的,通過(guò)和首先獲取然后調(diào)用方法,我們來(lái)看看是不是感覺(jué)這個(gè)函數(shù)有些熟悉,表示當(dāng)前實(shí)例,表示類型,表示執(zhí)行的回調(diào)函數(shù),表示本地化后的一個(gè)變量。必須是一個(gè)函數(shù),如果返回的值發(fā)生了變化,那么就調(diào)用回調(diào)函數(shù)。 這幾天忙啊,有絕地求生要上分,英雄聯(lián)盟新賽季需要上分,就懶著什么也沒(méi)寫,很慚愧。這個(gè)vuex,vue-router,vue的源碼我半個(gè)月前就看的差不多了,但是懶,哈哈。...

    gplane 評(píng)論0 收藏0
  • Vuex源碼閱讀分析

    摘要:源碼閱讀分析是專為開發(fā)的統(tǒng)一狀態(tài)管理工具。本文將會(huì)分析的整個(gè)實(shí)現(xiàn)思路,當(dāng)是自己讀完源碼的一個(gè)總結(jié)。再次回到構(gòu)造函數(shù),接下來(lái)是各類插件的注冊(cè)插件注冊(cè)到這里的初始化工作已經(jīng)完成。 Vuex源碼閱讀分析 Vuex是專為Vue開發(fā)的統(tǒng)一狀態(tài)管理工具。當(dāng)我們的項(xiàng)目不是很復(fù)雜時(shí),一些交互可以通過(guò)全局事件總線解決,但是這種觀察者模式有些弊端,開發(fā)時(shí)可能沒(méi)什么感覺(jué),但是當(dāng)項(xiàng)目變得復(fù)雜,維護(hù)時(shí)往往會(huì)摸不...

    Eastboat 評(píng)論0 收藏0
  • Vuex源碼閱讀筆記

    摘要:而鉆研最好的方式,就是閱讀的源代碼。整個(gè)的源代碼,核心內(nèi)容包括兩部分。逃而動(dòng)手腳的代碼,就存在于源代碼的中。整個(gè)源代碼讀下來(lái)一遍,雖然有些部分不太理解,但是對(duì)和一些代碼的使用的理解又加深了一步。 筆記中的Vue與Vuex版本為1.0.21和0.6.2,需要閱讀者有使用Vue,Vuex,ES6的經(jīng)驗(yàn)。 起因 俗話說(shuō)得好,沒(méi)有無(wú)緣無(wú)故的愛(ài),也沒(méi)有無(wú)緣無(wú)故的恨,更不會(huì)無(wú)緣無(wú)故的去閱讀別人的源...

    hosition 評(píng)論0 收藏0
  • 前方來(lái)報(bào),八月最新資訊--關(guān)于vue2&3的最佳文章推薦

    摘要:哪吒別人的看法都是狗屁,你是誰(shuí)只有你自己說(shuō)了才算,這是爹教我的道理。哪吒去他個(gè)鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰(shuí)和你做朋友太乙真人人是否能夠改變命運(yùn),我不曉得。我只曉得,不認(rèn)命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...

    izhuhaodev 評(píng)論0 收藏0
  • 前端閱讀筆記 2016-11-25

    摘要:為了防止某些文檔或腳本加載別的域下的未知內(nèi)容,防止造成泄露隱私,破壞系統(tǒng)等行為發(fā)生。模式構(gòu)建函數(shù)響應(yīng)式前端架構(gòu)過(guò)程中學(xué)到的經(jīng)驗(yàn)?zāi)J降牟煌幵谟冢饕獙W⒂谇‘?dāng)?shù)貙?shí)現(xiàn)應(yīng)用程序狀態(tài)突變。嚴(yán)重情況下,會(huì)造成惡意的流量劫持等問(wèn)題。 今天是編輯周刊的日子。所以文章很多和周刊一樣。微信不能發(fā)鏈接,點(diǎn)了也木有用,所以請(qǐng)記得閱讀原文~ 發(fā)個(gè)動(dòng)圖娛樂(lè)下: 使用 SVG 動(dòng)畫制作游戲 使用 GASP ...

    KoreyLee 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<