iT邦幫忙

2025 iThome 鐵人賽

DAY 17
0
Vue.js

需求至上的 Vue 魔法之旅系列 第 17

Day : 12.5 Pinia Options API vs Composition API 差異比較

  • 分享至 

  • xImage
  •  

前言

昨天我們已經在魔法書院裡完成了 Pinia 的 Composition API 咒語
今天要召喚另一種魔法陣:Options API 風格

在開始之前,先釐清一個重點:

Pinia 並沒有「setter」這個東西。

在 Pinia 的世界裡,寫入/更新 state 的行為,是透過 function 來完成的

  • Composition API 中,就是一般的 function(例如 setOrdersJson)。
  • Options API 中,就是寫在 actions: {} 裡的方法(例如 replaceAllOrders)。

你可以把它們視為 Pinia 的 setter 行為,但它們不是 computed 的 set()
Vue 的 computed.set() 是 Vue 本身的能力,而不是 Pinia 的;在複雜邏輯下更建議用 function/action 來承擔。


🧙‍ 為什麼 Pinia 有兩種寫法?

因為 Pinia 想要照顧不同的魔法師:

  • 有些人習慣靈活的咒語(Composition API)
  • 有些人偏好結構化的卷軸(Options API)
特性 Composition API Options API
學習曲線 需要熟悉 Vue 3 Composition API 類似 Vuex,容易上手
語法風格 函數式,更靈活 物件式,結構化
TypeScript 更好的型別推斷 需要額外設定
適用場景 複雜邏輯、現代開發 團隊協作、傳統開發
程式碼組織 邏輯可自由組合 結構清晰分離

從 Vuex 到 Pinia 的演進

其實可以考古一下

從前的大老們都是使用vuex但是現在改用pinia這樣

資訊技術本身就是會隨時間演進改變的~

所以基本上還是追隨公司的慣用風格跟框架來做調整~ 還有使用

https://ithelp.ithome.com.tw/upload/images/20251001/201210525FLqAqvnhD.png


核心概念對照表

這邊可以幫大家複習一下

Vuex 概念 Pinia Options API Pinia Composition API
state state: () => ({}) const state = ref()
getters getters: {} const computed = computed()
mutations ❌ 不需要 ❌ 不需要
actions actions: {} function action() {}
modules 多個 store 多個 store

為什麼 Vuex 需要 mutations

在 Vuex 中,mutations 是唯一能夠同步修改 state 的方式。

一般流程是:
dispatch action → (可包含非同步操作) → commit mutation → 改變 state


一、Pinia 的兩種寫法對比

Composition API 風格(Day 12 使用)

// Day 12 的寫法 - Composition API
export const useOrderStore = defineStore('orders', () => {
  const orders = ref([])
  const loading = ref(false)
  const error = ref('')
  
  const ordersJson = computed(() => JSON.stringify(orders.value, null, 2))
  
  function setOrdersJson(txt) {
    orders.value = parsed
  }
  
  return { orders, loading, error, ordersJson, setOrdersJson }
})

Options API 風格(今天介紹)

// Day 12.5 的寫法 - Options API
export const useOrderStore = defineStore('orders', {
  state: () => ({
    orders: [],
    loading: false,
    error: ''
  }),
  getters: {
    ordersJson: (state) => JSON.stringify(state.orders, null, 2)
  },
  actions: {
    setOrdersJson(txt) {
      this.orders = parsed
    }
  }
})

二、核心差異對照表

其實有了概念交互對照就很快

概念 Options API Composition API
狀態 state: () => ({}) const state = ref()
計算屬性 getters: {} const computed = computed()
動作(setter 行為) actions: {} function action() {}
存取狀態 this.state state.value
語法 物件結構 函數回傳

三、實際程式碼對比

Day 12 的 orderStore.js(Composition API)

// Day 12 - Composition API 風格
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { OrderService } from '../services/orderService'

