昨天我們已經在魔法書院裡完成了 Pinia 的 Composition API 咒語。
今天要召喚另一種魔法陣:Options API 風格!
在開始之前,先釐清一個重點:
Pinia 並沒有「setter」這個東西。
在 Pinia 的世界裡,寫入/更新 state 的行為,是透過 function 來完成的:
function(例如 setOrdersJson)。actions: {} 裡的方法(例如 replaceAllOrders)。你可以把它們視為 Pinia 的 setter 行為,但它們不是 computed 的 set()。
Vue 的 computed.set() 是 Vue 本身的能力,而不是 Pinia 的;在複雜邏輯下更建議用 function/action 來承擔。
因為 Pinia 想要照顧不同的魔法師:
| 特性 | Composition API | Options API | 
|---|---|---|
| 學習曲線 | 需要熟悉 Vue 3 Composition API | 類似 Vuex,容易上手 | 
| 語法風格 | 函數式,更靈活 | 物件式,結構化 | 
| TypeScript | 更好的型別推斷 | 需要額外設定 | 
| 適用場景 | 複雜邏輯、現代開發 | 團隊協作、傳統開發 | 
| 程式碼組織 | 邏輯可自由組合 | 結構清晰分離 | 
其實可以考古一下
從前的大老們都是使用vuex但是現在改用pinia這樣
資訊技術本身就是會隨時間演進改變的~
所以基本上還是追隨公司的慣用風格跟框架來做調整~ 還有使用

這邊可以幫大家複習一下
| 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
// 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 }
})
// 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 - 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 - 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
      }
    }
  }
})
例如:
setOrdersJson(txt)
replaceAllOrders(newOrders)
這些 function 都是 setter 行為。
👉 Vue 的 computed.set() 是 Vue 的能力,而不是 Pinia 的。
computed.set()。actions: {} 裡的方法,就是 setter。Component → 呼叫 store 的 action。
Store → action(function)負責 setter 行為。
Action → 修改 state。
State 改變 → Vue 響應式機制觸發 UI 更新。

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