通過一個六步曲,解開Vuex原理神祕的面紗(手把手詳解,看完還不懂,歡迎寄刀片)

時樾1998發表於2020-04-07

Vuex是什麼?執行原理是什麼?

Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。它採用集中式儲存管理應用的所有元件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。


在View通過Dispatch觸底Action,Action觸發Mutation,最後更新State觸發View更新,這一流程遵循單向資料流的原則。

通過一個六步曲,解開Vuex原理神祕的面紗(手把手詳解,看完還不懂,歡迎寄刀片)

圖來自Vuex官網?

Vuex的簡單定義和用法

import Vuex from 'Vuex'

import Vue from 'vue'

Vue.use(Vuex)

let store = new Vuex.Store({

    state: {
        num: 1
    },

    getters: {

        numStore(state) {

            return state.num + 1

        }

    },

    mutations: {

        addOne(state, params) {

            state.num = state.num + params.num

        }

    },

    actions: {

        addOneAsync({ commit }, params) {
            setTimeout(() => {
                commit('addOne', params)
            }, 1000)
        }

    }

})

export default store
複製程式碼

在View中可以通過this.$store.commit觸發mutation,或者通過this.$store.dispatch觸發action。這裡不會詳細介紹Vuex怎麼去使用,這篇嘮嗑主要講的Vuex的實現原理,入門的話可以到官網溜一波

溫馨提示:定義好的Vuex就得在Vue例項引數傳上Vuex例項

new Vue({
  store,
  render: h => h(App),
}).$mount('#app')
複製程式碼

Vuex的原理是怎麼樣的?六步帶你撕開它神祕的面紗

第一步,初始化Vuex檔案

在自己感覺看著舒服的微信建一個miniVuex.js檔案
複製程式碼
let Vue
    
class Store{

    constructor(options) {}
    
}
function install(_Vue){
    
}
    
export default { Store, install }

複製程式碼

為什麼要定義這Store跟install方法?

Store方法應該大家都很好理解,回想一下Vuex的用法,是不是上來就給它來一個new Vuex.Store?然後傳進去一個Object?

import Vuex from 'Vuex'

let store = new Vuex.Store({})

複製程式碼

再回看自己定義Store函式,是不是清晰很多了?Store函式接收一個options實參物件

可能大家對於為什麼要定義install函式有點困惑(如果開發過Vue外掛,或者熟悉Vue.use()Api的大佬,請忽略),我們再回想一下當,匯出Vuex的時候,是不是要Vue.use(Vuex)一下?

import Vuex from 'Vuex'

Vue.use(Vuex)

複製程式碼

因為當Vue.use(Vuex)Api的時候,Vue.use會將呼叫外掛(這裡是Vuex)install函式,然後將Vue傳進去,並可以在install函式裡通過this可以訪問Vue例項。所以在開發外掛的時候,install函式是必須要提供的。

第二步,實現install方法

Vuexinstall函式,主要是拿到Vue儲存起來,然後,通過Vue.mixin混入將store掛載到Vue.prototype上;至於平時在頁面上我們為什麼能通過this.$store訪問Vuex例項,就是在這裡混入進去的;至於使用Vue.mixin混入來掛載Vuex例項Vue.prototype,主要是因為,我只想在new Vue的時候只在beforeCreate鉤子裡掛載一次就ok,不想每個頁面都去重新掛載一次。

具體程式碼如下?


let Vue

class Store {
    constructor(options) {}
}

// 新增install實現
function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }

複製程式碼

第三步,實現state的雙向繫結

實現staet的雙向繫結是件非常簡單的事,我們只需要利用Vue自身的雙向繫結機制就就可以實現;我們主要將new Vuex.Store({state:{}})存放在一個new Vue 例項上就可以。之所以Vuex官網說Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式,就是因為Vuex內部實現雙向繫結的時候,使用的是Vue自身的雙向繫結機制,將Vue強強的綁在身上。

let Vue

class Store {

    constructor(options) {
    
        //新增程式碼
        this.state = new Vue({

            data: options.state && options.state || {}

        })
        
    }
}


function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }
複製程式碼

第四步,實現mutations和commit

實現mutations只要是將傳進來的mutations儲存到例項就行,如果不傳就預設為一個空物件;在實現commite之前,先來看看commit的用法。

 this.$store.commit(type, params)
複製程式碼

commit主要是將傳進來的type,找到對應的mutations裡面的函式,進行觸發,並將this.state傳給它的一個引數(因為在定義mutations的時候是不是(可以引數下面第一段示例程式碼)在第一個引數可以訪問state裡面的資料?至於為什麼可以訪問,就是在這個時候傳進去的),第二個引數才是額外的引數。

mutations定義示例程式碼

 mutations: {

        addOne(state, params) {

            state.num = state.num + params.num

        }

    }
複製程式碼

具體程式碼如下?


let Vue

class Store {

