您好,登錄后才能下訂單哦!
這篇文章主要介紹“Vuex狀態(tài)管理的方法是什么”,在日常操作中,相信很多人在Vuex狀態(tài)管理的方法是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Vuex狀態(tài)管理的方法是什么”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
(圖2)
一圖勝千言,如圖 2 。當(dāng)然我們想到的最簡單的方案,通過實現(xiàn)一個中心化的事件處理中心,來記錄組件內(nèi)的訂閱者,當(dāng)需要協(xié)同時就通過自定義事件通知到各個相關(guān)的組件內(nèi)部的訂閱者。當(dāng)然通知中可以攜帶 payload 參數(shù)信息,達到數(shù)據(jù)共享的目的。其實 Vue 本身也自帶一個自定義事件系統(tǒng), Vue 組件之間的自定義事件就是基于此來實現(xiàn),詳細(xì) api 請參與 Vue 文檔。我們可以基于 Vue 本身實現(xiàn) EventBus 的機制,不需要引入新的依賴,減少 bundle 體積,api使用如下述代碼。
const vm = new Vue() // 注冊訂閱者 vm.$on('event-name', (payload) => {/*執(zhí)行業(yè)務(wù)邏輯*/}) // 注冊訂閱者,執(zhí)行一次后自動取消訂閱者 vm.$once('some-event-name', (payload) => {/*執(zhí)行業(yè)務(wù)邏輯*/}) // 取消某事件的某個訂閱者 vm.$off('event-name',[callback]) // 通知各個訂閱者執(zhí)行相對應(yīng)的業(yè)務(wù)邏輯 vm.$emit('event-name',payload)
在實踐中發(fā)現(xiàn)基于 EventBus 的數(shù)據(jù)狀態(tài)管理模式的優(yōu)點:
代碼的實現(xiàn)比較簡單,設(shè)計方案容易理解
很輕量的方式就可以完成組件之間的解耦,將組件之間的強耦合變成對 EventBus 的弱耦合。
當(dāng)然EventBus方案的也會有些不足:
因為業(yè)務(wù)邏輯分散在多個組件訂閱者中,所以導(dǎo)致業(yè)務(wù)邏輯的處理變得碎片化,缺乏連貫的上下文。
在閱讀和維護代碼時,需要在代碼中不斷去尋找訂閱者,導(dǎo)致業(yè)務(wù)流程理解上的中斷和注意力的分散。
在認(rèn)識到 EventBus 的架構(gòu)設(shè)計上的不足時,我們也會 Eating our own dog food,實現(xiàn)了一套可視化的機制,通過對代碼的抽象語法樹的分析,提取訂閱者和發(fā)送者的信息,可視化顯示他們之間的關(guān)聯(lián)關(guān)系,幫助我們快速理解問題。
另外,對于復(fù)雜的業(yè)務(wù)邏輯設(shè)計出【前置腳本】的改進方案。例如,活動頁面雖然是由多個RSC組件構(gòu)成,但是請求的服務(wù)端接口還是一個,包含了頁面初始化狀態(tài)的所有的數(shù)據(jù),此時我們就可以在前置腳本中統(tǒng)一處理獲取數(shù)據(jù)的邏輯,然后再同步到各個RSC組件內(nèi)部。【前置腳本】的方式,就是抽取一個全局的對象,包含共享的狀態(tài)和業(yè)務(wù)邏輯。多個組件依賴這個全局的對象,架構(gòu)設(shè)計如圖3,是對 EventBus 方案的一個補充。
(圖3)
通過前置腳本,可以解決復(fù)雜業(yè)務(wù)難以維護理解的問題,但是也帶來一些風(fēng)險點如需要暴露全局對象,有被覆蓋或者被修改的風(fēng)險。經(jīng)過前置腳本的改進之后,我們越來越清晰的感受到我們需要的狀態(tài)管理模式是什么樣子,那就是 Vuex 。那接下來我們就聊聊Vuex。
Vuex 是什么?
Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。Vuex 也集成到 Vue 的官方調(diào)試工具 devtools extension,提供了諸如零配置的 time-travel 調(diào)試、狀態(tài)快照導(dǎo)入導(dǎo)出等高級調(diào)試功能。
Vuex 有哪些特點?
集中式的組件狀態(tài)管理,支持動態(tài)注冊 store
與 Vue 的匹配度高,底層基于 Vue 的響應(yīng)式數(shù)據(jù)特性來實現(xiàn),保持了和 Vue 一樣的數(shù)據(jù)處理特點
熟悉 Vue 后可以快速上手 Vuex ,學(xué)習(xí)成本比較低
完善的開發(fā)體驗,官方的 devtools 和 time-travel 調(diào)試等,幫助開發(fā)者梳理數(shù)據(jù)可預(yù)測的變化
Vuex 是一個通用狀態(tài)管理框架,怎么無縫融入到我們的 RSC 組件體系中呢?我們需要在項目中引入對 Vuex 的依賴和支持,在頂層的 Vue 中添加對 store 的依賴。
我們項目的基本結(jié)構(gòu):
. └── src ├── App.vue ├── app.less ├── assets ├── component ├── directive ├── main.js ├── stat ├── store └── utils ├── babel.config.js ├── package.json ├── public
2.1 添加依賴
根據(jù)規(guī)范,首先在我們的項目目錄中的 package.json 中添加對 Vuex 的依賴
{ "dependencies": { "vuex": "^3.0.1" } }
//store/index.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export const store = new Vuex.Store({ // 狀態(tài)數(shù)據(jù) state() { return {} }, getters: {}, mutations: {}, actions: {}, })
將上述創(chuàng)建 store對象注入到頂層的 Vue 對象中,這樣所有的 Vue 組件就會通過 this.$store 獲取頂層的 store 對象。另外, Vuex 還提供了好用的工具類方法 ( mapState , mapActions , mapGetters , mapMutations ) 來進行數(shù)據(jù)共享和協(xié)同。
// App.vueimport { store } from './store'new Vue({ // 注入 store // 在所有的改 Vue 管理的 vue 對象中都可以通過 this.$store 來獲取 store, })
我們還是希望在開發(fā)組件時,開發(fā)者大部分時間只關(guān)注自己的展現(xiàn)和業(yè)務(wù)邏輯,只是在組件在活動頁中被渲染時,才將自身狀態(tài)共享到頂層的 store 中去。所以組件具有自身的獨立 store 狀態(tài)管理,通過 namespace 命名空間進行模塊的狀態(tài)隔離,然后在組件的 beforeCreate 生命周期方法內(nèi),通過 Vuex 的 registerModule 進行動態(tài)的 store 的注冊。
可以通過抽取公共 StoreMixin 來簡化這一過程,還可以自動開啟 namespaced: true 和針對當(dāng)前的命名空間擴展快捷方法和屬性。代碼如下:
// store-mixn.jsexport default function StoreMixin(ns, store) { return beforeCreate() { // 保證 namespace 唯一性 // 開發(fā)者可以通過函數(shù)生成唯一的namespace // 框架可以生成唯一的namespace const namespace = isFn(ns) ? ns(this) : gen(ns) this.$ns = namespace store.namespaced = true this.$store.registerModule(namespace, store) // 擴展快捷方法和屬性 this.$state = this.$store.state[namespace] this.$dispatch = (action, payload) => this.$store.dispatch(`${namespace}/${action}`, payload) this.$getter = //... this.$commit = //... } }
//store.js// 當(dāng)前組件自有storeexport default { // 組件自身的狀態(tài) state() { return {} }, mutations: {}, getters: {}, //...other things}// code.vue// 組件對外的入口模塊import store from './store'export default { mixins: [StoreMixin(/*namespace*/ 'hello', /* 組件的 store */ store)], }
因為在一個活動中 RSC 組件會被重復(fù)加載多次,所有也會導(dǎo)致相同 namespace 的 store 模塊重復(fù)加載導(dǎo)致模塊覆蓋。怎么保證 namespace 的唯一性呢?我們可以,在 StoreMixin 中進行 namespace 注冊的時候,判斷有沒有相同的 namespace ,如果有就對 namespace 做一次重命名。比如在已經(jīng)注冊了 hello 為命令空間的 store 時,再次注冊 namspace hello 自動會變成 hello1 ,自動做區(qū)分。簡單的算法實現(xiàn)如下,
// gen.js// 生成唯一的 namespaceconst g = window || global g.__namespaceCache__ = g.__namespaceCache__ || {}/** * 生成唯一的 moduleName, 同名 name 默認(rèn)自動增長 * @param {*} name */export default function genUniqueNamespace(name) { let cache = g.__namespaceCache__ if (cache[name]) { cache[name].count += 1 } else { cache[name] = { count: 0, } } return name + (cache[name].count === 0 ? '' : cache[name].count) }
另外,開發(fā)者可以通過 store-mixin 中傳遞自定義函數(shù)來生成唯一的 namespace 標(biāo)識。比如,如下代碼,根據(jù) vue-router 中的路由動態(tài)參數(shù)來設(shè)置 namespace
export default { mixins: [StoreMixin((vm) => vm.$router.params.spuId), store], }
因為動態(tài) namespace 就會帶來不確定性的問題,如下代碼示例,假如hello被重命名為hello1, 另外在 Vuex 中 mapXXX ( mapState , mapMutations 等)方法時,需要精確傳遞 namespace 才能獲取組件內(nèi) store 的上下文。
// code.vueexport default { mixins: [StoreMixin('hello', store)], computed: { ...mapGetters('hello', [ /* hello namespace store getter */ ]), ...mapState('hello', [ /* hello namespace state property */ ]), }, methods: { ...mapActions('hello', [ /* hello namespace actions method */ ]), ...mapMutations('hello', [ /* hello namespace mutations method */ ]), }, }
怎么解決 Vuex mapXXX 方法中動態(tài) namespace 的問題?首先我們我們想到的是在 StoreMixin 中將 namespace 設(shè)置在 Vue 的 this.$ns 對象上,這樣被 StoreMixin 混入的組件就就可以動態(tài)獲取 namespace 。
// store-mixn.jsexport default function StoreMixin(ns, store) { return beforeCreate() { // 保證 namespace 唯一性 const namespace = gen(ns) // 將重命名后的 namespace 掛載到當(dāng)前 vue 對象的$ns 屬性上 this.$ns = namespace //... } }
雖然我們可以在組件內(nèi)通過 this.$ns 獲取組件中的 store 的命名空間,假想著我們可以:
// code.vueexport default { computed: { ...mapGetter(this.$ns, [ /* hello namespace store getter */ ]), ...mapState(this.$ns, [ /* hello namespace state property */ ]), }, methods: { ...mapActions(this.$ns, [ /* hello namespace actions method */ ]), ...mapMutations(this.$ns, [ /* hello namespace mutations method */ ]), }, }
很遺憾,在這個時刻 this 根本就不是當(dāng)前 Vue 的實例,this.$ns 華麗麗的 undefined。那怎么辦呢?JS 有很多函數(shù)式編程的特點,函數(shù)也是值,可以作為參數(shù)等進行傳遞,其實函數(shù)除了具有值特性外還有一個很重要的特性就是 lazy computed 惰性計算。基于這樣的思考,對 mapXX 方法進行擴展,支持動態(tài)的 namespace 。然后在 mapXXX 方法中,等到 vm 是當(dāng)前 Vue 的組件實例時,才去獲取當(dāng)前的組件的 namespace 。
// code.vueimport { mapGetters, mapState, mapActions, mapMutations } from 'vuex-helper-ext'export default { computed: { ...mapGetters((vm) => vm.$ns, [ /* hello namespace store getter */ ]), ...mapState((vm) => vm.$ns, [ /* hello namespace state property */ ]), }, methods: { ...mapActions((vm) => vm.$ns, [ /* hello namespace actions method */ ]), ...mapMutations((vm) => vm.$ns, [ /* hello namespace mutations method */ ]), }, }
我相信你,肯定發(fā)現(xiàn)了其中一個問題,this.$ns 只能 StoreMixin 的組件內(nèi)獲取到,那該組件的子組件怎么辦呢?怎么解決子組件獲取父組件的 namespace ?這個時候我們就需要借助 Vue 強悍的 mixin 的體系了,設(shè)計一個全局 mixin ,在組件創(chuàng)建的時候判斷父組件有沒有 $ns 對象,如果存在就將當(dāng)前的組件的 $ns 設(shè)置為父組件一致,如果沒有就跳過。
function injectNamespace(Vue) { Vue.mixin({ beforeCreate: function _injectNamespace() { const popts = this.$options.parent; if (popts && popts.$ns) { this.$ns = popts.$ns; const namespace = this.$ns; // 為組件擴展快捷方法和屬性 this.$state = this.$store.state[namespace] this.$dispatch = (action, payload) => this.$store.dispatch(`${namespace}/${action}`, payload) this.$getter = //... this.$commit = //... } } }); }// main.jsVue.use(injectNamespace);
這樣子組件就會默認(rèn)獲取父組件設(shè)置的 namespace ,有了這個 mixin 的魔力,我們就可以把 mapXXX 方法的設(shè)計的擴展更優(yōu)雅的一點,因為在 mapXX 方法中可以以 $ns 屬性為默認(rèn)的 namespace 。更清爽一點,保持和官方一致的風(fēng)格, 這樣才把 Vuex 更好的融入我們體系中去。
// code.vue export default { computed: { ...mapGetter([ /* hello namespace store getter */ ]), ...mapState([ /* hello namespace state property */ ]), }, methods: { ...mapActions([ /* hello namespace actions method */ ]), ...mapMutations([ /* hello namespace mutations method */ ]), }, }
通過下面的小栗子,我們可以看到對于開發(fā)者來說,只要按照標(biāo)準(zhǔn)的 Vuex 的開發(fā)方式來開發(fā)就可以了,好似什么都沒有發(fā)生過 ^_^。其實在內(nèi)部我們做了很多的努力,架構(gòu)設(shè)計的目的就是【讓簡單的事情變得更加簡單 , 讓復(fù)雜的事情變得可能】。
store.js RSC 組件自有 store
export default { state() { return { mott: 'hello vue' } }, mutations: { changeMott(state) { state.mott = 'hello vuex' }, }, }
text.vue text 子組件,mapState 自動動態(tài)獲取命名空間
<template> <div @click="changeMott">{{ mott }}</div> </template> <script> import { mapState, mapMutations } from 'vuex-helper-ext' export default { computed: { ...mapState(['mott']), }, methods: { ...mapMutations(['changeMott']), }, } </script>
code.vue
<tempalte> <text></text> </template> <script> import store from './store'; import text from './text'; export default { mixins: [StoreMixin('hello', store)], components: { text }, methods: { // .... } } </script>
到此,關(guān)于“Vuex狀態(tài)管理的方法是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。