export const useOrderStore = defineStore('orders', () => {
  // State
  const orders = ref([])
  const loading = ref(false)
  const error = ref('')

  // Getters
  const summaryRows = computed(() => {
    const m = new Map()
    for (const o of orders.value) {
      const key = `${o.drink}|${o.sweetness}|${o.ice}`
      m.set(key, (m.get(key) || 0) + 1)
    }
    return Array.from(m.entries()).map(([key, count]) => {
      const [drink, sweetness, ice] = key.split('|')
      return { key, drink, sweetness, ice, count }
    })
  })

  const ordersJson = computed(() => {
    return JSON.stringify(orders.value, null, 2)
  })

  // Actions (setter 行為)
  function setOrdersJson(txt) {
    try {
      const parsed = JSON.parse(txt)
      // 驗證邏輯...
      orders.value = parsed
      error.value = ''
    } catch (e) {
      error.value = '匯入訂單 JSON 失敗: ' + e.message
    }
  }

  async function loadOrders() {
    try {
      loading.value = true
      error.value = ''
      const data = await OrderService.list()
      orders.value = data
    } catch (err) {
      error.value = '載入訂單失敗: ' + err.message
    } finally {
      loading.value = false
    }
  }

  return {
    orders, loading, error,
    summaryRows, ordersJson,
    setOrdersJson, loadOrders
  }
})

Day 12.5 的 orderStore.js(Options API)

// Day 12.5 - Options API 風格
import { defineStore } from 'pinia'
import { OrderService } from '../services/orderService'

export const useOrderStore = defineStore('orders', {
  state: () => ({
    orders: [],
    loading: false,
    error: ''
  }),

  getters: {
    summaryRows: (state) => {
      const m = new Map()
      for (const o of state.orders) {
        const key = `${o.drink}|${o.sweetness}|${o.ice}`
        m.set(key, (m.get(key) || 0) + 1)
      }
      return Array.from(m.entries()).map(([key, count]) => {
        const [drink, sweetness, ice] = key.split('|')
        return { key, drink, sweetness, ice, count }
      })
    },

    ordersJson: (state) => {
      return JSON.stringify(state.orders, null, 2)
    }
  },

  actions: {
    // setter 行為
    setOrdersJson(txt) {
      try {
        const parsed = JSON.parse(txt)
        this.orders = parsed
        this.error = ''
      } catch (e) {
        this.error = '匯入訂單 JSON 失敗: ' + e.message
      }
    },

    async loadOrders() {
      try {
        this.loading = true
        this.error = ''
        const data = await OrderService.list()
        this.orders = data
      } catch (err) {
        this.error = '載入訂單失敗: ' + err.message
      } finally {
        this.loading = false
      }
    }
  }
})

四、Setter 行為的觀念補充 🪄

  • 在 Pinia 中,寫入/更新 state 的動作 → 就是 function/action
  • 你可以把這些 function/action 視為 Pinia 的 setter

例如:

  • setOrdersJson(txt)
  • replaceAllOrders(newOrders)

這些 function 都是 setter 行為

👉 Vue 的 computed.set() 是 Vue 的能力,而不是 Pinia 的。

  • 簡單雙向綁定 → 可以用 computed.set()
  • 包含驗證、錯誤處理、API 呼叫、邏輯規則 → 一律寫在 action/function。

對應關係

  • Composition API → store 裡的 function,本質上就是 setter。
  • Options API → 寫在 actions: {} 裡的方法,就是 setter。

這樣你會看到的流程:

  1. Component → 呼叫 store 的 action。

  2. Store → action(function)負責 setter 行為。

  3. Action → 修改 state。

  4. State 改變 → Vue 響應式機制觸發 UI 更新。

5.UI 更新 → 使用者看到新結果。
https://ithelp.ithome.com.tw/upload/images/20251001/20121052Tm3BpjcSS5.png

五、最終建議

  1. 簡單同步雙向綁定 → 可以用 computed.set(少用)。
  2. 複雜邏輯 → 用 function/action 來承擔。
  3. 一致性最重要 → 專案內選一種寫法即可,不要混搭。
  4. Pinia 的 setter = function/action,不是 computed.set

✨小結:
Pinia 的魔法核心,就是「用 function 來改變 state」。
至於你想寫在卷軸(Options API),還是用靈活的咒語(Composition API),就看你的團隊習慣與專案需求囉!

day12.5 orderstore的差異 github


上一篇
Day 12 : 可靠的中央行政單位:Pinia Getters / Setters
下一篇
Day 13 : 用咒語守護表單:VeeValidate + Yup 的即時驗證魔法
系列文
需求至上的 Vue 魔法之旅18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言