    constructor(options) {

        this.state = new Vue({

            data: options.state && options.state || {}

        })
        //新增程式碼
        this.mutations = options.mutations && options.mutations || {}

    }
    // 新增程式碼
    commit = (type, params) => {

        this.mutations[type]&&this.mutations[type](this.state, params)

    }

}

function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }
複製程式碼

至於commit為什麼要使用箭頭函式,這裡做一個解釋,因為在actions裡面異地再提交一下commit(如下面示例程式碼),那麼會導致this中的上下文產生變化,不再指向Store例項就報一個mutations of undefined的錯誤(如下面圖片);為了避免這種情況出現,使用了箭頭函式。

示例程式碼

actions: {

        addOneAsync({ commit }, params) {
            setTimeout(() => {
                commit('addOne', params)
            }, 1000)
        }

    }
    
複製程式碼

報錯提示

通過一個六步曲,解開Vuex原理神祕的面紗(手把手詳解,看完還不懂,歡迎寄刀片)

第五步,實現getters

getters的原理,也不難理解,但是實現就要手動通過(Object.defineProperty)去監聽,它的那個getter別使用了,再觸發對應的getter,getters是隻讀,所以在Object.defineProperty監聽的時候,只需要提供get(有傳的前提在才會進行Object.defineProperty監聽,不傳的話,什麼也不敢),然後把this.state傳給它的一個引數(因為在定義getter的時候,可以通過第一個引數訪問state,可參考下面的示例)。

溫馨提示:上面的getters是指整個大的getters,getter是指的getters裡面小的各個鍵值對(比如下面示例中的numStore),我怕有同學會不知道,還是提一下

定義getters示例

getters: {

        numStore(state) {

            return state.num + 1

        }

    }
複製程式碼

具體實現程式碼?


let Vue

class Store {

    constructor(options) {

        this.state = new Vue({

            data: options.state && options.state || {}

        })

        this.mutations = options.mutations && options.mutations || {}
        //新增程式碼
        options.getters && this.initGetters(options.getters)

    }
    //新增程式碼
    initGetters(getters) {

        this.getters = {}

        Object.keys(getters).forEach(key => {

            Object.defineProperty(this.getters, key, {

                get: () => {

                    return getters[key](this.state)

                }

            })

        })

    }

    commit = (type, params) => {

        this.mutations[type]&&this.mutations[type](this.state, params)

    }

}


function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }
複製程式碼

第六步,實現actions和dispatch

actionsdispatch的實現跟mutationscommit非常類似;實現actions只要是將傳進來的actions儲存到例項就行,如果不傳就預設為一個空物件;在實現dispatch之前,先來看看dispatch的用法。

 this.$store.dispacth(type, params)
複製程式碼

dispacth主要是將傳進來的type,找到對應的actions裡面的函式,進行觸發,並將一個Object(裡面包含commit,dispatch,state)傳給它的一個引數(因為在定義actions的時候是不是在第一個引數可以訪問這些(commit,dispatch,state...可以引數下面第一段示例程式碼)?至於為什麼可以訪問,就是在這個時候傳進去的),第二個引數才是額外的引數。

示例程式碼

actions: {
        addOneAsync({ commit,dispatch,state }, params) {
            setTimeout(() => {
                commit('addOne', params)
            }, 1000)
        }
    }
複製程式碼

具體實現程式碼?


let Vue

class Store {

    constructor(options) {

        this.state = new Vue({

            data: options.state && options.state || {}

        })

        this.mutations = options.mutations && options.mutations || {}
        //新增程式碼
        this.actions = options.actions && options.actions || {}

        options.getters && this.initGetters(options.getters)

    }

    initGetters(getters) {

        this.getters = {}

        Object.keys(getters).forEach(key => {

            Object.defineProperty(this.getters, key, {

                get: () => {

                    return getters[key](this.state)

                }

            })

        })

    }

    commit = (type, params) => {

        this.mutations[type](this.state, params)

    }
    //新增程式碼
    dispatch(type, params) {

        this.actions[type] && this.actions[type]({
            commit: this.commit,
            state: this.state,
            dispatch: this.dispatch
        }, params)

    }

}


function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }

複製程式碼

好了,以上那段也是整個miniVuex的完整程式碼了,裡面我們實現了state,getters,mutations,actions,commit,dispatch;至於怎麼使用,就跟之前怎麼用Vuex一樣,把import的路徑換成我們自己的miniVuex路徑

使用例子:


import Vuex from './miniVuex'

import Vue from 'vue'

Vue.use(Vuex)

let store = new Vuex.Store({

    state: {},

    getters: {},

    mutations:{},

    actions: {}
    
})

export default store

複製程式碼

如果,你每一步都看懂了,你還怕面試官問你Vuex的原理?還怕他讓你手寫?還害怕逼逼不贏他?

通過一個六步曲,解開Vuex原理神祕的面紗(手把手詳解,看完還不懂,歡迎寄刀片)

有錯誤的地方,歡迎大佬指出;有解釋不夠充分的地方,歡迎大佬補充;有更好的改進方法,歡迎大佬提供,一起交流交流...

相關文章