协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
vue-pinia【开发与工程】Vue 3 state management with Pinia using the Composition API, covering setup stores, SSR/Nuxt integration, persistence, and testing. Use when creating or refactoring Pinia stores, deciding what state belongs in a store versus a component, wiring stores into router guards, persisting store state to storage, or testing store actions with @pinia/testing.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Pinia is the standard state management library for Vue 3, and setup stores built with the Composition API give shared application state the same ergonomics as local ref/computed state.
stores/, e.g. stores/cart.ts, using defineStore('cart', () => { ... }).ref/reactive, derive values with computed, and put all writes/side effects in functions (actions).<script setup>, destructure reactive state with storeToRefs(), and destructure actions directly.@pinia/testing.ref, reactive, or computed.useFetch / useAsyncData, TanStack Query for Vue, or the existing API layer for server state — Pinia is for client state, not a data-fetching cache.defineStore('name', () => { ... }) for Vue 3 Composition API projects over the older options-API defineStore({ state, getters, actions }) form.stores/, such as stores/cart.ts or stores/session.ts.ref is invisible to the rest of the system.// stores/cart.ts
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
interface CartLine {
id: string
name: string
quantity: number
unitPrice: number
}
export const useCartStore = defineStore('cart', () => {
const lines = ref<CartLine[]>([])
const itemCount = computed(() =>
lines.value.reduce((total, line) => total + line.quantity, 0),
)
const subtotal = computed(() =>
lines.value.reduce((total, line) => total + line.quantity * line.unitPrice, 0),
)
function addLine(line: CartLine) {
const existing = lines.value.find((item) => item.id === line.id)
if (existing) {
existing.quantity += line.quantity
return
}
lines.value.push(line)
}
function removeLine(id: string) {
lines.value = lines.value.filter((line) => line.id !== id)
}
function clearCart() {
lines.value = []
}
return {
lines,
itemCount,
subtotal,
addLine,
removeLine,
clearCart,
}
})
<script setup> or inside setup functions, getters, and actions — never conditionally, and never inside a plain .js/.ts module loaded at import time.storeToRefs() when destructuring store state or getters in components, so reactivity is preserved.storeToRefs().watch that just recomputes a value should usually be a computed.<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useCartStore } from '@/stores/cart'
const cart = useCartStore()
const { itemCount, subtotal } = storeToRefs(cart)
const { clearCart } = cart
</script>
<template>
<div>
<p>{{ itemCount }} items — ${{ subtotal.toFixed(2) }}</p>
<button type="button" :disabled="itemCount === 0" @click="clearCart">
Clear cart
</button>
</div>
</template>
any; use unknown and narrow external inputs (API responses, persisted storage) before committing them to state.{ status: 'idle' } | { status: 'loading' } | { status: 'error'; error: string }.defineStore) stable and descriptive because they appear in devtools and persistence keys.status, error, and lastUpdatedAt fields when the UI depends on them.connect()/disconnect() action pair).localStorage, window) during server rendering — guard with import.meta.client (Nuxt) or an onMounted check.@pinia/nuxt integration and SSR-safe composables for data fetching instead of manually wiring a Pinia instance.// router guard example (outside setup)
router.beforeEach((to) => {
const auth = useAuthStore(pinia) // pass the active Pinia instance explicitly
if (to.meta.requiresAuth && !auth.isAuthenticated) {
return { name: 'login' }
}
})
localStorage directly.@pinia/testing (createTestingPinia()) for component tests that need stores, with actions stubbed by default.acceptHMRUpdate() in stores when the project uses Vite HMR patterns.import { setActivePinia, createPinia } from 'pinia'
import { describe, it, expect, beforeEach } from 'vitest'
import { useCartStore } from '@/stores/cart'
describe('cart store', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
it('merges quantities for duplicate line items', () => {
const cart = useCartStore()
cart.addLine({ id: '1', name: 'Widget', quantity: 1, unitPrice: 10 })
cart.addLine({ id: '1', name: 'Widget', quantity: 2, unitPrice: 10 })
expect(cart.itemCount).toBe(3)
})
})
storeToRefs(); this breaks reactivity silently.