以前閱讀過 redux
的源碼,vuex
一樣也是一個狀態管理工具,以前開發 vue 應用的時候,用到的 vuex 也比較多,因此天然對 vuex 很有興趣,最主要的緣由是 我愛學習
,(很差意思,臉掉地上了),,哈哈哈哈哈哈 javascript
(其實過年這幾天也挺無聊的。。。vue
接下來咱們來回歸正題 java
咱們都知道,在 Vue 中使用 Vuex 必須經過 Vue.use(Vuex) 方法來使用,因此先來瞄一瞄 Vue 對 Vuex 作了什麼不可告人的事,驚恐.gifreact
來到 vue 源碼下的 core/global-api/use.js
git
Vue.use = function (plugin: Function | Object) {
// 檢測該插件是否已經被安裝
if (plugin.installed) {
return
}
// use 支持傳入一個選項對象,如
// Vue.use(MyPlugin, { someOption: true })
const args = toArray(arguments, 1) //獲取可選對象
args.unshift(this) // 將Vue 構造器傳入,當插件的第一個參數
if (typeof plugin.install === 'function') {
// install執行插件安裝
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
plugin.installed = true // 防止再次安裝
return this
}
複製代碼
因此,vue 對插件仍是挺溫柔的,只用了它的一個安裝函數進行插件的安裝。好,咱們來看看 vuex 的 install
函數 在 store.js 的最下面,咱們看到了它的身影github
function install(_Vue) {
if (Vue && _Vue === Vue) {
// 給出警告:說明重複安裝
return
}
Vue = _Vue
applyMixin(Vue)
}
複製代碼
這裏咱們也能夠看到 vuex 本身內部也作了一個限制,防止重複安裝,最後該函數調用了 applyMixin(Vue)
vuex
咻一下,咱們來到 mixin.js
,該函數也很簡單,先是獲取 Vue
的版本號,若是版本是 2.0 以上版本,就使用 Vue.mixin
來在全部組件中混入 beforeCreate
生命鉤子,對應的處理函數是 vuexInit
,反之就向後兼容 1.x
版本redux
function vuexInit() {
// this -> vm
const options = this.$options
// store injection
if (options.store) { // 獲取傳入new Vue({store}) 裏面的 store,並註冊爲 vm 的 $store 屬性,這樣咱們就能在實例中使用 this.$store 訪問 store 對象了
this.$store = typeof options.store === 'function' ?
options.store() :
options.store
} else if (options.parent && options.parent.$store) {
// 子組件從其父組件引用 $store 屬性
this.$store = options.parent.$store
}
}
複製代碼
至此,前菜已經上齊了api
store.js
,別看長長一竄,有500多行,其實看進去了,你會感受,也沒啥可怕嘛 該文件主要由一個 Store
類和一些輔助函數構成,咱們先來看 這個構造類瀏覽器
該構造函數中首先是進行一些必要的判斷,如瀏覽器環境下自動安裝、是否已經安裝了 Vue、是否支持 Promise
,是否已經實例化了 Store 構造函數,其中用到了 assert
斷言函數
// 構造函數
constructor(options = {}) {
// .......
}
// util.js
function assert (condition, msg) {
if (!condition) throw new Error(`[vuex] ${msg}`)
}
複製代碼
而後是從傳入的options 中提取出 plugins
和 strict
,並作一些變量的初始化
// store internal state
// 表示提交狀態,做用是保證對 Vuex 中 state 的修改只能在 mutation 的回調函數中,而不能在外部隨意修改state
this._committing = false
// 用戶定義的 actions
this._actions = Object.create(null)
// action 訂閱者
this._actionSubscribers = []
// // 用戶定義的 mutations
this._mutations = Object.create(null)
// 用戶定義的 getters
this._wrappedGetters = Object.create(null)
// 收集用戶定義的 modules
this._modules = new ModuleCollection(options)
// 模塊命名空間map
this._modulesNamespaceMap = Object.create(null)
// 存儲全部對 mutation 變化的訂閱者,當執行commit時會執行隊列中的函數
this._subscribers = []
// 建立一個 Vue 實例, 利用 $watch 監測 store 數據的變化
this._watcherVM = new Vue()
複製代碼
接着後面是對 dispatch 和 commit 函數中的 this 的從新綁定
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)
}
複製代碼
這樣作是由於在組件中經過 this.$store 直接調用 dispatch/commit 方法時, 可以使 dispatch/commit 方法中的 this 指向當前的 store 對象而不是當前組件的 this。
咱們知道 new Vue
時會把傳入的對象的中的 this
綁定爲 vm
,例如 computed
屬性,裏面咱們寫以下代碼時,會把計算屬性裏面的 this 綁定爲當前的 vm 實例
computed: {
// 計算屬性的 getter
reversedMessage: function () {
// `this` 指向 vm 實例
return this.message.split('').reverse().join('')
}
}
複製代碼
( 上面這段若有不妥,歡迎指出,謝謝 ^_^
接着就是 安裝模塊,vm 組件設置,傳入插件以及 devtoolPlugin
插件的設置了
this.strict = strict
const state = this._modules.root.state
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreVM(this, state)
// apply plugins
plugins.forEach(plugin => plugin(this))
if (Vue.config.devtools) {
devtoolPlugin(this)
}
複製代碼
接下來咱們先不講 dispatch
和 commit
是怎麼實現的,先來重點關注下 modules
這部分,畢竟前面已經 new ModuleCollection(options)
,先來後到嘛,,哈哈
因爲如今的篇幅也算能夠了,怕你們看到長篇大論頭疼,因此咱們轉移陣地。
(若有不當,歡迎指出,謝謝 ^_^
原文地址: 我看Vuex(一)