EP48 - 狀態管理

更新 發佈閱讀 11 分鐘
State Management 管理的部分都蠻值得一看的
但前提是什麼狀態啊?哈
一知半解的技術郎 www

什麼是狀態管理?What is State Management?

技術上,每個 Vue 組件實例已經「管理」自己的響應式狀態。以一個簡單的計數器組件為例:

<script setup>
import { ref } from 'vue'

// 狀態
const count = ref(0)

// 行為
function increment() {
count.value++
}
</script>

<!-- 視圖 -->
<template>{{ count }}</template>

這是一個獨立的單元,包含以下部分:

  • 狀態:驅動我們應用程式的真實來源;
  • 視圖:狀態的聲明式映射;
  • 行為:狀態可能在響應於來自視圖的用戶輸入時發生變化的方式。

這是「單向數據流」概念的簡單表示:

vocus|新世代的創作平台

然而,當我們有多個組件共享相同的狀態時,這種簡單性開始崩潰:

  1. 多個視圖可能依賴於同一段狀態。
  2. 來自不同視圖的行為可能需要修改相同的狀態。

對於第一種情況,一個可能的解決方法是將共享狀態「提升」到共同的祖先組件,然後作為屬性傳遞下去。然而,在具有深層次層次結構的組件樹中,這很快就變得繁瑣,導致另一個問題,即「屬性傳遞」。

對於第二種情況,我們經常發現自己不得不求助於解決方案,例如通過模板引用訪問直接的父子實例,或者試圖通過發出的事件來修改和同步多個狀態副本。這兩種模式都很脆弱,並迅速導致不可維護的代碼。

一個更簡單且直接的解決方案是將共享狀態提取出組件,並在全局單例中進行管理。這樣,我們的組件樹變成一個大型「視圖」,無論它們在樹中的哪個位置,任何組件都可以訪問狀態或觸發行為!

簡單的狀態管理與反應性 API - Simple State Management with Reactivity API​

如果您有一個狀態需要由多個實例共享,可以使用 reactive() 創建一個反應性對象,然後將其導入多個組件:

// store.js
import { reactive } from 'vue'

export const store = reactive({
count: 0
})
<!-- ComponentA.vue -->
<script setup>
import { store } from './store.js'
</script>

<template>From A: {{ store.count }}</template>
<!-- ComponentB.vue -->
<script setup>
import { store } from './store.js'
</script>

<template>From B: {{ store.count }}</template>

現在,每當 store 對象被修改時,<ComponentA><ComponentB> 都會自動更新它們的視圖,我們現在擁有一個單一的真相來源。

然而,這也意味著任何導入 store 的組件都可以隨意修改它:

<template>
<button @click="store.count++">
From B: {{ store.count }}
</button>
</template>

雖然這在簡單情況下是有效的,但全局狀態可以被任何組件任意修改,從長遠來看並不是很可維護。為了確保狀態修改邏輯集中管理,就像狀態本身一樣,建議在 store 上定義方法,並使用表達操作意圖的名稱:

// store.js
import { reactive } from 'vue'

export const store = reactive({
count: 0,
increment() {
this.count++
}
})
<template>
<button @click="store.increment()">
From B: {{ store.count }}
</button>
</template>

Try it in the playground

提示:請注意,點擊處理器使用 store.increment() 並帶有括號——這是必要的,以便在正確的 this 上下文中調用該方法,因為它不是組件方法。

雖然這裡我們使用單個反應性對象作為商店,您還可以共享使用其他反應性 API(如 ref()computed())創建的反應性狀態,甚至從可組合函數返回全局狀態:

import { ref } from 'vue'

// global state, created in module scope
const globalCount = ref(1)

export function useCount() {
// local state, created per-component
const localCount = ref(1)

return {
globalCount,
localCount
}
}

Vue 的反應性系統與組件模型解耦的特性使其具有極大的靈活性。

SSR 考量 - SSR Considerations​

