- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma binaryTargets for openssl 3.0 - nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal cron, http->https redirect - prisma: add origin_goods.delisted migration, sync missing schema (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz - api: CORS wildcard reflection, helmet CORP cross-origin, price backfill in persistProductDetail, categoryIcon ancestor fallback, mediaByColor per-color gallery in public goods detail - admin: /admin base path (vite + router) - import-data.mjs: udt_name casting, serial sequence advance fix
6.5 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Use Pinia for Large-Scale Vue Applications | MEDIUM | Hand-rolled reactive stores lack DevTools integration, TypeScript support, and debugging capabilities needed for production apps | best-practice |
|
Use Pinia for Large-Scale Vue Applications
Impact: MEDIUM - While Vue's Composition API allows creating simple reactive stores with reactive() and ref(), these hand-rolled solutions lack the tooling, conventions, and debugging capabilities needed for large-scale production applications. Pinia is the official Vue state management solution and should be used for any non-trivial application.
Task Checklist
- Use Pinia for applications with shared state across multiple components
- Use Pinia when team collaboration requires consistent patterns
- Use Pinia when you need Vue DevTools state debugging
- Hand-rolled
reactive()is acceptable only for simple, single-developer apps - Migrate from Vuex to Pinia (Vuex is in maintenance mode)
When Hand-Rolled State is Acceptable
Simple reactive state is fine for:
- Prototypes and proof-of-concepts
- Very small applications with minimal shared state
- Single-developer projects with limited scope
- Learning purposes
// Simple hand-rolled store - OK for small apps
import { reactive, readonly } from 'vue'
export const store = reactive({
count: 0,
increment() {
this.count++
}
})
When to Use Pinia
Use Pinia when you have any of these requirements:
1. DevTools Integration
Pinia provides rich Vue DevTools support:
- Timeline of state changes
- State inspection and editing
- Time-travel debugging
- Action tracking
// Pinia store - fully visible in DevTools
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++ // Tracked in DevTools timeline
}
}
})
2. TypeScript Support
Pinia has excellent TypeScript inference:
// Full type inference without extra configuration
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: '',
age: 0,
preferences: {
theme: 'light' as 'light' | 'dark'
}
}),
getters: {
// Return type is inferred
displayName: (state) => state.name || 'Anonymous'
},
actions: {
// Full parameter type checking
setUser(name: string, age: number) {
this.name = name
this.age = age
}
}
})
// Usage is fully typed
const userStore = useUserStore()
userStore.name // string
userStore.displayName // string
userStore.setUser('John', 30) // Type-checked
3. Team Collaboration
Pinia enforces conventions that help teams:
// Consistent structure across all stores
// stores/products.js
export const useProductsStore = defineStore('products', {
state: () => ({ /* ... */ }),
getters: { /* ... */ },
actions: { /* ... */ }
})
// stores/cart.js - Same structure
export const useCartStore = defineStore('cart', {
state: () => ({ /* ... */ }),
getters: { /* ... */ },
actions: { /* ... */ }
})
4. Hot Module Replacement (HMR)
Pinia supports HMR out of the box - state persists during development:
// State survives code changes during development
// No need to re-login or re-create state after every edit
5. Server-Side Rendering (SSR)
Pinia handles SSR state correctly:
// Automatic per-request state isolation
// State serialization for hydration
// No cross-request pollution
6. Plugin Ecosystem
Pinia supports plugins for common needs:
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
// Now stores can persist to localStorage
export const useSettingsStore = defineStore('settings', {
state: () => ({
theme: 'light',
language: 'en'
}),
persist: true // Automatically saved/restored
})
Pinia vs Hand-Rolled Comparison
| Feature | Hand-Rolled reactive() |
Pinia |
|---|---|---|
| DevTools integration | No | Yes |
| TypeScript inference | Manual | Automatic |
| HMR support | No | Yes |
| SSR support | Manual | Built-in |
| Plugins | No | Yes |
| Time-travel debugging | No | Yes |
| Learning curve | Lower | Slightly higher |
| Bundle size | Smaller | ~1KB |
| Team conventions | None | Enforced |
Migration from Vuex
Vuex is now in maintenance mode. Migrate to Pinia for new features:
// Vuex (legacy)
export default createStore({
state: { count: 0 },
mutations: {
INCREMENT(state) { state.count++ }
},
actions: {
increment({ commit }) { commit('INCREMENT') }
}
})
// Pinia (recommended)
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() { this.count++ } // No mutations needed!
}
})
Pinia advantages over Vuex:
- No mutations (simpler mental model)
- Better TypeScript support
- No nested modules complexity
- Smaller bundle size
- Composition API style available
Pinia Store Styles
Choose the style that fits your team:
Options Style (Similar to Options API)
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2
},
actions: {
increment() { this.count++ }
}
})
Setup Style (Composition API)
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
return { count, double, increment }
})
Quick Start
npm install pinia
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')