在Vue 3中怎么管理共享狀態(tài),針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。
發(fā)展壯大離不開廣大客戶長(zhǎng)期以來的信賴與支持,我們將始終秉承“誠(chéng)信為本、服務(wù)至上”的服務(wù)理念,堅(jiān)持“二合一”的優(yōu)良服務(wù)模式,真誠(chéng)服務(wù)每家企業(yè),認(rèn)真做好每個(gè)細(xì)節(jié),不斷完善自我,成就企業(yè),實(shí)現(xiàn)共贏。行業(yè)涉及成都崗?fù)?/a>等,在重慶網(wǎng)站建設(shè)公司、營(yíng)銷型網(wǎng)站、WAP手機(jī)網(wǎng)站、VI設(shè)計(jì)、軟件開發(fā)等項(xiàng)目上具有豐富的設(shè)計(jì)經(jīng)驗(yàn)。
編寫大型應(yīng)用程序可能是一個(gè)挑戰(zhàn)。在Vue3應(yīng)用程序中使用共享狀態(tài)可以解決項(xiàng)目復(fù)雜性。有很多常見的狀態(tài)解決方案,我將深入探討使用工廠、共享對(duì)象和Vuex等方法的優(yōu)缺點(diǎn)。我還將向你展示Vuex5中可能改變我們?cè)赩ue3中使用共享狀態(tài)的一些內(nèi)容。
state
可能很難,當(dāng)我們開始一個(gè)簡(jiǎn)單的Vue項(xiàng)目時(shí),只需要我們保持特定組件的工作狀態(tài)就很簡(jiǎn)單:
setup() { let books: Work[] = reactive([]); onMounted(async () => { // Call the API const response = await bookService.getScienceBooks(); if (response.status === 200) { books.splice(0, books.length, ...response.data.works); } }); return { books }; },
當(dāng)你的項(xiàng)目是顯示數(shù)據(jù)的單一頁面(可能是為了排序或過濾數(shù)據(jù))時(shí),這可能很有吸引力。但是在本例中,該組件將獲得每個(gè)請(qǐng)求的數(shù)據(jù)。如果你想保留它呢?這時(shí)候狀態(tài)管理就可以發(fā)揮作用了。由于網(wǎng)絡(luò)連接通常代價(jià)很高并且偶爾不太可靠。因此在瀏覽應(yīng)用程序時(shí)最好保持一些狀態(tài)。
另一個(gè)問題是組件之間的通信。雖然您可以使用events
和props
直接與child
-parents
通信, 但是當(dāng)你的每個(gè)views/pages都是獨(dú)立的時(shí)候,處理諸如錯(cuò)誤攔截和忙碌標(biāo)志之類的簡(jiǎn)單情況會(huì)變得很困難。舉個(gè)例子, 假設(shè)你使用了一個(gè)頂級(jí)控件來顯示error和加載動(dòng)畫:
// App.vue <template> <div class="container mx-auto bg-gray-100 p-1"> <router-link to="/"><h2>Bookcase</h2></router-link> <div class="alert" v-if="error">{{ error }}</div> <div class="alert bg-gray-200 text-gray-900" v-if="isBusy"> Loading... </div> <router-view :key="$route.fullPath"></router-view> </div> </template>
如果沒有有效的方法來處理這種狀態(tài),它可能會(huì)建議使用發(fā)布/訂閱系統(tǒng),但實(shí)際上在許多情況下共享數(shù)據(jù)更為直接。如果你想使用共享狀態(tài),你該怎么做呢?讓我們看看一些常見的方法。
注意:你將在 GitHub 上示例項(xiàng)目的“main”分支中找到此部分的代碼。
自從遷移到 Vue 3 后,我已經(jīng)完全遷移到使用 Composition API,這篇文章我還使用了TypeScript
盡管在我展示的示例中這不是必需的。雖然你可以以任何方式共享狀態(tài),但我將向你展示我發(fā)現(xiàn)的幾種最常用的技術(shù)模式,每一種都有自己的優(yōu)點(diǎn)和缺點(diǎn),所以不要把我在這里所說的任何東西當(dāng)作唯一。
技術(shù)包括:
工廠模式,
共享單例,
Vuex 4,
Vuex 5.
注意: 在撰寫本文時(shí),Vuex 5還處于RFC(征求意見)階段,所以我想讓你為Vuex5的到來做好準(zhǔn)備,但目前還沒有該選項(xiàng)的工作版本。
讓我們深入了解……
注意: 此部分的代碼位于GitHub 上示例項(xiàng)目的“Factories”分支中。
工廠模式只關(guān)心你所創(chuàng)建的狀態(tài)的實(shí)例。在此模式中,你將返回一個(gè)與Composition API中的start函數(shù)非常相似的函數(shù)。你將創(chuàng)建一個(gè)作用域并構(gòu)建你想尋找的組件。舉個(gè)例子:
export default function () { const books: Work[] = reactive([]); async function loadBooks(val: string) { const response = await bookService.getBooks(val, currentPage.value); if (response.status === 200) { books.splice(0, books.length, ...response.data.works); } } return { loadBooks, books }; }
你可以要求工廠創(chuàng)建你所需要的那部分對(duì)象:
// In Home.vue const { books, loadBooks } = BookFactory();
如果我們添加一個(gè)isBusy
標(biāo)記來顯示網(wǎng)絡(luò)請(qǐng)求何時(shí)發(fā)生,上面的代碼不會(huì)改變,但你可以決定在哪里顯示isBusy
:
export default function () { const books: Work[] = reactive([]); const isBusy = ref(false); async function loadBooks(val: string) { isBusy.value = true; const response = await bookService.getBooks(val, currentPage.value); if (response.status === 200) { books.splice(0, books.length, ...response.data.works); } } return { loadBooks, books, isBusy }; }
在另一個(gè)視圖(vue?)中,你可以只要isBusy
標(biāo)記,而不需要知道工廠其他部分是如何工作的:
// App.vue export default defineComponent({ setup() { const { isBusy } = BookFactory(); return { isBusy } }, })
但是你可能注意到了一個(gè)問題;每次我們調(diào)用工廠時(shí),我們都會(huì)獲得所有對(duì)象的新實(shí)例。有時(shí),你希望工廠返回新實(shí)例,但在我們的例子中,我們討論的是共享狀態(tài),因此我們需要將創(chuàng)建移到工廠之外:
const books: Work[] = reactive([]); const isBusy = ref(false); async function loadBooks(val: string) { isBusy.value = true; const response = await bookService.getBooks(val, currentPage.value); if (response.status === 200) { books.splice(0, books.length, ...response.data.works); } } export default function () { return { loadBooks, books, isBusy }; }
現(xiàn)在,工廠給了我們一個(gè)共享實(shí)例,或者一個(gè)單例。雖然這種模式可以工作,但返回一個(gè)每次都不創(chuàng)建新實(shí)例的函數(shù)可能會(huì)令人困惑。
因?yàn)榈讓訉?duì)象被標(biāo)記為const
,所以不能替換它們(也不能破壞單例的性質(zhì))。所以這段代碼應(yīng)該會(huì)報(bào)錯(cuò):
// In Home.vue const { books, loadBooks } = BookFactory(); books = []; // Error, books is defined as const
因此,確??勺儬顟B(tài)能夠被更新是很重要的(例如,使用books.splice()
而不是設(shè)置books)。另一種處理方法是使用共享實(shí)例
本節(jié)的代碼在GitHub上示例項(xiàng)目的“Sharedstate”分支中。
如果你使用共享狀態(tài),最好清楚狀態(tài)是一個(gè)單例的事實(shí)。在本例中,它可以作為一個(gè)靜態(tài)對(duì)象導(dǎo)入。例如,我喜歡創(chuàng)建一個(gè)可以作為響應(yīng)式對(duì)象導(dǎo)入的對(duì)象:
export default reactive({ books: new Array<Work>(), isBusy: false, async loadBooks() { this.isBusy = true; const response = await bookService.getBooks(this.currentTopic, this.currentPage); if (response.status === 200) { this.books.splice(0, this.books.length, ...response.data.works); } this.isBusy = false; } });
在這種情況下,你只需導(dǎo)入對(duì)象(在本例中我將其稱為商店):
// Home.vue import state from "@/state"; export default defineComponent({ setup() { // ... onMounted(async () => { if (state.books.length === 0) state.loadBooks(); }); return { state, bookTopics, }; }, });
然后很容易綁定到狀態(tài):
<!-- Home.vue --> <div class="grid grid-cols-4"> <div v-for="book in state.books" :key="book.key" class="border bg-white border-grey-500 m-1 p-1" > <router-link :to="{ name: 'book', params: { id: book.key } }"> <BookInfo :book="book" /> </router-link> </div>
與其他模式一樣,你可以在視圖之間共享此實(shí)例:
// App.vue import state from "@/state"; export default defineComponent({ setup() { return { state }; }, })
然后它可以綁定到同一個(gè)對(duì)象(無論它是Home.Vue
的父頁面)或路由到其他頁面):
<!-- App.vue --> <div class="container mx-auto bg-gray-100 p-1"> <router-link to="/"><h2>Bookcase</h2></router-link> <div class="alert bg-gray-200 text-gray-900" v-if="state.isBusy">Loading...</div> <router-view :key="$route.fullPath"></router-view> </div>
無論使用工廠模式還是共享實(shí)例,它們都有一個(gè)共同的問題:可變狀態(tài)。當(dāng)你不希望綁定或代碼更改狀態(tài)時(shí),可能會(huì)產(chǎn)生意外的副作用。在本文中我使用簡(jiǎn)單示例中,它還沒有那么復(fù)雜,因此不用擔(dān)心。但是當(dāng)你構(gòu)建的應(yīng)用程序越大越大時(shí),將需要更仔細(xì)地考慮狀態(tài)的變化。這時(shí)候Vuex就可以伸出援手了。
本節(jié)的代碼位于GitHub上示例項(xiàng)目的“Vuex4”分支中。
Vuex是vue的狀態(tài)管理器。它是由核心團(tuán)隊(duì)構(gòu)建的,但它作為一個(gè)單獨(dú)的項(xiàng)目進(jìn)行管理。Vuex的目的是將狀態(tài)與狀態(tài)執(zhí)行操作分離開來。所有狀態(tài)更改都必須通過 Vuex,這意味著它更復(fù)雜,但你可以防止意外狀態(tài)更改。
Vuex 的想法是提供可預(yù)測(cè)的狀態(tài)管理流程。視圖流向 Actions,Actions 反過來使用 Mutations 來改變狀態(tài),從而更新視圖。通過限制狀態(tài)更改的流程,可以減少改變應(yīng)用程序狀態(tài)的副作用;因此更容易構(gòu)建更大的應(yīng)用程序。Vuex 有一個(gè)學(xué)習(xí)曲線,但有了這種復(fù)雜性,你就可以獲得可預(yù)測(cè)性。
此外,Vuex 還支持程序調(diào)試(通過 Vue Tools)來處理狀態(tài)管理,包括一個(gè)稱為time-travel
的功能。這允許你查看狀態(tài)的歷史記錄并前后移動(dòng)以查看它如何影響應(yīng)用程序的。
有時(shí),Vuex 也很重要。
要將其添加到你的 Vue 3 項(xiàng)目中,你可以將包添加到項(xiàng)目中:
> npm i vuex
或者,你也可以使用 Vue CLI 添加它:
> vue add vuex
通過使用CLI,它將為你的 Vuex Store 創(chuàng)建初始的起點(diǎn), 否則你需要手動(dòng)將其接入到項(xiàng)目。讓我們來看看這是如何工作的。
首先,你需要使用 Vuex 的 createStore 函數(shù)創(chuàng)建一個(gè)狀態(tài)對(duì)象:
import { createStore } from 'vuex' export default createStore({ state: {}, mutations: {}, actions: {}, getters: {} });
如你所見,Store需要定義多個(gè)屬性。State只是一個(gè)想要授予應(yīng)用程序訪問權(quán)限的數(shù)據(jù)列表:
import { createStore } from 'vuex' export default createStore({ state: { books: [], isBusy: false }, mutations: {}, actions: {} });
注意state不應(yīng)使用refor reactive包裝。這里的數(shù)據(jù)與我們?cè)诠蚕韺?shí)例或工廠中使用的共享數(shù)據(jù)類型相同。這個(gè)store在你的應(yīng)用程序中將作為一個(gè)單例,因此狀態(tài)中的數(shù)據(jù)也會(huì)被共享
然后,讓我們看看actions
。 Actions是可以授予你改變state中數(shù)據(jù)的一個(gè)操作臺(tái),舉個(gè)例子:
actions: { async loadBooks(store) { const response = await bookService.getBooks(store.state.currentTopic, if (response.status === 200) { // ... } } },
Actions 會(huì)傳遞一個(gè) store 的實(shí)例,以便你可以獲取狀態(tài)和其他操作。通常,我們只會(huì)解構(gòu)我們需要的部分:
actions: { async loadBooks({ state }) { const response = await bookService.getBooks(state.currentTopic, if (response.status === 200) { // ... } } },
最后一個(gè)是Mutations
。Mutations是能夠改變狀態(tài)的函數(shù)。只有Mutations才能影響狀態(tài)。在這個(gè)例子中,我們需要Mutations來改變狀態(tài):
mutations: { setBusy: (state) => state.isBusy = true, clearBusy: (state) => state.isBusy = false, setBooks(state, books) { state.books.splice(0, state.books.length, ...books); } },
Mutation函數(shù)總是傳入狀態(tài)對(duì)象,以便你可以改變狀態(tài)。在前兩個(gè)示例中,你可以看到我們顯式地設(shè)置了狀態(tài)。但在第三個(gè)例子中,我們傳入了set的狀態(tài)。調(diào)用mutation時(shí),mutation總是接收兩個(gè)參數(shù):state和實(shí)參。
要調(diào)用mutation, 你需要在store中使用commit函數(shù)。在我們的例子中,我只是將它添加到解構(gòu)中:
actions: { async loadBooks({ state, commit }) { commit("setBusy"); const response = await bookService.getBooks(state.currentTopic, if (response.status === 200) { commit("setBooks", response.data); } commit("clearBusy"); } },
你將在此處看到commit如何要求action的命名。有一些技巧使它不僅僅是有魔力的字符串。但是我現(xiàn)在跳過它們。這種有魔力字符串是限制Vuex的使用方式之一。
雖然使用 commit 可能看起來像是一個(gè)不必要的包裝器,但請(qǐng)記住,Vuex 不會(huì)讓你改變狀態(tài),除非在mutation內(nèi)部,因此只有通過commit調(diào)用才會(huì)。
你還可以看到對(duì)setBooks的調(diào)用采用了第二個(gè)參數(shù)。這是調(diào)用mutation的第二個(gè)參數(shù)。如果你需要更多信息,則需要將其打包成一個(gè)參數(shù)(目前 Vuex 的另一個(gè)限制)。假設(shè)你需要將一本書插入到圖書列表中,你可以這樣稱呼它:
commit("insertBook", { book, place: 4 }); // object, tuple, etc.
然后你可以解構(gòu)成你需要的部分:
mutations: { insertBook(state, { book, place }) => // ... }
這優(yōu)雅嗎?并不是,但它有效。
現(xiàn)在我們的 action 已經(jīng)處理了mutations,我們需要能夠在我們的代碼中使用 Vuex 存儲(chǔ)。有兩種方法可以得到store。首先,通過使用應(yīng)用程序(例如 main.ts/js)注冊(cè)store,你將可以訪問一個(gè)集中式store, 你可以在應(yīng)用程序的任何地方訪問它:
// main.ts import store from './store' createApp(App) .use(store) .use(router) .mount('#app')
注意,這并不是在添加Vuex,而是你實(shí)際上正在創(chuàng)建的商店。一旦添加后, 你只需要調(diào)用useStore
即可獲取store對(duì)象:
import { useStore } from "vuex"; export default defineComponent({ components: { BookInfo, }, setup() { const store = useStore(); const books = computed(() => store.state.books); // ...
這很好用,但我更喜歡直接導(dǎo)入store:
import store from "@/store"; export default defineComponent({ components: { BookInfo, }, setup() { const books = computed(() => store.state.books); // ...
既然你可以訪問 store 對(duì)象,那么如何使用它呢?對(duì)于state,你需要使用計(jì)算函數(shù)包裝它們,以便將它們更改并傳遞到你的綁定上:
export default defineComponent({ setup() { const books = computed(() => store.state.books); return { books }; }, });
要調(diào)用actions,你需要調(diào)用dispatch方法:
export default defineComponent({ setup() { const books = computed(() => store.state.books); onMounted(async () => await store.dispatch("loadBooks")); return { books }; }, });
Actions可以在方法名稱后添加的參數(shù)。最后,要更改狀態(tài),你需要像我們?cè)?Actions 中所做的那樣去調(diào)用 commit。例如,我在 store 中有一個(gè) 分頁屬性,然后我可以使用commit更改狀態(tài):
const incrementPage = () => store.commit("setPage", store.state.currentPage + 1); const decrementPage = () => store.commit("setPage", store.state.currentPage - 1);
注意, 這樣調(diào)用可能會(huì)拋出一個(gè)異常(因?yàn)槟悴荒苁謩?dòng)改變state):
const incrementPage = () => store.state.currentPage++; const decrementPage = () => store.state.currentPage--;
這是Vuex真正強(qiáng)大的地方,我們想要控制狀態(tài)改變的地方并且不會(huì)造成在開發(fā)過程中產(chǎn)生進(jìn)一步錯(cuò)誤。
你可能對(duì) Vuex 中大量改變狀態(tài)的方式感到不知所措,但它確實(shí)可以幫助管理更大、更復(fù)雜的項(xiàng)目中的狀態(tài)。我不會(huì)說你在每種情況下都需要它,但是會(huì)有一些大型項(xiàng)目在總體上對(duì)你有幫助。
這就是 Vuex 5 旨在簡(jiǎn)化 Vuex 在 TypeScript(以及一般的 JavaScript 項(xiàng)目)中的工作方式的地方。讓我們看看它在下一次發(fā)布后將如何運(yùn)作。
注意: 此部分的代碼位于GitHub 上示例項(xiàng)目的“Vuex5”分支中。
在撰寫本文時(shí),Vuex 5 還沒有出現(xiàn)。這是一個(gè) RFC(征求意見)。這是計(jì)劃。是討論的起點(diǎn)。所以我在這里說明的很多東西可能會(huì)發(fā)生變化。但是為了讓你們對(duì) Vuex 的變化有所準(zhǔn)備,我想讓你們了解一下它的發(fā)展方向。因此,與此示例關(guān)聯(lián)的代碼不會(huì)生成。
Vuex工作原理的基本概念從一開始就沒有改變過。隨著Vue 3的引入,Vuex 4的創(chuàng)建主要允許Vuex在新項(xiàng)目中工作。但該團(tuán)隊(duì)正試圖找到Vuex的真正痛點(diǎn)并解決它們。為此,他們正在計(jì)劃一些重要的改變:
沒有更多的mutations: actions能改變mutation (可能任意的也可以)。
更好的TypeScript支持。
更好的多store功能。
那么它是如何工作的呢?讓我們從創(chuàng)建store開始:
export default createStore({ key: 'bookStore', state: () => ({ isBusy: false, books: new Array<Work>() }), actions: { async loadBooks() { try { this.isBusy = true; const response = await bookService.getBooks(); if (response.status === 200) { this.books = response.data.works; } } finally { this.isBusy = false; } } }, getters: { findBook(key: string): Work | undefined { return this.books.find(b => b.key === key); } } });
首先要看到的是,每個(gè)store現(xiàn)在都需要有自己的key。這允許你檢索多個(gè)store。接下來,你會(huì)注意到state對(duì)象現(xiàn)在是一個(gè)工廠(例如,從一個(gè)函數(shù)返回,不是在解析時(shí)創(chuàng)建的)。沒有mutations部分了。最后,在actions內(nèi)部,你可以看到我們?cè)L問的狀態(tài)只是this
指向的屬性。不需要再通過傳遞state來commit Actions。這不僅有助于簡(jiǎn)化開發(fā),還使TypeScript更容易推斷類型。
要將 Vuex 注冊(cè)到你的應(yīng)用程序中,你將注冊(cè) Vuex 而不是你的全局store:
import { createVuex } from 'vuex' createApp(App) .use(createVuex()) .use(router) .mount('#app')
最后,要使用store,你將導(dǎo)入store然后創(chuàng)建它的一個(gè)實(shí)例:
import bookStore from "@/store"; export default defineComponent({ components: { BookInfo, }, setup() { const store = bookStore(); // Generate the wrapper // ...
請(qǐng)注意,從商店返回的是一個(gè)工廠對(duì)象,無論你調(diào)用工廠多少次,它都會(huì)返回該商店的實(shí)例。返回只是一個(gè)具有單一類行為的actions、state和getters對(duì)象(帶有類型信息):
onMounted(async () => await store.loadBooks()); const incrementPage = () => store.currentPage++; const decrementPage = () => store.currentPage--;
你將在此處看到 state(例如currentPage
)只是簡(jiǎn)單的屬性。而actions(例如loadBooks
)只是函數(shù)。實(shí)際上你在這里使用的store是一個(gè)副作用。你可以將 Vuex 對(duì)象視為一個(gè)對(duì)象并繼續(xù)工作。這是 API 的重大改進(jìn)。
另一個(gè)需要指出的重要變化是,你也可以使用類似于Composition API的語法來生成你的store:
export default defineStore("another", () => { // State const isBusy = ref(false); const books = reactive(new Array?Work>()); // Actions async function loadBooks() { try { this.isBusy = true; const response = await bookService.getBooks(this.currentTopic, this.currentPage); if (response.status === 200) { this.books = response.data.works; } } finally { this.isBusy = false; } } findBook(key: string): Work | undefined { return this.books.find(b => b.key === key); } // Getters const bookCount = computed(() => this.books.length); return { isBusy, books, loadBooks, findBook, bookCount } });
這允許你像使用 Composition API 構(gòu)建視圖那樣去構(gòu)建 Vuex 對(duì)象,并且可以說它更簡(jiǎn)單。
這種新設(shè)計(jì)的一個(gè)主要缺點(diǎn)是失去了狀態(tài)的不可變性。圍繞能夠啟用此功能(僅用于開發(fā),就像 Vuex 4 一樣)進(jìn)行了討論,但對(duì)此的重要性尚未達(dá)成共識(shí)。我個(gè)人認(rèn)為這是 Vuex 的主要有點(diǎn),但我們必須看看它是如何發(fā)揮作用的。
關(guān)于在Vue 3中怎么管理共享狀態(tài)問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
本文標(biāo)題:在Vue3中怎么管理共享狀態(tài)
文章出自:http://jinyejixie.com/article48/pgigep.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)、App開發(fā)、網(wǎng)站制作、電子商務(wù)、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站維護(hù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)