iT邦幫忙

1

【Pinia】專案使用 Pinia 取代 mitt 紀錄

  • 分享至 

  • xImage
  •  

在學習 Pinia 之後,我便決心為我的舊專案導入狀態管理
於是我挑了我之前做的虛構電商網站來導入 Pinia

為什麼要使用 Pinia

在使用 Vue.js 開發 SPA 的時候,我們會將每一頁乃至於頁面中的其中一部份切分成單一的元件
而元件本身的資料是獨立的
簡單來說,a.vue 中可以有 cart, b.vue 中也可以有同名的 cart,兩者互不衝突
然而,這樣的情況下也會遇到困難
也就是跨元件的資料傳遞

在我的專案中,有一個 UserNavbar.vue 元件
它會顯示現在購物車裡面有多少筆商品
https://ithelp.ithome.com.tw/upload/images/20250108/201288634sGw4bgIu9.png
然而,我們卻會在其他元件中觸發加入購物車的方法
這時候購物車內商品的數量發生改變,我們就必須讓 UserNavbar.vue 正確抓到更新後的數量
然而,觸發的元件與 UserNavbar.vue 之間要跨多層元件傳送,無法輕易的使用 props 解決問題
簡單來說,我有了跨元件的資料傳遞的需求

mitt

之前我使用的是 mitt
首先我們單獨建立一個 emitter.js

import mitt from 'mitt'
const emitter = mitt()
export default emitter

然後在 UserNavbar.vue 中 import

import emitter from '@/methods/emitter'
......
mounted () {
    emitter.on('update-cart', () => {
      this.getCart() //更新購物車數量
    })
 }

我們透過 mitt 註冊了一個 update-cart 事件,這樣不管在哪一個元件中,我們都可以透過觸發這個事件來讓購物車更新

// UserProduct.vue
import emitter from '@/methods/emitter'
......
addCart (id) {
     ......
     this.$http.post(api, { data: { product_id: id, qty: 1 } }).then(() => {
        emitter.emit('update-cart') // 觸發 UserNavbar.vue 中註冊的事件
     })
},

mitt 這樣的作法,只是幫我們便於掛元件傳遞,並沒有資料狀態的管理效果
data 仍是四散於各個元件內

pinia

pinia 就是真正的狀態管理工具了
比起 mitt,pinia 不單單可以做到跨元件的資料傳遞,更能夠實踐統一管理
https://ithelp.ithome.com.tw/upload/images/20250108/20128863L40B2g0fz6.png
簡單來說,我們原先購物車的內容是四散於每一個元件中的data內
每個元件中的購物車資料都是獨立的
而 pinia 將其集中的一隻獨立的 store 檔案中進行單獨管理
cartStore.js 中會包含了購物車的資料狀態(state),改變購物車的方法(actions),取得購物車資料的處理(getters)等等

import { defineStore } from 'pinia'
export default defineStore('cartStore', {
  state: () => ({
    cart: {
      carts: []
    }
  }),
  actions: {
    getCart () {
      ......
    },
    addCart (id, qty = 1) {
      ......
    }
  }
})

透過 pinia ,我們會建立一個 store 將資料狀態和方法存在 store 中,並透過 store 中的方法更新資料,最後再把資料傳遞到所需要的元件內
改用了 pinia 之後,我更新 UserNavbar.vue 的流程變成這樣

// UserProduct.vue
<button class="..." type="button" @click="addCart(product.id, qty)"></button>

import cartStore from '@/stores/cartStore'
import { mapActions } from 'pinia'
......
methods: {
    ......
    ...mapActions(cartStore, ['addCart'])
}

我們直接從 cartStore.js 中匯入更新購物車的方法,然後通過模板中的按鈕觸發 cartStore.js 中的 actions
這樣我們無論是購物車的資料,還是更新購物車的方法,都是透過 cartStore.js 統一進行管理的
而當 cartStore.js 中的購物車資料改變的時候
因為 UserNavbar.vue 也是獲取同一個 cartStore.js 中的購物車資料,我們也就不用額外去觸發什麼來更新 UserNavbar.vue 了

<div class="...">{{ cart.length }}</div>
import cartStore from '@/stores/cartStore'
......
computed: {
    ...mapState(cartStore, ['cart'])
 }

透過 pinia
當我們在元件 A 中修改了 store 中的資料,其他匯入了相同 store 的元件,資料也會同時改變
這就不僅僅實現了跨元件的資料傳遞,更實現了狀態管理


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言