Vue3 筆記 | Pinia 管理全域資料

2023/11/07閱讀時間約 9 分鐘

Vue 的狀態 (資料) 是靠著 ref reactive 儲存,然後靠著 propsemit 在組件中傳遞,可是如果今天專案一大,難免會有需要資料共享的狀況,那時不免要 propsprops 去、emitemit 去。

針對這個問題,當然也可以選擇使用 Provide-Inject 的方式來傳遞,但其實有更好的做法,就是用 Pinia 統一管理資料。



Pinia 使用

在 Vue 專案建立的時候其實就有選項問說要不要使用 Pinia 來做管理,如果選了的話,當專案建立完會在 /src 下發現一個 stores 資料夾,同時在 main.js 中也會發現 Pinia 自動被引入:

import './assets/main.css'

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

const app = createApp(App)

app.use(createPinia())

app.mount('#app')


如果是舊專案要引入 Pinia,也可以透過 npm install pinia 的方式下載安裝,在自己手動修改 main.js 的內容。



認識 store

新專案會在 /src/stores 下預設建立好一個 counter.js 作為範例,它就是一個 store。

Store 負責儲存可供全域使用的狀態及邏輯,換句話說,它可以儲存 data、computed 以及 method。

定義一個 store,必須先從 pinia 引入 defineStore,命名的時候通常以 "use 開頭 + 這個 store 主要幹的事 + store 結尾"defineStore 接受兩個參數,一個是 store id,大可直接放入去掉 use 的命名;第二個是一個 arrow function,裡面就是儲存資料或邏輯的地方。

現在來看一下 pinia 預先在專案裡給我們的例子:

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}

return { count, doubleCount, increment }
})

會發現 store 裡的資料或邏輯寫法跟在 <script setup> 裡寫的基本一樣,這是組合式的寫法,較早一點的選項式寫法更簡便易懂。然後透過 return 把資料或邏輯給拋出來給全域使用。



引用 store 內部資料或邏輯

在組件中我們可以直接 import 要用的 store,並把內容除存在某變數之中 (xxStore 之類的),之後可以在組件中調用 xxStore 內部的資料或邏輯。

App.vue:

<script setup>
import {useCounterStore} from '@/stores/counter'

const store = useCounterStore()
</script>

<template>
<h1>{{ store.count }}</h1>
<button @click="store.increment">Add</button>
</template>

我們可以看到畫面渲染出 store 中定義的 count 並且按鈕也可以運作。



Store 的解構

Store 的解構一句你想解構的是資料還是方法有不同做法。如果單純是方法可以直接解構,但如果是響應式資料 (包括 computed) 則要使用 storeToRefs 來做提取,看一下官方的例子,來實做看看:

App.vue:

<script setup>
import { storeToRefs } from 'pinia'
import {useCounterStore} from '@/stores/counter'

const store = useCounterStore()
const {count, doubleCount} = storeToRefs(store)
const {increment} = store

</script>

<template>
<h1>{{ count }}</h1>
<button @click="increment">Add</button>
</template>

畫面一樣可以運作,解構成功!



Store 狀態的重置

我們可以在 store 中定義 $reset 方法來讓狀態重置:

Counter.js:

// 添加 reset 方法
const $reset = () => count.value = 0

return { count, doubleCount, increment, $reset }

App.vue:

<script setup>
import {useCounterStore} from '@/stores/counter'

const store = useCounterStore()
</script>

<template>
<h1>{{ store.count }}</h1>
<button @click="store.increment">Add</button>
<button @click="store.$reset">Reset to 0</button>
</template>

當我們點擊重置按鈕時,畫面數字就會歸零了。



數據修改

存在 store 中的數據有三種修改方式:

  1. 直接修改,但不推薦:
<button @click="store.count++">Add</button>
  1. 引用方法:
<button @click="store.increment">Add</button>
  1. 使用 $patch
const addCount = () =>{
store.$patch(state =>{
state.count++
})
}

<button @click="addCount">Add</button>



數據監聽

可不可以用 watch?當然可以,但 pinia 提供了屬於它的 $subscribe 用法。

Subscribe 接收兩個參數,一個是 mutation,一個是監聽對象。Mutation console.log 出來又有三個數性值:

  1. type:記錄數據變化是通過什麼途徑。
  2. storeId:就當前 store 的 id。
  3. events:state 改變的具體數據。

我們在 App.vue 中新增一個 subscribe

store.$subscribe((mutation, state) =>{
console.log('Something changed!')
})

當我們點擊按鈕後會 console.logSomething changed!

raw-image

我們也可以用 watch 做監聽,但記得添加深層監聽:

watch(store, state =>{
console.log(`watch here`, state)
}, {deep : true})



Action (方法) 的監聽

如同監聽數據一般,Vue 可以用 $onAction 監聽方法的使用,比方說我要監聽 increment 這個函式的執行時間:

store.$onAction(({ name, after }) => {
if (name === 'increment') {
const startTime = Date.now();

after(() => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.`
);
});
}
})

打開 devtool 就可以看到監測到的執行時間。

raw-image



參考資料

  1. Pinia 官方文件
  2. Vue3 + Vite 快速上手 Get Startrd EP6 - Pinia 的全域資料管理!
18會員
37內容數
這個專題用來存放我在學習網頁開發時的心得及知識。
留言0
查看全部
發表第一個留言支持創作者!