摘要:繼續看后面的首先遍歷的,通過和首先獲取然后調用方法,我們來看看是不是感覺這個函數有些熟悉,表示當前實例,表示類型,表示執行的回調函數,表示本地化后的一個變量。必須是一個函數,如果返回的值發生了變化,那么就調用回調函數。
這幾天忙啊,有絕地求生要上分,英雄聯盟新賽季需要上分,就懶著什么也沒寫,很慚愧。這個vuex,vue-router,vue的源碼我半個月前就看的差不多了,但是懶,哈哈。
下面是vuex的源碼分析
在分析源碼的時候我們可以寫幾個例子來進行了解,一定不要閉門造車,多寫幾個例子,也就明白了
在vuex源碼中選擇了example/counter這個文件作為例子來進行理解
counter/store.js是vuex的核心文件,這個例子比較簡單,如果比較復雜我們可以采取分模塊來讓代碼結構更加清楚,如何分模塊請在vuex的官網中看如何使用。
我們來看看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 })
在代碼中我們實例化了Vuex.Store,每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含著你的應用中大部分的狀態 。在源碼中我們來看看Store是怎樣的一個構造函數(因為代碼太多,我把一些判斷進行省略,讓大家看的更清楚)
Stor構造函數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表示應用的插件,strict表示是否為嚴格模式。然后對store
的一系列屬性進行初始化,來看看這些屬性分別代表的是什么
_committing 表示一個提交狀態,在Store中能夠改變狀態只能是mutations,不能在外部隨意改變狀態
_actions用來收集所有action,在store的使用中經常會涉及到的action
_actionSubscribers用來存儲所有對 action 變化的訂閱者
_mutations用來收集所有action,在store的使用中經常會涉及到的mutation
_wappedGetters用來收集所有action,在store的使用中經常會涉及到的getters
_modules 用來收集module,當應用足夠大的情況,store就會變得特別臃腫,所以才有了module。每個Module都是多帶帶的store,都有getter、action、mutation
_subscribers 用來存儲所有對 mutation 變化的訂閱者
_watcherVM 一個Vue的實例,主要是為了使用Vue的watch方法,觀測數據的變化
后面獲取dispatch和commit然后將this.dipatch和commit進行綁定,我們來看看
commit和dispatchcommit (_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有三個參數,_type表示mutation的類型,_playload表示額外的參數,options表示一些配置。unifyObjectStyle()這個函數就不列出來了,它的作用就是判斷傳入的_type是不是對象,如果是對象進行響應簡單的調整。然后就是根據type來獲取相應的mutation,如果不存在就報錯,如果有就調用this._witchCommit。下面是_withCommit的具體實現
_withCommit (fn) { const committing = this._committing this._committing = true fn() this._committing = committing }
函數中多次提到了_committing,這個的作用我們在初始化Store的時候已經提到了更改狀態只能通過mutatiton進行更改,其他方式都不行,通過檢測committing的值我們就可以查看在更改狀態的時候是否發生錯誤
繼續來看commit函數,this._withCommitting對獲取到的mutation進行提交,然后遍歷this._subscribers,調用回調函數,并且將state傳入。總體來說commit函數的作用就是提交Mutation,遍歷_subscribers調用回調函數
下面是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函數類似,首先獲取type對象的actions,然后遍歷action訂閱者進行回調,對actions的長度進行判斷,當actions只有一個的時候進行將payload傳入,否則遍歷傳入參數,返回一個Promise.
commit和dispatch函數談完,我們回到store.js中的Store構造函數
this.strict表示是否開啟嚴格模式,嚴格模式下我們能夠觀測到state的變化情況,線上環境記得關閉嚴格模式。
this._modules.root.state就是獲取根模塊的狀態,然后調用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個參數store表示當前store實例,rootState表示根組件狀態,path表示組件路徑數組,module表示當前安裝的模塊,hot 當動態改變 modules 或者熱更新的時候為 true
首先進通過計算組件路徑長度判斷是不是根組件,然后獲取對應的命名空間,如果當前模塊存在namepaced那么就在store上添加模塊
如果不是根組件和hot為false的情況,那么先通過getNestedState找到rootState的父組件的state,因為path表示組件路徑數組,那么最后一個元素就是該組件的路徑,最后通過_withComment()函數提交Mutation,
Vue.set(parentState, moduleName, module.state)
這是Vue的部分,就是在parentState添加屬性moduleName,并且值為module.state
const local = module.context = makeLocalContext(store, namespace, path)
這段代碼里邊有一個函數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 }
傳入的三個參數store, namspace, path,store表示Vuex.Store的實例,namspace表示命名空間,path組件路徑。整體的意思就是本地化dispatch,commit,getter和state,意思就是新建一個對象上面有dispatch,commit,getter 和 state方法
那么installModule的那段代碼就是綁定方法在module.context和local上。繼續看后面的首先遍歷module的mutation,通過mutation 和 key 首先獲取namespacedType,然后調用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) }) }
是不是感覺這個函數有些熟悉,store表示當前Store實例,type表示類型,handler表示mutation執行的回調函數,local表示本地化后的一個變量。在最開始的時候我們就用到了這些東西,例如
const mutations = { increment (state) { state.count++ } ...
首先獲取type對應的mutation,然后向mutations數組push進去包裝后的hander函數。
module.forEachChild((child, key) => { installModule(store, rootState, path.concat(key), child, hot) })
遍歷module,對module的每個子模塊都調用installModule()方法
講完installModule()方法,我們繼續往下看resetStoreVM()函數
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這個方法主要是重置一個私有的 _vm對象,它是一個 Vue 的實例。傳入的有store表示當前Store實例,state表示狀態,hot就不用再說了。對store.getters進行初始化,獲取store._wrappedGetters并且用計算屬性的方法存儲了store.getters,所以store.getters是Vue的計算屬性。使用defineProperty方法給store.getters添加屬性,當我們使用store.getters[key]實際上獲取的是store._vm[key]。然后用一個Vue的實例data來存儲state 樹,這里使用slient=true,是為了取消提醒和警告,在這里將一下slient的作用,silent表示靜默模式(網上找的定義),就是如果開啟就沒有提醒和警告。后面的代碼表示如果oldVm存在并且hot為true時,通過_witchCommit修改$$state的值,并且摧毀oldVm對象
get state () { return this._vm._data.$$state }
因為我們將state信息掛載到_vm這個Vue實例對象上,所以在獲取state的時候,實際訪問的是this._vm._data.$$state。
watch (getter, cb, options) { return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options) }
在wacher函數中,我們先前提到的this._watcherVM就起到了作用,在這里我們利用了this._watcherVM中$watch方法進行了數據的檢測。watch 作用是響應式的監測一個 getter 方法的返回值,當值改變時調用回調。getter必須是一個函數,如果返回的值發生了變化,那么就調用cb回調函數。
我們繼續來將
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) }
沒什么難度就是,注冊一個動態模塊,首先判斷path,將其轉換為數組,register()函數的作用就是初始化一個模塊,安裝模塊,重置Store._vm。
因為文章寫太長了,估計沒人看,所以本文對代碼進行了簡化,有些地方沒寫出來,其他都是比較簡單的地方。有興趣的可以自己去看一下,最后補一張圖,結合起來看源碼更能事半功倍。這張圖要我自己通過源碼畫出來,再給我看幾天我也是畫不出來的,所以還需要努力啊
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/51774.html
摘要:繼續看后面的首先遍歷的,通過和首先獲取然后調用方法,我們來看看是不是感覺這個函數有些熟悉,表示當前實例,表示類型,表示執行的回調函數,表示本地化后的一個變量。必須是一個函數,如果返回的值發生了變化,那么就調用回調函數。 這幾天忙啊,有絕地求生要上分,英雄聯盟新賽季需要上分,就懶著什么也沒寫,很慚愧。這個vuex,vue-router,vue的源碼我半個月前就看的差不多了,但是懶,哈哈。...
摘要:源碼閱讀分析是專為開發的統一狀態管理工具。本文將會分析的整個實現思路,當是自己讀完源碼的一個總結。再次回到構造函數,接下來是各類插件的注冊插件注冊到這里的初始化工作已經完成。 Vuex源碼閱讀分析 Vuex是專為Vue開發的統一狀態管理工具。當我們的項目不是很復雜時,一些交互可以通過全局事件總線解決,但是這種觀察者模式有些弊端,開發時可能沒什么感覺,但是當項目變得復雜,維護時往往會摸不...
摘要:而鉆研最好的方式,就是閱讀的源代碼。整個的源代碼,核心內容包括兩部分。逃而動手腳的代碼,就存在于源代碼的中。整個源代碼讀下來一遍,雖然有些部分不太理解,但是對和一些代碼的使用的理解又加深了一步。 筆記中的Vue與Vuex版本為1.0.21和0.6.2,需要閱讀者有使用Vue,Vuex,ES6的經驗。 起因 俗話說得好,沒有無緣無故的愛,也沒有無緣無故的恨,更不會無緣無故的去閱讀別人的源...
摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運,我不曉得。我只曉得,不認命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...
摘要:為了防止某些文檔或腳本加載別的域下的未知內容,防止造成泄露隱私,破壞系統等行為發生。模式構建函數響應式前端架構過程中學到的經驗模式的不同之處在于,它主要專注于恰當地實現應用程序狀態突變。嚴重情況下,會造成惡意的流量劫持等問題。 今天是編輯周刊的日子。所以文章很多和周刊一樣。微信不能發鏈接,點了也木有用,所以請記得閱讀原文~ 發個動圖娛樂下: 使用 SVG 動畫制作游戲 使用 GASP ...
閱讀 810·2021-11-22 15:25
閱讀 1408·2021-09-08 09:45
閱讀 1685·2021-09-02 09:46
閱讀 1299·2019-08-30 15:56
閱讀 1528·2019-08-29 15:14
閱讀 1159·2019-08-29 13:06
閱讀 2010·2019-08-29 12:34
閱讀 1400·2019-08-26 12:14