如果您正在構建一個利用伺服器端渲染(SSR)的應用程序,上述模式可能會導致問題,因為該商店是跨多個請求共享的單例。這在 SSR 指南中有更詳細的討論。

Pinia

雖然我們自己手動開發的狀態管理解決方案在簡單的情境下已經足夠,但在大型生產應用中還有許多需要考量的事項:

  • 更強的團隊協作慣例
  • 與 Vue DevTools 的整合,包括時間線、組件內檢查和時間旅行調試
  • 熱模組替換(Hot Module Replacement)
  • 支援伺服器端渲染

Pinia 是一個狀態管理庫,實現了上述所有功能。它由 Vue 核心團隊維護,並且可用於 Vue 2 和 Vue 3。

現有用戶可能熟悉 Vuex,這是之前 Vue 的官方狀態管理庫。隨著 Pinia 在生態系統中扮演相同角色,Vuex 現在處於維護模式。它仍然可用,但將不再接收新功能。建議新應用使用 Pinia。

Pinia 最初是探索 Vuex 下一個版本可能的樣子,並融合了核心團隊對 Vuex 5 討論中的許多想法。最終,我們意識到 Pinia 已經實現了我們在 Vuex 5 中想要的大多數功能,因此決定將其作為新的推薦方案。

與 Vuex 相比,Pinia 提供了更簡單的 API,儀式性更少,提供組合 API 樣式的 API,最重要的是,當與 TypeScript 一起使用時,具有穩固的類型推斷支援。

看來還是有得學! 好多庫要學~
雖然不用自己從頭刻,但學習使用也是很累人的www
Pinia來看個範例吧~

Pinia有範例嗎?

以下是一個使用 Pinia 進行狀態管理的簡單範例,這個範例展示了如何創建一個 Pinia 存儲並在組件中使用它。

1. 安裝 Pinia

首先,你需要安裝 Pinia。在你的 Vue 項目中運行以下命令:

npm install pinia

2. 創建 Pinia 存儲

在你的 src 目錄下創建一個 stores 文件夾,並在裡面創建一個 counterStore.js 文件。

// src/stores/counterStore.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++
},
decrement() {
this.count--
}
}
})

3. 在應用中使用 Pinia

接下來,在你的 main.js 中引入並安裝 Pinia。

// src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)

const pinia = createPinia()
app.use(pinia)

app.mount('#app')

4. 使用存儲的組件範例

然後,你可以在任何組件中使用這個存儲。例如,創建一個 Counter.vue 組件:

<!-- src/components/Counter.vue -->
<template>
<div>
<h1>Count: {{ counter.count }}</h1>
<button @click="counter.increment">Increment</button>
<button @click="counter.decrement">Decrement</button>
</div>
</template>

<script setup>
import { useCounterStore } from '../stores/counterStore'

const counter = useCounterStore()
</script>

5. 在 App.vue 中引入 Counter

最後,將 Counter 組件引入到你的 App.vue 中並顯示它:

<!-- src/App.vue -->
<template>
<div>
<h1>My Counter App</h1>
<Counter />
</div>
</template>

<script setup>
import Counter from './components/Counter.vue'
</script>

完成

現在你已經設置了一個簡單的 Vue 應用,使用 Pinia 作為狀態管理。這個應用包含一個計數器,你可以增加和減少計數。這樣的結構能夠讓你輕鬆地管理全局狀態。

