在深入研究核心概念之前,我們得知道 Store 是用 defineStore()
定義的,它的第一個參數(shù)要求是一個獨一無二的名字:
import { defineStore } from 'pinia'
// 你可以對 `defineStore()` 的返回值進行任意命名,但最好使用 store 的名字,同時以 `use` 開頭且以 `Store` 結(jié)尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一個參數(shù)是你的應用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {
// 其他配置...
})
這個名字 ,也被用作 id ,是必須傳入的, Pinia 將用它來連接 store 和 devtools。為了養(yǎng)成習慣性的用法,將返回的函數(shù)命名為 use... 是一個符合組合式函數(shù)風格的約定。
defineStore()
的第二個參數(shù)可接受兩類值:Setup 函數(shù)或 Option 對象。
與 Vue 的選項式 API 類似,我們也可以傳入一個帶有 state
、actions
與 getters
屬性的 Option 對象
```js {2-10} export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), getters: { double: (state) => state.count * 2, }, actions: { increment() { this.count++ }, }, })
你可以認為 `state` 是 store 的數(shù)據(jù) (`data`),`getters` 是 store 的計算屬性 (`computed`),而 `actions` 則是方法 (`methods`)。
為方便上手使用,Option Store 應盡可能直觀簡單。
## Setup Store
也存在另一種定義 store 的可用語法。與 Vue 組合式 API 的 [setup 函數(shù)](https://cn.vuejs.org/api/composition-api-setup.html) 相似,我們可以傳入一個函數(shù),該函數(shù)定義了一些響應式屬性和方法,并且返回一個帶有我們想暴露出去的屬性和方法的對象。
```js
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
在 Setup Store 中:
ref()
就是 state
屬性computed()
就是 getters
function()
就是 actions
Setup store 比 Option Store 帶來了更多的靈活性,因為你可以在一個 store 內(nèi)創(chuàng)建偵聽器,并自由地使用任何組合式函數(shù)。不過,請記住,使用組合式函數(shù)會讓 SSR 變得更加復雜。
和在 Vue 中如何選擇組合式 API 與選項式 API 一樣,選擇你覺得最舒服的那一個就好。如果你還不確定,可以先試試 Option Store。
雖然我們前面定義了一個 store,但在我們使用 <script setup>
調(diào)用 useStore()
(或者使用 setup()
函數(shù),像所有的組件那樣) 之前,store 實例是不會被創(chuàng)建的:
<script setup>
import { useCounterStore } from '@/stores/counter'
// 可以在組件中的任意位置訪問 `store` 變量 ?
const store = useCounterStore()
</script>
你可以定義任意多的 store,但為了讓使用 pinia 的益處最大化(比如允許構(gòu)建工具自動進行代碼分割以及 TypeScript 推斷),你應該在不同的文件中去定義 store。
如果你還不會使用 setup
組件,你也可以通過映射輔助函數(shù)來使用 Pinia。
一旦 store 被實例化,你可以直接訪問在 store 的 state
、getters
和 actions
中定義的任何屬性。我們將在后續(xù)章節(jié)繼續(xù)了解這些細節(jié),目前自動補全將幫助你使用相關屬性。
請注意,store
是一個用 reactive
包裝的對象,這意味著不需要在 getters 后面寫 .value
,就像 setup
中的 props
一樣,如果你寫了,我們也不能解構(gòu)它:
<script setup>
const store = useCounterStore()
// ? 這將不起作用,因為它破壞了響應性
// 這就和直接解構(gòu) `props` 一樣
const { name, doubleCount } = store // [!code warning]
name // 將始終是 "Eduardo" // [!code warning]
doubleCount // 將始終是 0 // [!code warning]
setTimeout(() => {
store.increment()
}, 1000)
// ? 這樣寫是響應式的
// ???? 當然你也可以直接使用 `store.doubleCount`
const doubleValue = computed(() => store.doubleCount)
</script>
為了從 store 中提取屬性時保持其響應性,你需要使用 storeToRefs()
。它將為每一個響應式屬性創(chuàng)建引用。當你只使用 store 的狀態(tài)而不調(diào)用任何 action 時,它會非常有用。請注意,你可以直接從 store 中解構(gòu) action,因為它們也被綁定到 store 上:
<script setup>
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// `name` 和 `doubleCount` 是響應式的 ref
// 同時通過插件添加的屬性也會被提取為 ref
// 并且會跳過所有的 action 或非響應式 (不是 ref 或 reactive) 的屬性
const { name, doubleCount } = storeToRefs(store)
// 作為 action 的 increment 可以直接解構(gòu)
const { increment } = store
</script>
更多建議: