如何手寫一個(gè)簡易的 Vuex
前言
本文適合使用過 Vuex 的人閱讀,來了解下怎么自己實(shí)現(xiàn)一個(gè) Vuex。
基本骨架
這是本項(xiàng)目的src/store/index.js文件,看看一般 vuex 的使用
import Vue from 'vue' import Vuex from './myvuex' // 引入自己寫的 vuex import * as getters from './getters' import * as actions from './actions' import state from './state' import mutations from './mutations' Vue.use(Vuex) // Vue.use(plugin)方法使用vuex插件 // vuex 導(dǎo)出一個(gè)類叫Store,并傳入對象作為參數(shù) export default new Vuex.Store({ state, mutations, actions, getters, })
Vue.use
的用法:
安裝 Vue.js 插件。如果插件是一個(gè)對象,必須提供 install 方法。如果插件是一個(gè)函數(shù),它會被作為 install 方法。install 方法調(diào)用時(shí),會將 Vue 作為參數(shù)傳入。這個(gè)方法的第一個(gè)參數(shù)是 Vue 構(gòu)造器,第二個(gè)參數(shù)是一個(gè)可選的選項(xiàng)對象。
- 該方法需要在調(diào)用 new Vue() 之前被調(diào)用。
- 當(dāng) install 方法被同一個(gè)插件多次調(diào)用,插件將只會被安裝一次。
即是我們需要在./myvuex.js
中導(dǎo)出 install
方法,同時(shí)導(dǎo)出一個(gè)類Store
,于是第一步可以寫出代碼:
let Vue = null class Store { constructor(options) {} } function install(_Vue) { Vue = _Vue // 上面Store類需要能獲取到Vue } export default { Store, install, }
install 方法
當(dāng)我們使用 vuex 的時(shí)候,每一個(gè)組件上面都有一個(gè)this.$store屬性,里面包含了 state,mutations, actions, getters 等,所以我們也需要在每個(gè)組件上都掛載一個(gè)$store 屬性,要讓每一個(gè)組件都能獲取到,這里我們使用Vue.mixin(mixin),用法介紹如下:
全局注冊一個(gè)混入,影響注冊之后所有創(chuàng)建的每個(gè) Vue 實(shí)例??梢允褂没烊胂蚪M件注入自定義的行為,它將影響每一個(gè)之后創(chuàng)建的 Vue 實(shí)例。
function install(_Vue) { Vue = _Vue // install方法調(diào)用時(shí),會將Vue作為參數(shù)傳入(上面Store類需要用到Vue) // 實(shí)現(xiàn)每一個(gè)組件,都能通過this調(diào)用$store Vue.mixin({ beforeCreate() { // 通過this.$options可以獲取new Vue({參數(shù)}) 傳遞的參數(shù) if (this.$options && this.$options.store) { // 證明這個(gè)this是根實(shí)例,也就是new Vue產(chǎn)生的那個(gè)實(shí)例 this.$store = this.$options.store } else if (this.$parent && this.$parent.$store) { // 子組件獲取父組件的$store屬性 this.$store = this.$parent.$store } }, }) }
state
由于 Vuex 是基于 Vue 的響應(yīng)式原理基礎(chǔ),所以我們要讓數(shù)據(jù)改變可刷新視圖,則需要創(chuàng)建一個(gè) vue 實(shí)例
class Store { // options 即是 Vuex.Store({})傳入的參數(shù) constructor(options) { // vuex 的核心就是借用了vue的實(shí)例,因?yàn)関ue的實(shí)例數(shù)據(jù)變化,會刷新視圖 let vm = new Vue({ data: { state: options.state, }, }) // state this.state = vm.state } }
commit
我們使用 vuex 改變數(shù)據(jù)時(shí),是觸發(fā) commit 方法,即是這樣使用的:
this.$store.commit('eventName', '參數(shù)' );
所以我們要實(shí)現(xiàn)一個(gè)commit方法,把 Store 構(gòu)造函數(shù)傳入的 mutations 做下處理
class Store { constructor(options) { // 實(shí)現(xiàn) state ... // mutations this.mutations = {} // 存儲傳進(jìn)來的mutations let mutations = options.mutations || {} // 循環(huán)取出事件名進(jìn)行處理(mutations[事件名]: 執(zhí)行方法) Object.keys(mutations).forEach(key => { this.mutations[key] = params => { mutations[key].call(this, this.state, params) // 修正this指向 } }) } commit = (key, params) => { // key為要觸發(fā)的事件名 this.mutations[key](params) } }
dispatch
跟上面的 commit 流程同理
class Store { constructor(options = {}) { // ... // actions this.actions = {} let actions = options.actions || {} Object.keys(actions).forEach(key => { this.actions[key] = params => { actions[key].call(this, this, params) } }) } dispatch = (type, payload) => { this.actions[type](payload) } }
getters
getters 實(shí)際就是返回 state 的值,在使用的時(shí)候是放在 computed 屬性,每一個(gè) getter 都是函數(shù)形式;
getters 是需要雙向綁定的。但不需要雙向綁定所有的 getters,只需要綁定項(xiàng)目中事件使用的 getters。
這里使用Object.defineProperty()方法,它會直接在一個(gè)對象上定義一個(gè)新屬性,或者修改一個(gè)對象的現(xiàn)有屬性,并返回此對象。
class Store { constructor(options = {}) { // ... // getters this.getters = {} let getters = options.getters || {} Object.keys(getters).forEach(key => { Object.defineProperty(this.getters, key, { get: () => { return getters[key].call(this, this.state) }, }) }) } }
到此為止,已經(jīng)可以使用我們自己寫的 vuex 做一些基本操作了,但只能通過this.$store.xx的形式調(diào)用,故需要再實(shí)現(xiàn)方法。
map 輔助函數(shù)
先來說說 mapState
沒有 map 輔助函數(shù)之前這樣使用:
computed: { count () { return this.$store.state.count } }
當(dāng)映射的計(jì)算屬性的名稱與 state 的子節(jié)點(diǎn)名稱相同時(shí),給 mapState 傳一個(gè)字符串?dāng)?shù)組。
computed: { // 使用對象展開運(yùn)算符將此對象混入到外部對象中 ...mapState(['count']) }
我們這里簡單就只實(shí)現(xiàn)數(shù)組的情況
export const mapState = args => { let obj = {} args.forEach(item => { obj[item] = function() { return this.$store.state[item] } }) return obj }
之后幾個(gè) map 輔助函數(shù)都是類似
- mapGetters
export const mapGetters = args => { let obj = {} args.forEach(item => { obj[item] = function() { return this.$store.getters[item] } }) return obj }
- mapMutations
export const mapMutations = args => { let obj = {} args.forEach(item => { obj[item] = function(params) { return this.$store.commit(item, params) } }) return obj }
- mapActions
export const mapActions = args => { let obj = {} args.forEach(item => { obj[item] = function(payload) { return this.$store.dispatch(item, payload) } }) return obj }
完整代碼
let Vue = null class Store { constructor(options) { // vuex 的核心就是借用了vue的實(shí)例,因?yàn)関ue的實(shí)例數(shù)據(jù)變化,會刷新視圖 let vm = new Vue({ data: { state: options.state, }, }) // state this.state = vm.state // mutations this.mutations = {} // 存儲傳進(jìn)來的mutations let mutations = options.mutations || {} Object.keys(mutations).forEach(key => { this.mutations[key] = params => { mutations[key].call(this, this.state, params) } }) // actions this.actions = {} let actions = options.actions || {} Object.keys(actions).forEach(key => { this.actions[key] = params => { actions[key].call(this, this, params) } }) // getters this.getters = {} let getters = options.getters || {} Object.keys(getters).forEach(key => { Object.defineProperty(this.getters, key, { get: () => { return getters[key].call(this, this.state) }, }) }) } commit = (key, params) => { this.mutations[key](params) } dispatch = (type, payload) => { this.actions[type](payload) } } export const mapState = args => { let obj = {} args.forEach(item => { obj[item] = function() { return this.$store.state[item] } }) return obj } export const mapGetters = args => { let obj = {} args.forEach(item => { obj[item] = function() { return this.$store.getters[item] } }) return obj } export const mapMutations = args => { let obj = {} args.forEach(item => { obj[item] = function(params) { return this.$store.commit(item, params) } }) return obj } export const mapActions = args => { let obj = {} args.forEach(item => { obj[item] = function(payload) { return this.$store.dispatch(item, payload) } }) return obj } function install(_Vue) { Vue = _Vue // install方法調(diào)用時(shí),會將Vue作為參數(shù)傳入(上面Store類需要用到Vue) // 實(shí)現(xiàn)每一個(gè)組件,都能通過this調(diào)用$store Vue.mixin({ beforeCreate() { // 通過this.$options可以獲取new Vue({參數(shù)}) 傳遞的參數(shù) if (this.$options && this.$options.store) { // 證明這個(gè)this是根實(shí)例,也就是new Vue產(chǎn)生的那個(gè)實(shí)例 this.$store = this.$options.store } else if (this.$parent && this.$parent.$store) { // 子組件獲取父組件的$store屬性 this.$store = this.$parent.$store } }, }) } export default { Store, install, }
以上就是如何手寫一個(gè)簡易的 Vuex的詳細(xì)內(nèi)容,更多關(guān)于手寫 vuex的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
BuildAdmin elementPlus自定義表頭添加tooltip方法示例
這篇文章主要介紹了BuildAdmin elementPlus 自定義表頭,添加tooltip實(shí)現(xiàn)方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09Vue進(jìn)階之利用transition標(biāo)簽實(shí)現(xiàn)頁面跳轉(zhuǎn)動畫
這篇文章主要為大家詳細(xì)介紹了Vue如何利用transition標(biāo)簽實(shí)現(xiàn)頁面跳轉(zhuǎn)動畫,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起一下2023-08-08WebStorm啟動vue項(xiàng)目報(bào)錯代碼:1080?throw?err解決辦法
在使用webstorm新建vue項(xiàng)目時(shí)常會遇到一些報(bào)錯,下面這篇文章主要給大家介紹了關(guān)于WebStorm啟動vue項(xiàng)目報(bào)錯代碼:1080?throw?err的解決辦法,文中將解決辦法介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12uniapp使用webview內(nèi)嵌H5的注意事項(xiàng)詳解
在移動應(yīng)用開發(fā)中,uniApp框架提供了一種跨平臺的解決方案,允許開發(fā)者使用一套代碼來構(gòu)建iOS、Android等多平臺的應(yīng)用,這篇文章主要給大家介紹了關(guān)于uniapp使用webview內(nèi)嵌H5的注意事項(xiàng),需要的朋友可以參考下2024-07-07基于vue-cli npm run build之后vendor.js文件過大的解決方法
今天小編就為大家分享一篇基于vue-cli npm run build之后vendor.js文件過大的解決方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09vue-cli創(chuàng)建的項(xiàng)目中的gitHooks原理解析
這篇文章主要介紹了vue-cli創(chuàng)建的項(xiàng)目中的gitHooks原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02基于Vue+element-ui 的Table二次封裝的實(shí)現(xiàn)
這篇文章主要介紹了基于Vue+element-ui 的Table二次封裝的實(shí)現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07