留言
avatar-img
卡關的人生
4會員
73內容數
分享生活趣事~
卡關的人生的其他內容
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
看更多
你可能也想看
Thumbnail
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
若說易卜生的《玩偶之家》為 19 世紀的女性,開啟了一扇離家的窄門,那麼《海妲.蓋柏樂》展現的便是門後的窒息世界。本篇文章由劇場演員 Amily 執筆,同為熟稔文本的演員,亦是深刻體察制度縫隙的當代女性,此文所看見的不僅僅是崩壞前夕的最後發聲,更是女人被迫置於冷酷的制度之下,步步陷入無以言說的困境。
Thumbnail
若說易卜生的《玩偶之家》為 19 世紀的女性,開啟了一扇離家的窄門,那麼《海妲.蓋柏樂》展現的便是門後的窒息世界。本篇文章由劇場演員 Amily 執筆,同為熟稔文本的演員,亦是深刻體察制度縫隙的當代女性,此文所看見的不僅僅是崩壞前夕的最後發聲,更是女人被迫置於冷酷的制度之下,步步陷入無以言說的困境。
Thumbnail
一開始你先把你的專案push上去後,修改vite.config.ts ,要在裡面新增  base: "/Cart/" (/放自己的專案名稱/) build: {outDir: "docs"}, 接下來你要去你的github setting 裡面 -> Page ->選Deploy fro
Thumbnail
一開始你先把你的專案push上去後,修改vite.config.ts ,要在裡面新增  base: "/Cart/" (/放自己的專案名稱/) build: {outDir: "docs"}, 接下來你要去你的github setting 裡面 -> Page ->選Deploy fro
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
本文深度解析賽勒布倫尼科夫的舞臺作品《傳奇:帕拉贊諾夫的十段殘篇》,如何以十段殘篇,結合帕拉贊諾夫的電影美學、象徵意象與當代政治流亡抗爭,探討藝術在儀式消失的現代社會如何承接意義,並展現不羈的自由靈魂。
Thumbnail
本文深度解析賽勒布倫尼科夫的舞臺作品《傳奇:帕拉贊諾夫的十段殘篇》,如何以十段殘篇,結合帕拉贊諾夫的電影美學、象徵意象與當代政治流亡抗爭,探討藝術在儀式消失的現代社會如何承接意義,並展現不羈的自由靈魂。
Thumbnail
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
Thumbnail
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
Thumbnail
全新版本的《三便士歌劇》如何不落入「復刻經典」的巢臼,反而利用華麗的秀場視覺,引導觀眾在晚期資本主義的消費愉悅之中,而能驚覺「批判」本身亦可能被收編——而當絞繩升起,這場關於如何生存的黑色遊戲,又將帶領新時代的我們走向何種後現代的自我解構?
Thumbnail
全新版本的《三便士歌劇》如何不落入「復刻經典」的巢臼,反而利用華麗的秀場視覺,引導觀眾在晚期資本主義的消費愉悅之中,而能驚覺「批判」本身亦可能被收編——而當絞繩升起,這場關於如何生存的黑色遊戲,又將帶領新時代的我們走向何種後現代的自我解構?
Thumbnail
在過去兩年中,我持續運用 Notion 進行個人管理,個人管理的模板也逐漸定型,藉此分享個人管理模板的使用心得。
Thumbnail
在過去兩年中,我持續運用 Notion 進行個人管理,個人管理的模板也逐漸定型,藉此分享個人管理模板的使用心得。
Thumbnail
本文介紹了在網站開發中如何運用狀態機的原則和設計方法。通過具體案例分析,以及狀態和數據的區分,詳細介紹了狀態機的設計原則和應用。讀者可以通過本文瞭解如何將狀態機應用於實際的網站開發中。
Thumbnail
本文介紹了在網站開發中如何運用狀態機的原則和設計方法。通過具體案例分析,以及狀態和數據的區分,詳細介紹了狀態機的設計原則和應用。讀者可以通過本文瞭解如何將狀態機應用於實際的網站開發中。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
長期以來,西方美學以《維特魯威人》式的幾何比例定義「完美身體」,這種視覺標準無形中成為殖民擴張與種族分類的暴力工具。本文透過分析奈及利亞編舞家庫德斯.奧尼奎庫的舞作《轉轉生》,探討當代非洲舞蹈如何跳脫「標本式」的文化觀看。
Thumbnail
長期以來,西方美學以《維特魯威人》式的幾何比例定義「完美身體」,這種視覺標準無形中成為殖民擴張與種族分類的暴力工具。本文透過分析奈及利亞編舞家庫德斯.奧尼奎庫的舞作《轉轉生》,探討當代非洲舞蹈如何跳脫「標本式」的文化觀看。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News