What is Pinia作郭?
官網(wǎng)的介紹是:
Pinia最初是在 2019 年 11 月左右重新設(shè)計使用Composition API的 Vue Store 的實驗陨囊。從那時起,最初的原則仍然相同夹攒,但 Pinia 適用于 Vue 2 和 Vue 3 蜘醋,并且不需要你使用組合 API。除了安裝和SSR之外咏尝,兩者的 API 都是相同的压语,并且這些文檔針對 Vue 3 ,并在必要時提供有關(guān) Vue 2 的注釋编检,以便 Vue 2 和 Vue 3 用戶可以閱讀胎食!
我的理解是:
Pinia 是 Vue.js 的輕量級狀態(tài)管理庫,最近很受歡迎允懂。它使用 Vue 3 中的新反應(yīng)系統(tǒng)來構(gòu)建一個直觀且完全類型化的狀態(tài)管理庫厕怜。
Pinia的成功可以歸功于其管理存儲數(shù)據(jù)的獨特功能(可擴展性、存儲模塊組織蕾总、狀態(tài)變化分組粥航、多存儲創(chuàng)建等)。
另一方面生百,Vuex也是為Vue框架建立的一個流行的狀態(tài)管理庫递雀,它也是Vue核心團隊推薦的狀態(tài)管理庫。 Vuex高度關(guān)注應(yīng)用程序的可擴展性蚀浆、開發(fā)人員的工效和信心缀程。它基于與Redux相同的流量架構(gòu)搜吧。
自3月份掘金夜談No.1猶大重比提到了Pinia,我關(guān)注到了這個庫杠输。
尤大明確表示了:“不會有vuex5赎败,或者說pinia就是vuex5。pinia是vuex的維護者在決定vuex5風(fēng)格的時候做的一個庫蠢甲,效果比預(yù)期的好,同時為了尊重作者据忘,pinia也不會更名為vuex5”
Pinia 是 Vue 的存儲庫鹦牛,它允許您跨組件/頁面共享狀態(tài)。如果您熟悉 Composition API勇吊,您可能會認(rèn)為您已經(jīng)可以使用簡單的export const state = reactive({})
. 這對于單頁應(yīng)用程序來說是正確的曼追,但如果它是服務(wù)器端呈現(xiàn)的,則會將您的應(yīng)用程序暴露給安全漏洞汉规。 但即使在小型單頁應(yīng)用程序中礼殊,您也可以從使用 Pinia 中獲得很多好處:
-
開發(fā)工具支持
- A timeline to track actions, mutations
- Stores appear in components where they are used
- Time travel and easier debugging
-
熱模塊更換
- 在不重新加載頁面的情況下修改您的Store
- 在開發(fā)時保持任何現(xiàn)有狀態(tài)
插件:使用插件擴展 Pinia 功能
為 JS 用戶提供適當(dāng)?shù)?TypeScript 支持或自動完成功能
服務(wù)器端渲染支持
基本示例
這就是使用 pinia 在 API 方面的樣子(請務(wù)必查看官網(wǎng)中入門以獲取完整說明)。您首先創(chuàng)建一個Store:
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 }
},
// could also be defined as
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++
},
},
})
然后在組件中使用它:
import { useCounterStore } from '@/stores/counter'
export default {
setup() {
const counter = useCounterStore()
counter.count++
// with autocompletion ?
counter.$patch({ count: counter.count + 1 })
// or using an action instead
counter.increment()
},
}
你甚至可以使用一個函數(shù)(類似于一個組件setup()
)來為更高級的用例定義一個 Store:
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
如果您仍然不熟悉setup()
Composition API针史,請不要擔(dān)心晶伦,Pinia 還支持一組類似的map helpers,例如 Vuex啄枕。您以相同的方式定義存儲婚陪,但隨后使用mapStores()
、mapState()
或mapActions()
:
const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
}
}
})
const useUserStore = defineStore('user', {
// ...
})
export default {
computed: {
// other computed properties
// ...
// gives access to this.counterStore and this.userStore
...mapStores(useCounterStore, useUserStore)
// gives read access to this.count and this.double
...mapState(useCounterStore, ['count', 'double']),
},
methods: {
// gives access to this.increment()
...mapActions(useCounterStore, ['increment']),
},
}
您將在核心概念中找到有關(guān)每個地圖助手的更多信息频祝。
為什么選擇Pinia#
Pinia(發(fā)音為/pi?nj?/
泌参,如英語中的“peenya”)是最接近pi?a(西班牙語中的菠蘿)的詞,它是一個有效的包名稱常空。菠蘿實際上是一組單獨的花朵沽一,它們結(jié)合在一起形成多個水果。與Store類似漓糙,每一家都是獨立誕生的铣缠,但最終都是相互聯(lián)系的。它也是一種美味的熱帶水果兼蜈,原產(chǎn)于南美洲攘残。
一個更現(xiàn)實的例子#
這是一個更完整的 API 示例,您將在 Pinia中使用類型为狸,即使在 JavaScript 中也是如此歼郭。對于某些人來說,這可能足以在不進一步閱讀的情況下開始使用辐棒,但我們?nèi)匀唤ㄗh檢查文檔的其余部分病曾,甚至跳過此示例牍蜂,并在閱讀完所有核心概念后返回。
import { defineStore } from 'pinia'
export const todos = defineStore('todos', {
state: () => ({
/** @type {{ text: string, id: number, isFinished: boolean }[]} */
todos: [],
/** @type {'all' | 'finished' | 'unfinished'} */
filter: 'all',
// type will be automatically inferred to number
nextId: 0,
}),
getters: {
finishedTodos(state) {
// autocompletion! ?
return state.todos.filter((todo) => todo.isFinished)
},
unfinishedTodos(state) {
return state.todos.filter((todo) => !todo.isFinished)
},
/**
* @returns {{ text: string, id: number, isFinished: boolean }[]}
*/
filteredTodos(state) {
if (this.filter === 'finished') {
// call other getters with autocompletion ?
return this.finishedTodos
} else if (this.filter === 'unfinished') {
return this.unfinishedTodos
}
return this.todos
},
},
actions: {
// any amount of arguments, return a promise or not
addTodo(text) {
// you can directly mutate the state
this.todos.push({ text, id: this.nextId++, isFinished: false })
},
},
})
與 Vuex 的比較#
Pinia 最初是為了探索 Vuex 的下一次迭代會是什么樣子泰涂,結(jié)合了 Vuex 5 核心團隊討論中的許多想法鲫竞。最終,我們意識到 Pinia 已經(jīng)實現(xiàn)了我們在 Vuex 5 中想要的大部分內(nèi)容逼蒙,并決定實現(xiàn)它取而代之的是新的建議从绘。
與 Vuex 相比,Pinia 提供了一個更簡單的 API是牢,具有更少的儀式僵井,提供了 Composition-API 風(fēng)格的 API,最重要的是驳棱,在與 TypeScript 一起使用時具有可靠的類型推斷支持批什。
RFC#
最初 Pinia 沒有通過任何 RFC。我根據(jù)我開發(fā)應(yīng)用程序社搅、閱讀其他人的代碼驻债、為使用 Pinia 的客戶工作以及在 Discord 上回答問題的經(jīng)驗測試了想法。這使我能夠提供一種適用于各種情況和應(yīng)用程序大小的解決方案形葬。我過去經(jīng)常發(fā)布并在保持其核心 API 不變的同時使庫不斷發(fā)展合呐。
現(xiàn)在 Pinia 已經(jīng)成為默認(rèn)的狀態(tài)管理解決方案,它和 Vue 生態(tài)系統(tǒng)中的其他核心庫一樣遵循 RFC 流程荷并,其 API 也進入了穩(wěn)定狀態(tài)合砂。
與 Vuex 3.x/4.x 的比較#
Vuex 3.x 對應(yīng)的是 Vue 2 而 Vuex 4.x 是 Vue 3
Pinia API 與 Vuex ≤4 有很大不同,即:
- 突變不再存在源织。他們經(jīng)常被認(rèn)為是非常冗長的翩伪。他們最初帶來了 devtools 集成,但這不再是問題谈息。
- 無需創(chuàng)建自定義復(fù)雜包裝器來支持 TypeScript缘屹,所有內(nèi)容都是類型化的,并且 API 的設(shè)計方式盡可能利用 TS 類型推斷侠仇。
- 不再需要注入魔法字符串轻姿、導(dǎo)入函數(shù)、調(diào)用它們逻炊,享受自動完成功能互亮!
- 無需動態(tài)添加Store,默認(rèn)情況下它們都是動態(tài)的余素,您甚至都不會注意到豹休。請注意,您仍然可以隨時手動使用Store進行注冊桨吊,但因為它是自動的威根,您無需擔(dān)心凤巨。
- 不再有模塊的嵌套結(jié)構(gòu)。您仍然可以通過在另一個Store中導(dǎo)入和使用Store來隱式嵌套Store洛搀,但 Pinia 通過設(shè)計提供平面結(jié)構(gòu)敢茁,同時仍然支持Store之間的交叉組合方式。你甚至可以有 store 的循環(huán)依賴留美。
- 沒有命名空間的模塊彰檬。鑒于Store的扁平架構(gòu),“命名空間”Store是其定義方式所固有的谎砾,您可以說所有Store都是命名空間的僧叉。
有關(guān)如何將現(xiàn)有 Vuex ≤4 項目轉(zhuǎn)換為使用 Pinia 的更詳細(xì)說明,請參閱官網(wǎng)從 Vuex 遷移指南棺榔。
安裝#
pinia
使用您最喜歡的包管理器安裝:
yarn add pinia
# or with npm
npm install pinia
# or with pnpm
pnpm install pinia
提示
如果您的應(yīng)用使用 Vue 2,您還需要安裝組合 api: @vue/composition-api
隘道。如果您使用 Nuxt症歇,則應(yīng)遵循這些說明。
如果你使用的是 Vue CLI谭梗,你可以試試這個非官方的插件忘晤。
創(chuàng)建一個 pinia(根存儲)并將其傳遞給應(yīng)用程序:
import { createPinia } from 'pinia'
app.use(createPinia())
如果您使用的是 Vue 2,您還需要安裝一個插件并pinia
在應(yīng)用程序的根目錄注入創(chuàng)建的插件:
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
// other options...
// ...
// note the same `pinia` instance can be used across multiple Vue apps on
// the same page
pinia,
})
這也將添加 devtools 支持激捏。在 Vue 3 中设塔,仍然不支持時間旅行和編輯等一些功能,因為 vue-devtools 尚未公開必要的 API远舅,但 devtools 具有更多功能闰蛔,并且整體開發(fā)人員體驗要優(yōu)越得多。在 Vue 2 中图柏,Pinia 使用 Vuex 的現(xiàn)有接口(因此不能與它一起使用)序六。
什么是 Store?#
一個Store(如 Pinia)是一個實體蚤吹,它持有未綁定到您的組件樹的狀態(tài)和業(yè)務(wù)邏輯例诀。換句話說,它托管全局狀態(tài)裁着。它有點像一個始終存在并且每個人都可以讀取和寫入的組件繁涂。它包含三個概念,狀態(tài)二驰、getter和Action扔罪,并且可以安全地假設(shè)這些概念等同于data
,computed
和methods
在組件中。
我什么時候應(yīng)該使用Store#
存儲應(yīng)該包含可以在整個應(yīng)用程序中訪問的數(shù)據(jù)诸蚕。這包括在許多地方使用的數(shù)據(jù)步势,例如在導(dǎo)航欄中顯示的用戶信息氧猬,以及需要通過頁面保存的數(shù)據(jù),例如非常復(fù)雜的多步驟表單坏瘩。
另一方面盅抚,您應(yīng)該避免在存儲中包含可能托管在組件中的本地數(shù)據(jù),例如頁面本地元素的可見性倔矾。
并非所有應(yīng)用程序都需要訪問全局狀態(tài)妄均,但如果您需要一個,Pania 將使您的生活更輕松哪自。
定義Store
在深入研究核心概念之前丰包,我們需要知道存儲是使用定義的defineStore()
,并且它需要一個唯一的名稱壤巷,作為第一個參數(shù)傳遞:
import { defineStore } from 'pinia'
// useStore could be anything like useUser, useCart
// the first argument is a unique id of the store across your application
export const useStore = defineStore('main', {
// other options...
})
這個名稱邑彪,也稱為id,是必要的胧华,Pania 使用它來將Store連接到 devtools寄症。將返回的函數(shù)命名為use... 是可組合項之間的約定,以使其使用習(xí)慣矩动。
使用Store#
我們正在定義useStore()
一個Store有巧,因為Store在被調(diào)用之前不會被創(chuàng)建setup()
:
import { useStore } from '@/stores/counter'
export default {
setup() {
const store = useStore()
return {
// you can return the whole store instance to use it in the template
store,
}
},
}
您可以根據(jù)需要定義任意數(shù)量的Store,并且應(yīng)該在不同的文件中定義每個Store以充分利用 pinia(例如自動允許您的包進行代碼拆分和 TypeScript 推理)悲没。
如果您還沒有使用setup
組件篮迎,您仍然可以將 Pinia 與map helpers一起使用。
實例化Store后示姿,您可以直接在Store中訪問定義在state
甜橱、getters
和中的任何屬性。actions
我們將在接下來的頁面中詳細(xì)介紹這些內(nèi)容峻凫,但自動補全會對您有所幫助渗鬼。
請注意,這store
是一個用 包裹的對象reactive
荧琼,這意味著不需要.value
在 getter 之后編寫譬胎,但是像props
in一樣setup
,我們不能對其進行解構(gòu):
export default defineComponent({
setup() {
const store = useStore()
// ? This won't work because it breaks reactivity
// it's the same as destructuring from `props`
const { name, doubleCount } = store
name // "eduardo"
doubleCount // 2
return {
// will always be "eduardo"
name,
// will always be 2
doubleCount,
// this one will be reactive
doubleValue: computed(() => store.doubleCount),
}
},
})
為了從存儲中提取屬性同時保持其反應(yīng)性命锄,您需要使用storeToRefs()
. 它將為每個反應(yīng)屬性創(chuàng)建參考堰乔。當(dāng)您僅使用Store中的狀態(tài)但不調(diào)用任何操作時,這很有用脐恩。請注意镐侯,您可以直接從Store中解構(gòu)操作,因為它們也綁定到Store本身:
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const store = useStore()
// `name` and `doubleCount` are reactive refs
// This will also create refs for properties added by plugins
// but skip any action or non reactive (non ref/reactive) property
const { name, doubleCount } = storeToRefs(store)
// the increment action can be just extracted
const { increment } = store
return {
name,
doubleCount
increment,
}
},
})
狀態(tài) State
大多數(shù)時候,州是Store的中心部分苟翻。人們通常從定義代表他們的應(yīng)用程序的狀態(tài)開始韵卤。在 Pinia 中,狀態(tài)被定義為返回初始狀態(tài)的函數(shù)崇猫。這允許 Pinia 在服務(wù)器端和客戶端工作沈条。
import { defineStore } from 'pinia'
const useStore = defineStore('storeId', {
// arrow function recommended for full type inference
state: () => {
return {
// all these properties will have their type inferred automatically
counter: 0,
name: 'Eduardo',
isAdmin: true,
}
},
})
提示
如果您使用 Vue 2,您在其中創(chuàng)建的數(shù)據(jù)state
遵循與在 Vue 實例中相同的規(guī)則data
诅炉,即狀態(tài)對象必須是普通的蜡歹,并且您需要在向其添加新屬性Vue.set()
時調(diào)用。另請參閱: Vue#data涕烧。********
訪問state
#
默認(rèn)情況下月而,您可以通過store
實例訪問狀態(tài)來直接讀取和寫入狀態(tài):
const store = useStore()
store.counter++
重置狀態(tài)#
您可以通過調(diào)用store 上的方法將狀態(tài)重置為其初始值:$reset()
const store = useStore()
store.$reset()
使用選項 API
對于以下示例,您可以假設(shè)已創(chuàng)建以下Store:
// Example File Path:
// ./src/stores/counterStore.js
import { defineStore } from 'pinia',
const useCounterStore = defineStore('counterStore', {
state: () => ({
counter: 0
})
})
和setup()
#
雖然 Composition API 并不適合所有人议纯,但setup()
鉤子可以使在 Options API 中使用 Pinia 變得更容易父款。不需要額外的map helper功能!
import { useCounterStore } from '../stores/counterStore'
export default {
setup() {
const counterStore = useCounterStore()
return { counterStore }
},
computed: {
tripleCounter() {
return this.counterStore.counter * 3
},
},
}
沒有setup()
#
如果您沒有使用 Composition API瞻凤,而您正在使用computed
, methods
, ...铛漓,則可以使用mapState()
幫助器將State屬性映射為只讀計算屬性:
import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
computed: {
// gives access to this.counter inside the component
// same as reading from store.counter
...mapState(useCounterStore, ['counter'])
// same as above but registers it as this.myOwnName
...mapState(useCounterStore, {
myOwnName: 'counter',
// you can also write a function that gets access to the store
double: store => store.counter * 2,
// it can have access to `this` but it won't be typed correctly...
magicValue(store) {
return store.someGetter + this.counter + this.double
},
}),
},
}
可修改State#
如果您希望能夠?qū)懭脒@些#### State屬性(例如,如果您有一個表單)鲫构,您可以mapWritableState()
改用。請注意玫坛,您不能傳遞類似 with 的函數(shù)mapState()
:
import { mapWritableState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
computed: {
// gives access to this.counter inside the component and allows setting it
// this.counter++
// same as reading from store.counter
...mapWritableState(useCounterStore, ['counter'])
// same as above but registers it as this.myOwnName
...mapWritableState(useCounterStore, {
myOwnName: 'counter',
}),
},
}
提示
您不需要mapWritableState()
像數(shù)組這樣的集合结笨,除非您將整個數(shù)組替換為cartItems = []
,mapState()
仍然允許您調(diào)用集合上的方法。
改變狀態(tài)#
除了直接用 改變 store 之外store.counter++
湿镀,您還可以調(diào)用該$patch
方法炕吸。state
它允許您對部分對象同時應(yīng)用多個更改:
store.$patch({
counter: store.counter + 1,
name: 'Abalam',
})
但是,使用這種語法應(yīng)用某些突變確實很難或成本很高:任何集合修改(例如勉痴,從數(shù)組中推送赫模、刪除、拼接元素)都需要您創(chuàng)建一個新集合蒸矛。正因為如此瀑罗,該$patch
方法還接受一個函數(shù)來對這種難以用補丁對象應(yīng)用的突變進行分組:
cartStore.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})
這里的主要區(qū)別是$patch()
允許您將多個更改分組到 devtools 中的一個條目中。請注意 雏掠,直接更改state
并$patch()
出現(xiàn)在 devtools 中斩祭,并且可以穿越時間(在 Vue 3 中還沒有)。
更換state
#
$state
您可以通過將Store的屬性設(shè)置為新對象來替換Store的整個狀態(tài):
store.$state = { counter: 666, name: 'Paimon' }
您還可以通過更改實例state
的來替換應(yīng)用程序的整個狀態(tài)乡话。pinia
這在SSR 期間用于補水摧玫。
pinia.state.value = {}
訂閱狀態(tài)#
你可以通過 store 的方法觀察狀態(tài)及其變化$subscribe()
,類似于 Vuex 的subscribe 方法绑青。$subscribe()
與常規(guī)相比使用的優(yōu)點watch()
是訂閱只會在補丁后觸發(fā)一次(例如诬像,使用上面的函數(shù)版本時)屋群。
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// same as cartStore.$id
mutation.storeId // 'cart'
// only available with mutation.type === 'patch object'
mutation.payload // patch object passed to cartStore.$patch()
// persist the whole state to the local storage whenever it changes
localStorage.setItem('cart', JSON.stringify(state))
})
默認(rèn)情況下,狀態(tài)訂閱綁定到添加它們的組件(如果存儲在組件的內(nèi)部setup()
)坏挠。意思是芍躏,當(dāng)組件被卸載時,它們將被自動刪除癞揉。如果要在組件卸載后保留它們纸肉,請{ detached: true }
作為第二個參數(shù)傳遞以從當(dāng)前組件中分離 狀態(tài)訂閱:
export default {
setup() {
const someStore = useSomeStore()
// this subscription will be kept after the component is unmounted
someStore.$subscribe(callback, { detached: true })
// ...
},
}
提示
pinia
您可以查看實例上的整個狀態(tài):
watch(
pinia.state,
(state) => {
// persist the whole state to the local storage whenever it changes
localStorage.setItem('piniaState', JSON.stringify(state))
},
{ deep: true }
)
Getters
Getter 完全等同于 Store 狀態(tài)的計算值。它們可以用 中的getters
屬性定義defineStore()
喊熟。他們接收state
作為第一個參數(shù)來鼓勵使用箭頭函數(shù):
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
doubleCount: (state) => state.counter * 2,
},
})
大多數(shù)時候柏肪,getter 只會依賴狀態(tài),但是芥牌,他們可能需要使用其他 getter烦味。因此,我們可以在定義常規(guī)函數(shù)時訪問整個store 實例壁拉, 但需要定義返回類型的類型(在 TypeScript 中) 谬俄。這是由于 TypeScript 中的一個已知限制,不會影響使用箭頭函數(shù)定義的 getter弃理,也不會影響不使用的 getter:this
**** this
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
// automatically infers the return type as a number
doubleCount(state) {
return state.counter * 2
},
// the return type **must** be explicitly set
doublePlusOne(): number {
// autocompletion and typings for the whole store ?
return this.doubleCount + 1
},
},
})
然后你可以直接在 store 實例上訪問 getter:
<template>
<p>Double count is {{ store.doubleCount }}</p>
</template>
<script>
export default {
setup() {
const store = useStore()
return { store }
},
}
</script>
訪問其他 getter#
與計算屬性一樣溃论,您可以組合多個 getter。通過 訪問任何其他Gettersthis
痘昌。即使您不使用 TypeScript钥勋,您也可以使用JSDoc提示您的 IDE 類型:
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
// type is automatically inferred because we are not using `this`
doubleCount: (state) => state.counter * 2,
// here we need to add the type ourselves (using JSDoc in JS). We can also
// use this to document the getter
/**
* Returns the counter value times two plus one.
*
* @returns {number}
*/
doubleCountPlusOne() {
// autocompletion ?
return this.doubleCount + 1
},
},
})
將參數(shù)傳遞給 getter#
Getter只是在幕后計算的屬性,因此不可能將任何參數(shù)傳遞給它們辆苔。但是算灸,您可以從getter返回一個函數(shù)以接受任何參數(shù):
export const useStore = defineStore('main', {
getters: {
getUserById: (state) => {
return (userId) => state.users.find((user) => user.id === userId)
},
},
})
并在組件中使用:
<script>
export default {
setup() {
const store = useStore()
return { getUserById: store.getUserById }
},
}
</script>
<template>
<p>User 2: {{ getUserById(2) }}</p>
</template>
請注意,執(zhí)行此操作時驻啤,getter 不再緩存菲驴,它們只是您調(diào)用的函數(shù)。但是骑冗,您可以在 getter 本身內(nèi)部緩存一些結(jié)果赊瞬,這并不常見,但應(yīng)該證明性能更高:
export const useStore = defineStore('main', {
getters: {
getActiveUserById(state) {
const activeUsers = state.users.filter((user) => user.active)
return (userId) => activeUsers.find((user) => user.id === userId)
},
},
})
訪問其他Store的Getters#
要使用其他存儲 getter贼涩,您可以直接在getter內(nèi)部使用它:**
import { useOtherStore } from './other-store'
export const useStore = defineStore('main', {
state: () => ({
// ...
}),
getters: {
otherGetter(state) {
const otherStore = useOtherStore()
return state.localData + otherStore.data
},
},
})
用法與setup()
#
您可以直接訪問任何 getter 作為 store 的屬性(與 state 屬性完全一樣):
export default {
setup() {
const store = useStore()
store.counter = 3
store.doubleCount // 6
},
}
使用選項 API
對于以下示例森逮,您可以假設(shè)已創(chuàng)建以下Store:
// Example File Path:
// ./src/stores/counterStore.js
import { defineStore } from 'pinia',
const useCounterStore = defineStore('counterStore', {
state: () => ({
counter: 0
}),
getters: {
doubleCounter() {
return this.counter * 2
}
}
})
和setup()
#
雖然 Composition API 并不適合所有人,但setup()
鉤子可以使在 Options API 中使用 Pinia 變得更容易磁携。不需要額外的map helper功能褒侧!
import { useCounterStore } from '../stores/counterStore'
export default {
setup() {
const counterStore = useCounterStore()
return { counterStore }
},
computed: {
quadrupleCounter() {
return counterStore.doubleCounter * 2
},
},
}
沒有setup()
#
您可以使用上一部分狀態(tài)中使用的相同mapState()
函數(shù)來映射到 getter:
import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
computed: {
// gives access to this.doubleCounter inside the component
// same as reading from store.doubleCounter
...mapState(useCounterStore, ['doubleCount'])
// same as above but registers it as this.myOwnName
...mapState(useCounterStore, {
myOwnName: 'doubleCounter',
// you can also write a function that gets access to the store
double: store => store.doubleCount,
}),
},
}
Actions
Actions相當(dāng)于組件中的方法。它們可以使用actions
in 屬性進行定義,defineStore()
并且非常適合定義業(yè)務(wù)邏輯:
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
},
},
})
像getter一樣闷供,action通過完全輸入(和自動完成?)支持訪問整個Store實例烟央。與它們不同的是,它 可以是異步的歪脏,您可以在它們內(nèi)部進行任何 API 調(diào)用甚至其他操作疑俭!這是一個使用Mande的示例。請注意婿失,您使用的庫并不重要钞艇,只要您獲得 a ,您甚至可以使用本機函數(shù)(僅限瀏覽器):this
**** actions
await
Promise``fetch
import { mande } from 'mande'
const api = mande('/api/users')
export const useUsers = defineStore('users', {
state: () => ({
userData: null,
// ...
}),
actions: {
async registerUser(login, password) {
try {
this.userData = await api.post({ login, password })
showTooltip(`Welcome back ${this.userData.name}!`)
} catch (error) {
showTooltip(error)
// let the form component display the error
return error
}
},
},
})
你也可以完全自由地設(shè)置你想要的任何參數(shù)并返回任何東西豪硅。調(diào)用action時哩照,一切都會自動推斷!
action像方法一樣被調(diào)用:
export default defineComponent({
setup() {
const main = useMainStore()
// call the action as a method of the store
main.randomizeCounter()
return {}
},
})
訪問其他Store操作#
要使用另一個Store懒浮,您可以直接在action內(nèi)部使用它:**
import { useAuthStore } from './auth-store'
export const useSettingsStore = defineStore('settings', {
state: () => ({
preferences: null,
// ...
}),
actions: {
async fetchUserPreferences() {
const auth = useAuthStore()
if (auth.isAuthenticated) {
this.preferences = await fetchPreferences()
} else {
throw new Error('User must be authenticated')
}
},
},
})
用法與setup()
#
您可以直接調(diào)用任何操作作為 store 的方法:
export default {
setup() {
const store = useStore()
store.randomizeCounter()
},
}
使用選項 API
對于以下示例飘弧,您可以假設(shè)已創(chuàng)建以下Store:
// Example File Path:
// ./src/stores/counterStore.js
import { defineStore } from 'pinia',
const useCounterStore = defineStore('counterStore', {
state: () => ({
counter: 0
}),
actions: {
increment() {
this.counter++
}
}
})
和setup()
#
雖然 Composition API 并不適合所有人,但setup()
鉤子可以使在 Options API 中使用 Pinia 變得更容易砚著。不需要額外的map helper功能次伶!
import { useCounterStore } from '../stores/counterStore'
export default {
setup() {
const counterStore = useCounterStore()
return { counterStore }
},
methods: {
incrementAndPrint() {
this.counterStore.increment()
console.log('New Count:', this.counterStore.count)
},
},
}
沒有setup()
#
如果您根本不想使用 Composition API,可以使用mapActions()
幫助器將操作屬性映射為組件中的方法:
import { mapActions } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
methods: {
// gives access to this.increment() inside the component
// same as calling from store.increment()
...mapActions(useCounterStore, ['increment'])
// same as above but registers it as this.myOwnName()
...mapActions(useCounterStore, { myOwnName: 'doubleCounter' }),
},
}
訂閱操作#
可以用 觀察Action及其結(jié)果store.$onAction()
稽穆。傳遞給它的回調(diào)在操作本身之前執(zhí)行冠王。after
處理承諾并允許您在操作解決后執(zhí)行功能。以類似的方式舌镶,onError
允許您在操作拋出或拒絕時執(zhí)行函數(shù)版确。這些對于在運行時跟蹤錯誤很有用,類似于Vue 文檔中的這個技巧乎折。
這是一個在運行操作之前和它們解決/拒絕之后記錄的示例。
const unsubscribe = someStore.$onAction(
({
name, // name of the action
store, // store instance, same as `someStore`
args, // array of parameters passed to the action
after, // hook after the action returns or resolves
onError, // hook if the action throws or rejects
}) => {
// a shared variable for this specific action call
const startTime = Date.now()
// this will trigger before an action on `store` is executed
console.log(`Start "${name}" with params [${args.join(', ')}].`)
// this will trigger if the action succeeds and after it has fully run.
// it waits for any returned promised
after((result) => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.\nResult: ${result}.`
)
})
// this will trigger if the action throws or returns a promise that rejects
onError((error) => {
console.warn(
`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
)
// manually remove the listener
unsubscribe()
默認(rèn)情況下侵歇,操作訂閱綁定到添加它們的組件(如果Store位于組件的 內(nèi)部setup()
)骂澄。意思是,當(dāng)組件被卸載時惕虑,它們將被自動刪除坟冲。如果要在卸載組件后保留它們,請true
作為第二個參數(shù)傳遞以將操作訂閱與當(dāng)前組件分離:**
export default {
setup() {
const someStore = useSomeStore()
// this subscription will be kept after the component is unmounted
someStore.$onAction(callback, true)
// ...
},
}
Plugins 插件
由于低級 API溃蔫,Pania Store可以完全擴展健提。以下是您可以執(zhí)行的操作列表:
- 向Store添加新屬性
- 定義Store時添加新選項
- 向Store添加新方法
- 包裝現(xiàn)有方法
- 更改甚至取消操作
- 實現(xiàn)像本地存儲這樣的副作用
- 僅適用于特定Store
插件被添加到 pinia 實例中pinia.use()
。最簡單的例子是通過返回一個對象為所有Store添加一個靜態(tài)屬性:
import { createPinia } from 'pinia'
// add a property named `secret` to every store that is created after this plugin is installed
// this could be in a different file
function SecretPiniaPlugin() {
return { secret: 'the cake is a lie' }
}
const pinia = createPinia()
// give the plugin to pinia
pinia.use(SecretPiniaPlugin)
// in another file
const store = useStore()
store.secret // 'the cake is a lie'
這對于添加全局對象(如路由器伟叛、模式或 toast 管理器)很有用私痹。
介紹#
Pinia 插件是一個函數(shù),可以選擇返回要添加到Store的屬性。它需要一個可選參數(shù)紊遵,一個context:
export function myPiniaPlugin(context) {
context.pinia // the pinia created with `createPinia()`
context.app // the current app created with `createApp()` (Vue 3 only)
context.store // the store the plugin is augmenting
context.options // the options object defining the store passed to `defineStore()`
// ...
}
然后將此函數(shù)傳遞給pinia
with pinia.use()
:
pinia.use(myPiniaPlugin)
插件僅適用于傳遞給應(yīng)用 后創(chuàng)建pinia
的Store账千,否則將不會被應(yīng)用威恼。
擴充Store#
您可以通過簡單地在插件中返回它們的對象來為每個Store添加屬性:
pinia.use(() => ({ hello: 'world' }))
您也可以直接在 上設(shè)置屬性坤检,store
但如果可能,請使用返回版本门驾,以便它們可以被 devtools 自動跟蹤:
pinia.use(({ store }) => {
store.hello = 'world'
})
插件返回的任何屬性都將由 devtools 自動跟蹤学搜,因此為了hello
在 devtools 中可見娃善,請確保僅當(dāng)您想在 devtools 中調(diào)試它時才將其添加到store._customProperties
dev 模式:
// from the example above
pinia.use(({ store }) => {
store.hello = 'world'
// make sure your bundler handle this. webpack and vite should do it by default
if (process.env.NODE_ENV === 'development') {
// add any keys you set on the store
store._customProperties.add('hello')
}
})
請注意,每個Store都用 包裝reactive
瑞佩,自動解包裝它包含的任何 Ref ( ref()
, computed()
, ...):
const sharedRef = ref('shared')
pinia.use(({ store }) => {
// each store has its individual `hello` property
store.hello = ref('secret')
// it gets automatically unwrapped
store.hello // 'secret'
// all stores are sharing the value `shared` property
store.shared = sharedRef
store.shared // 'shared'
})
這就是為什么您可以在沒有的情況下訪問所有計算屬性.value
以及它們是反應(yīng)性的原因聚磺。
添加新狀態(tài)#
如果您想將新的狀態(tài)屬性添加到Store或打算在水合期間使用的屬性,您必須在兩個地方添加它:
- 在
store
所以你可以訪問它store.myState
-
store.$state
因此它可以在 devtools 中使用钉凌,并在SSR 期間被序列化咧最。
請注意,這允許您共享ref
orcomputed
屬性:
const globalSecret = ref('secret')
pinia.use(({ store }) => {
// `secret` is shared among all stores
store.$state.secret = globalSecret
store.secret = globalSecret
// it gets automatically unwrapped
store.secret // 'secret'
const hasError = ref(false)
store.$state.hasError = hasError
// this one must always be set
store.hasError = toRef(store.$state, 'hasError')
// in this case it's better not to return `hasError` since it
// will be displayed in the `state` section in the devtools
// anyway and if we return it, devtools will display it twice.
})
請注意御雕,插件中發(fā)生的狀態(tài)更改或添加(包括調(diào)用store.$patch()
)發(fā)生在Store處于活動狀態(tài)之前矢沿,因此不會觸發(fā)任何訂閱。
警告
如果您使用的是Vue 2酸纲,Pinia 會受到與Vue相同的反應(yīng)性警告捣鲸。創(chuàng)建新的狀態(tài)屬性時需要使用set
from ,例如and :@vue/composition-api``secret``hasError
import { set } from '@vue/composition-api'
pinia.use(({ store }) => {
if (!store.$state.hasOwnProperty('hello')) {
const secretRef = ref('secret')
// If the data is meant to be used during SSR, you should
// set it on the `$state` property so it is serialized and
// picked up during hydration
set(store.$state, 'secret', secretRef)
// set it directly on the store too so you can access it
// both ways: `store.$state.secret` / `store.secret`
set(store, 'secret', secretRef)
store.secret // 'secret'
}
})
添加新的外部屬性#
當(dāng)添加外部屬性闽坡、來自其他庫的類實例或僅僅是非反應(yīng)性的東西時栽惶,您應(yīng)該在將對象markRaw()
傳遞給 pinia 之前將其包裝起來。這是一個將路由器添加到每個Store的示例:
import { markRaw } from 'vue'
// adapt this based on where your router is
import { router } from './router'
pinia.use(({ store }) => {
store.router = markRaw(router)
})
$subscribe
在插件內(nèi)部調(diào)用#
您也可以在插件中使用store.onAction :
pinia.use(({ store }) => {
store.$subscribe(() => {
// react to store changes
})
store.$onAction(() => {
// react to store actions
})
})
添加新選項#
可以在定義Store時創(chuàng)建新選項疾嗅,以便以后從插件中使用它們外厂。例如,您可以創(chuàng)建一個debounce
允許您對任何操作進行去抖動的選項:
defineStore('search', {
actions: {
searchContacts() {
// ...
},
},
// this will be read by a plugin later on
debounce: {
// debounce the action searchContacts by 300ms
searchContacts: 300,
},
})
然后插件可以讀取該選項以包裝操作并替換原始操作:
// use any debounce library
import debounce from 'lodash/debunce'
pinia.use(({ options, store }) => {
if (options.debounce) {
// we are overriding the actions with new ones
return Object.keys(options.debounce).reduce((debouncedActions, action) => {
debouncedActions[action] = debounce(
store[action],
options.debounce[action]
)
return debouncedActions
}, {})
}
})
請注意代承,使用設(shè)置語法時汁蝶,自定義選項作為第三個參數(shù)傳遞:
defineStore(
'search',
() => {
// ...
},
{
// this will be read by a plugin later on
debounce: {
// debounce the action searchContacts by 300ms
searchContacts: 300,
},
}
)
TypeScript#
上面顯示的所有內(nèi)容都可以通過鍵入支持來完成,因此您無需使用any
或@ts-ignore
.
Typing plugins 插件#
Pinia 插件可以按如下方式鍵入:
import { PiniaPluginContext } from 'pinia'
export function myPiniaPlugin(context: PiniaPluginContext) {
// ...
}
鍵入新的Store屬性#
向Store添加新屬性時论悴,您還應(yīng)該擴展PiniaCustomProperties
接口掖棉。
import 'pinia'
declare module 'pinia' {
export interface PiniaCustomProperties {
// by using a setter we can allow both strings and refs
set hello(value: string | Ref<string>)
get hello(): string
// you can define simpler values too
simpleNumber: number
}
}
然后可以安全地寫入和讀取它:
pinia.use(({ store }) => {
store.hello = 'Hola'
store.hello = ref('Hola')
store.simpleNumber = Math.random()
// @ts-expect-error: we haven't typed this correctly
store.simpleNumber = ref(Math.random())
})
PiniaCustomProperties
是一種通用類型,允許您引用Store的屬性膀估。想象以下示例幔亥,我們將初始選項復(fù)制為$options
(這僅適用于選項存儲):
pinia.use(({ options }) => ({ $options: options }))
我們可以通過使用 4 種通用類型來正確輸入PiniaCustomProperties
:
import 'pinia'
declare module 'pinia' {
export interface PiniaCustomProperties<Id, S, G, A> {
$options: {
id: Id
state?: () => S
getters?: G
actions?: A
}
}
}
提示
在泛型中擴展類型時,它們的命名必須與源代碼中的完全相同察纯。Id
不能命名id
或I
帕棉,S
也不能命名State
针肥。以下是每個字母所代表的含義:
- S: State
- G: Getters
- A: Actions
- SS: Setup Store / Store
輸入新狀態(tài)#
當(dāng)添加新的狀態(tài)屬性(store
和store.$state
)時,您需要添加類型來PiniaCustomStateProperties
代替笤昨。與 不同的是PiniaCustomProperties
祖驱,它只接收State
泛型:
import 'pinia'
declare module 'pinia' {
export interface PiniaCustomStateProperties<S> {
hello: string
}
}
鍵入新的創(chuàng)建選項#
在為 創(chuàng)建新選項時defineStore()
,您應(yīng)該擴展DefineStoreOptionsBase
. 與 不同的是PiniaCustomProperties
瞒窒,它只公開了兩個泛型:State 和 Store 類型捺僻,允許您限制可以定義的內(nèi)容。例如崇裁,您可以使用操作的名稱:
import 'pinia'
declare module 'pinia' {
export interface DefineStoreOptionsBase<S, Store> {
// allow defining a number of ms for any of the actions
debounce?: Partial<Record<keyof StoreActions<Store>, number>>
}
}
提示
還有一種類型可以從 Store 類型StoreGetters
中提取getter 匕坯。 您也可以僅通過分別擴展類型和來擴展設(shè)置Store或選項Store 的選項。****DefineStoreOptions``DefineSetupStoreOptions
Nuxt.js#
在Nuxt 旁邊使用 pinia 時拔稳,您必須先創(chuàng)建一個Nuxt 插件葛峻。這將使您可以訪問該pinia
實例:
// plugins/myPiniaPlugin.js
import { PiniaPluginContext } from 'pinia'
import { Plugin } from '@nuxt/types'
function MyPiniaPlugin({ store }: PiniaPluginContext) {
store.$subscribe((mutation) => {
// react to store changes
console.log(`[?? ${mutation.storeId}]: ${mutation.type}.`)
})
// Note this has to be typed if you are using TS
return { creationTime: new Date() }
}
const myPlugin: Plugin = ({ $pinia }) => {
$pinia.use(MyPiniaPlugin)
}
export default myPlugin
請注意,上面的示例使用的是 TypeScript巴比,如果您使用的是文件术奖,則必須刪除類型注釋PiniaPluginContext
及其導(dǎo)入。Plugin``.js
在組件之外使用存儲
Pinia Store依靠pinia
實例在所有調(diào)用中共享同一個Store實例轻绞。useStore()
大多數(shù)情況下采记,只需調(diào)用您的函數(shù)即可開箱即用。例如政勃,在 中setup()
唧龄,您無需執(zhí)行任何其他操作。但在組件之外奸远,情況有些不同既棺。在幕后,useStore()
將pinia
您提供給app
. 這意味著如果pinia
無法自動注入實例懒叛,則必須手動將其提供給useStore()
函數(shù)丸冕。您可以根據(jù)您正在編寫的應(yīng)用程序的類型以不同的方式解決這個問題。
單頁應(yīng)用程序#
如果你沒有做任何 SSR(服務(wù)器端渲染)薛窥,useStore()
安裝 pinia 插件后的任何調(diào)用都app.use(pinia)
將起作用:
import { useUserStore } from '@/stores/user'
import { createApp } from 'vue'
import App from './App.vue'
// ? fails because it's called before the pinia is created
const userStore = useUserStore()
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
// ? works because the pinia instance is now active
const userStore = useUserStore()
確保始終應(yīng)用此功能的最簡單方法是通過將調(diào)用放在將始終在 pinia 安裝后運行的函數(shù)中來推遲調(diào)用胖烛。useStore()
讓我們看一下這個使用 Vue Router 的導(dǎo)航守衛(wèi)內(nèi)部的Store的例子:
import { createRouter } from 'vue-router'
const router = createRouter({
// ...
})
// ? Depending on the order of imports this will fail
const store = useStore()
router.beforeEach((to, from, next) => {
// we wanted to use the store here
if (store.isLoggedIn) next()
else next('/login')
})
router.beforeEach((to) => {
// ? This will work because the router starts its navigation after
// the router is installed and pinia will be installed too
const store = useStore()
if (to.meta.requiresAuth && !store.isLoggedIn) return '/login'
})
SSR 應(yīng)用#
在處理服務(wù)器端渲染時,您必須將pinia
實例傳遞給useStore()
. 這可以防止 pinia 在不同的應(yīng)用程序?qū)嵗g共享全局狀態(tài)拆檬。
在SSR 指南中有一個專門的部分,這只是一個簡短的解釋:
服務(wù)器端渲染 (SSR)
提示
如果您使用的是Nuxt.js妥凳, 則需要閱讀這些說明竟贯。
只要您在useStore()
函數(shù)頂部調(diào)用函數(shù),使用 Pinia 創(chuàng)建Store就可以立即使用 SSR setup
逝钥,getters
并且actions
:
export default defineComponent({
setup() {
// this works because pinia knows what application is running inside of
// `setup()`
const main = useMainStore()
return { main }
},
})
在外面使用Storesetup()
#
如果您需要在其他地方使用Store屑那,則需要將傳遞給應(yīng)用程序pinia
的實例傳遞給函數(shù)調(diào)用:useStore()
const pinia = createPinia()
const app = createApp(App)
app.use(router)
app.use(pinia)
router.beforeEach((to) => {
// ? This will work make sure the correct store is used for the
// current running app
const main = useMainStore(pinia)
if (to.meta.requiresAuth && !main.isLoggedIn) return '/login'
})
Pinia 方便地將自身添加$pinia
到您的應(yīng)用程序中拱镐,因此您可以在以下功能中使用它serverPrefetch()
:
export default {
serverPrefetch() {
const store = useStore(this.$pinia)
},
}
State hydration#
為了水合初始狀態(tài),您需要確保 rootState 包含在 HTML 中的某個位置持际,以便 Pinia 稍后獲取它沃琅。根據(jù)您用于 SSR的內(nèi)容,出于安全原因蜘欲,您應(yīng)該轉(zhuǎn)義該狀態(tài)益眉。我們建議使用Nuxt.js 使用的@nuxt/ devalue:
import devalue from '@nuxt/devalue'
import { createPinia } from 'pinia'
// retrieve the rootState server side
const pinia = createPinia()
const app = createApp(App)
app.use(router)
app.use(pinia)
// after rendering the page, the root state is build and can be read directly
// on `pinia.state.value`.
// serialize, escape (VERY important if the content of the state can be changed
// by the user, which is almost always the case), and place it somewhere on
// the page, for example, as a global variable.
devalue(pinia.state.value)
根據(jù)您用于 SSR 的內(nèi)容,您將設(shè)置將在 HTML 中序列化的初始狀態(tài)變量姥份。您還應(yīng)該保護自己免受 XSS 攻擊郭脂。例如,使用vite-ssr您可以使用transformState
選項和@nuxt/devalue
:
import devalue from '@nuxt/devalue'
export default viteSSR(
App,
{
routes,
transformState(state) {
return import.meta.env.SSR ? devalue(state) : state
},
},
({ initialState }) => {
// ...
if (import.meta.env.SSR) {
// this will be stringified and set to window.__INITIAL_STATE__
initialState.pinia = pinia.state.value
} else {
// on the client side, we restore the state
pinia.state.value = initialState.pinia
}
}
)
您可以根據(jù)需要使用其他替代方案@nuxt/devalue
澈歉,例如展鸡,如果您可以使用JSON.stringify()
/序列化和解析您的狀態(tài)JSON.parse()
,則可以大大提高您的性能埃难。
使此策略適應(yīng)您的環(huán)境莹弊。useStore()
在調(diào)用客戶端的任何函數(shù)之前,請確保水合 pinia 的狀態(tài)涡尘。例如忍弛,如果我們將狀態(tài)序列化為一個<script>
標(biāo)簽,使其可以在客戶端通過 全局訪問window.__pinia
悟衩,我們可以這樣寫:
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
// must be set by the user
if (isClient) {
pinia.state.value = JSON.parse(window.__pinia)
}
Nuxt.js#
將 Pinia 與Nuxt.js一起使用更容易剧罩,因為 Nuxt 在服務(wù)器端渲染方面處理了很多事情。例如座泳,您不需要關(guān)心序列化或 XSS 攻擊惠昔。
安裝#
確保安裝在旁邊:@nuxtjs/composition-api
pinia
yarn add pinia @pinia/nuxt @nuxtjs/composition-api
# or with npm
npm install pinia @pinia/nuxt @nuxtjs/composition-api
# or with pnpm
pnpm install pinia @pinia/nuxt @nuxtjs/composition-api
我們提供了一個模塊來為您處理所有事情,您只需將其添加到buildModules
您的nuxt.config.js
文件中:
// nuxt.config.js
export default {
// ... other options
buildModules: [
// Nuxt 2 only:
// https://composition-api.nuxtjs.org/getting-started/setup#quick-start
'@nuxtjs/composition-api/module',
'@pinia/nuxt',
],
}
就是這樣挑势,像往常一樣使用您的Store镇防!
在外面使用Storesetup()
#
如果你想在外面使用一個 store setup()
,記得把pinia
對象傳給useStore()
. 我們將它添加到上下文中asyncData()
潮饱,因此您可以在and中訪問它fetch()
:
import { useStore } from '~/stores/myStore'
export default {
asyncData({ $pinia }) {
const store = useStore($pinia)
},
}
在Store中使用 Nuxt 上下文#
您還可以通過使用注入屬性在任何Store中使用上下文$nuxt
:
import { useUserStore } from '~/stores/userStore'
defineStore('cart', {
actions: {
purchase() {
const user = useUserStore()
if (!user.isAuthenticated()) {
this.$nuxt.redirect('/login')
}
},
},
})
將 Pinia 與 Vuex 一起使用#
建議避免同時使用 Pinia 和 Vuex来氧,但如果您需要同時使用兩者,則需要告訴 pinia 不要禁用它:
// nuxt.config.js
export default {
buildModules: [
'@nuxtjs/composition-api/module',
['@pinia/nuxt', { disableVuex: false }],
],
// ... other options
}
TypeScript#
如果您使用的是 TypeScript 或有jsconfig.json
香拉,您還應(yīng)該添加以下類型context.pinia
:
{
"types": [
// ...
"@pinia/nuxt"
]
}
這也將確保您具有自動完成功能??啦扬。
不使用setup()
#
即使您不使用組合 API,也可以使用 Pinia(如果您使用的是 Vue 2凫碌,您仍然需要安裝@vue/composition-api
插件)扑毡。雖然我們建議您嘗試使用 Composition API 并學(xué)習(xí)它,但您和您的團隊可能還不是時候盛险,您可能正在遷移應(yīng)用程序或任何其他原因瞄摊。有幾個功能:
- mapStores
- mapState
- mapWritableState
- ?? mapGetters (just for migration convenience, use
mapState()
instead) - mapActions
授予對整個Store的訪問權(quán)限#
如果您需要訪問Store中的幾乎所有內(nèi)容勋又,則映射Store的每個屬性可能太多了......相反,您可以通過以下方式訪問整個StoremapStores()
:
import { mapStores } from 'pinia'
// given two stores with the following ids
const useUserStore = defineStore('user', {
// ...
})
const useCartStore = defineStore('cart', {
// ...
})
export default {
computed: {
// note we are not passing an array, just one store after the other
// each store will be accessible as its id + 'Store'
...mapStores(useCartStore, useUserStore)
},
methods: {
async buyStuff() {
// use them anywhere!
if (this.userStore.isAuthenticated()) {
await this.cartStore.buy()
this.$router.push('/purchased')
}
},
},
}
默認(rèn)情況下换帜,Pania 會為每個Store添加"Store"
后綴楔壤。id
您可以通過調(diào)用來自定義此行為setMapStoreSuffix()
:
import { createPinia, setMapStoreSuffix } from 'pinia'
// completely remove the suffix: this.user, this.cart
setMapStoreSuffix('')
// this.user_store, this.cart_store (it's okay, I won't judge you)
setMapStoreSuffix('_store')
export const pinia = createPinia()
TypeScript#
默認(rèn)情況下,所有地圖助手都支持自動完成惯驼,你不需要做任何事情蹲嚣。如果您調(diào)用setMapStoreSuffix()
更改"Store"
后綴,您還需要將其添加到 TS 文件或您的文件中的某個位置global.d.ts
跳座。最方便的地方是您打電話的地方setMapStoreSuffix()
:
import { createPinia, setMapStoreSuffix } from 'pinia'
setMapStoreSuffix('') // completely remove the suffix
export const pinia = createPinia()
declare module 'pinia' {
export interface MapStoresCustomization {
// set it to the same value as above
suffix: ''
}
}
警告
如果您使用的是 TypeScript 聲明文件(如global.d.ts
)端铛,請確保import 'pinia'
在其頂部公開所有現(xiàn)有類型。