摘要:的源碼分析系列大概會分為三篇博客來講,為什么分三篇呢,因為寫一篇太多了。這段代碼檢測是否存在,如果不存在那么就調用方法這行代碼用于確保在我們實例化之前,已經存在。每一個集合也是的實例。
vuex的源碼分析系列大概會分為三篇博客來講,為什么分三篇呢,因為寫一篇太多了。您看著費勁,我寫著也累
這是vuex的目錄圖
分析vuex的源碼,我們先從index入口文件進行分析,入口文件在src/store.js文件中
export default { // 主要代碼,狀態存儲類 Store, // 插件安裝 install, // 版本 version: "__VERSION__", mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers }
一個一個來看Store是vuex提供的狀態存儲類,通常我們使用Vuex就是通過創建Store的實例,
install方法是配合Vue.use方法進行使用,install方法是用來編寫Vue插件的通用公式,先來看一下代碼
export function install (_Vue) { if (Vue && _Vue === Vue) { if (process.env.NODE_ENV !== "production") { console.error( "[vuex] already installed. Vue.use(Vuex) should be called only once." ) } return } Vue = _Vue applyMixin(Vue) }
這個方法的作用是什么呢:當window上有Vue對象的時候,就會手動編寫install方法,并且傳入Vue的使用。
在install中法中有applyMixin這個函數,這個函數來自云src/mixin.js,下面是mixin.js的代碼
export default function (Vue) { // 判斷VUe的版本 const version = Number(Vue.version.split(".")[0]) // 如果vue的版本大于2,那么beforeCreate之前vuex進行初始化 if (version >= 2) { Vue.mixin({ beforeCreate: vuexInit }) } else { // override init and inject vuex init procedure // for 1.x backwards compatibility. // 兼容vue 1的版本 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. */ // 給vue的實例注冊一個$store的屬性,類似咱們使用vue.$route function vuexInit () { const options = this.$options // store injection if (options.store) { this.$store = typeof options.store === "function" ? options.store() : options.store } else if (options.parent && options.parent.$store) { this.$store = options.parent.$store } } }
這段代碼的總體意思就是就是在vue的聲明周期中進行vuex的初始化,并且對vue的各種版本進行了兼容。vuexInit就是對vuex的初始化。為什么我們能在使用vue.$store這種方法呢,因為在vuexInit中為vue的實例添加了$store屬性
Store構造函數
先看constructor構造函數
constructor (options = {}) { // Auto install if it is not done yet and `window` has `Vue`. // To allow users to avoid auto-installation in some cases, // this code should be placed here. See #731 // 判斷window.vue是否存在,如果不存在那么就安裝 if (!Vue && typeof window !== "undefined" && window.Vue) { install(window.Vue) } // 這個是在開發過程中的一些環節判斷,vuex要求在創建vuex // store實例之前必須先使用這個方法Vue.use(Vuex),并且判斷promise是否可以使用 if (process.env.NODE_ENV !== "production") { assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`) assert(typeof Promise !== "undefined", `vuex requires a Promise polyfill in this browser.`) assert(this instanceof Store, `Store must be called with the new operator.`) } // 提取參數 const { plugins = [], strict = false } = options let { state = {} } = options if (typeof state === "function") { state = state() || {} } // store internal state // 初始化store內部狀態,Obejct.create(null)是ES5的一種方法,Object.create( // object)object是你要繼承的對象,如果是null,那么就是創建一個純凈的對象 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 // 定義dispatch方法 this.dispatch = function boundDispatch (type, payload) { return dispatch.call(store, type, payload) } // 定義commit方法 this.commit = function boundCommit (type, payload, options) { return commit.call(store, type, payload, options) } // strict mode // 嚴格模式 this.strict = strict // init root module. // this also recursively registers all sub-modules // and collects all module getters inside this._wrappedGetters // 初始化根模塊,遞歸注冊所有的子模塊,收集getters installModule(this, state, [], this._modules.root) // initialize the store vm, which is responsible for the reactivity // (also registers _wrappedGetters as computed properties) // 重置vm狀態,同時將getters轉換為computed計算屬性 resetStoreVM(this, state) // apply plugins // 執行每個插件里邊的函數 plugins.forEach(plugin => plugin(this)) if (Vue.config.devtools) { devtoolPlugin(this) } }
因為vuex是由es6進行編寫的,我真覺得es6的模塊簡直是神器,我以前就納悶了像Echarts這種8萬行的庫,他們是怎么寫出來的,上次還看到一個應聘百度的問Echarts的源碼沒有。可怕!后來才知道模塊化。如今es6引入了import這種模塊化語句,讓我們編寫龐大的代碼變得更加簡單了。
if (!Vue && typeof window !== "undefined" && window.Vue) { install(window.Vue) }
這段代碼檢測window.Vue是否存在,如果不存在那么就調用install方法
assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
這行代碼用于確保在我們實例化 Store之前,vue已經存在。
assert(typeof Promise !== "undefined", `vuex requires a Promise polyfill in this browser.`)
這一行代碼用于檢測是否支持Promise,因為vuex中使用了Promise,Promise是es6的語法,但是有的瀏覽器并不支持es6所以我們需要在package.json中加入babel-polyfill用來支持es6
下面來看接下來的代碼
const { plugins = [], strict = false } = options
這利用了es6的解構賦值拿到options中的plugins。es6的解構賦值在我的《前端面試之ES6》中講過,大致就是[a,b,c] = [1,2,3] 等同于a=1,b=2,c=3。后面一句的代碼類似取到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()
這里主要用于創建一些內部的屬性,為什么要加_這是用于辨別屬性,_就表示內部屬性,我們在外部調用這些屬性的時候,就需要小心。
this._committing 標志一個提交狀態 this._actions 用來存儲用戶定義的所有actions this._actionSubscribers this._mutations 用來存儲用戶定義所有的 mutatins this._wrappedGetters 用來存儲用戶定義的所有 getters this._modules this._subscribers 用來存儲所有對 mutation 變化的訂閱者 this._watcherVM 是一個 Vue 對象的實例,主要是利用 Vue 實例方法 $watch 來觀測變化的 commit和dispatch方法在過后再講
installModule(this, state, [], options)
是將我們的options傳入的各種屬性模塊注冊和安裝
resetStoreVM(this, state)
resetStoreVM 方法是初始化 store._vm,觀測 state 和 getters 的變化;最后是應用傳入的插件
下面是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.forEachAction((action, key) => { const type = action.root ? key : namespace + key const handler = action.handler || action registerAction(store, type, handler, local) }) module.forEachGetter((getter, key) => { const namespacedType = namespace + key registerGetter(store, namespacedType, getter, local) }) module.forEachChild((child, key) => { installModule(store, rootState, path.concat(key), child, hot) }) }
這個函數的主要作用是什么呢:初始化組件樹根組件、注冊所有子組件,并將所有的getters存儲到this._wrapperdGetters屬性中
這個函數接受5個函數,store, rootState, path, module, hot。我們來看看他們分別代表什么:
store: 表示當前Store實例
rootState: 表示根state,
path: 其他的參數的含義都比較好理解,那么這個path是如何理解的呢。我們可以將一個store實例看成module的集合。每一個集合也是store的實例。那么path就可以想象成一種層級關系,當你有了rootState和path后,就可以在Path路徑中找到local State。然后每次getters或者setters改變的就是localState
module:表示當前安裝的模塊
hot:當動態改變modules或者熱更新的時候為true
const isRoot = !path.length const namespace = store._modules.getNamespace(path)
用于通過Path的長度來判斷是否為根,下面那一句是用與注冊命名空間
module.forEachMutation((mutation, key) => { const namespacedType = namespace + key registerMutation(store, namespacedType, mutation, local) })
這句是將mutation注冊到模塊上,后面幾句是同樣的效果,就不做一一的解釋了
module.forEachChild((child, key) => { installModule(store, rootState, path.concat(key), child, hot) })
這兒是通過遍歷Modules,遞歸調用installModule安裝模塊。
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) })
}
這段用于判斷如果不是根并且Hot為false的情況,將要執行的俄操作,這兒有一個函數getNestedState (state, path),函數的內容如下:
function getNestedState (state, path) { return path.length ? path.reduce((state, key) => state[key], state) : state }
這個函數的意思拿到該module父級的state,拿到其所在的moduleName,然后調用store._withCommit()方法
store._withCommit(() => { Vue.set(parentState, moduleName, module.state) })
把當前模塊的state添加到parentState,我們使用了_withCommit()方法,_withCommit的方法我們留到commit方法再講。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/51442.html
摘要:的源碼分析系列大概會分為三篇博客來講,為什么分三篇呢,因為寫一篇太多了。這段代碼檢測是否存在,如果不存在那么就調用方法這行代碼用于確保在我們實例化之前,已經存在。每一個集合也是的實例。 vuex的源碼分析系列大概會分為三篇博客來講,為什么分三篇呢,因為寫一篇太多了。您看著費勁,我寫著也累showImg(https://segmentfault.com/img/bVXKqD?w=357&...
摘要:為了更清楚的理解它的原理和實現,還是從源碼開始讀起吧。結構梳理先拋開,的主要源碼一共有三個文件,,初始化相關用到了和我們使用創建的實例并傳遞給的根組件。這個方法的第一個參數是構造器。的中,在保證單次調用的情況下,調用對構造器進入了注入。 原文鏈接 Vuex 作為 Vue 官方的狀態管理架構,借鑒了 Flux 的設計思想,在大型應用中可以理清應用狀態管理的邏輯。為了更清楚的理解它的原理和...
摘要:我們通常稱之為狀態管理模式,用于解決組件間通信的以及多組件共享狀態等問題。創建指定命名空間的輔助函數,總結的功能首先分為兩大類自己的實例使用為組件中使用便利而提供的輔助函數自己內部對數據狀態有兩種功能修改數據狀態異步同步。 what is Vuex ? 這句話我想每個搜索過Vuex官網文檔的人都看到過, 在學習源碼前,當然要有一些前提條件了。 了解Vuex的作用,以及他的使用場景。 ...
摘要:繼續看后面的首先遍歷的,通過和首先獲取然后調用方法,我們來看看是不是感覺這個函數有些熟悉,表示當前實例,表示類型,表示執行的回調函數,表示本地化后的一個變量。必須是一個函數,如果返回的值發生了變化,那么就調用回調函數。 這幾天忙啊,有絕地求生要上分,英雄聯盟新賽季需要上分,就懶著什么也沒寫,很慚愧。這個vuex,vue-router,vue的源碼我半個月前就看的差不多了,但是懶,哈哈。...
閱讀 1357·2021-11-24 09:39
閱讀 1346·2021-11-04 16:12
閱讀 2686·2021-09-24 09:47
閱讀 3337·2021-09-01 10:50
閱讀 1477·2019-08-30 15:55
閱讀 1423·2019-08-30 15:43
閱讀 642·2019-08-30 11:08
閱讀 3578·2019-08-23 18:33