fix: convert apps/website from submodule to regular directory
This commit is contained in:
+262
@@ -0,0 +1,262 @@
|
||||
---
|
||||
title: Use Pinia for Large-Scale Vue Applications
|
||||
impact: MEDIUM
|
||||
impactDescription: Hand-rolled reactive stores lack DevTools integration, TypeScript support, and debugging capabilities needed for production apps
|
||||
type: best-practice
|
||||
tags: [vue3, pinia, state-management, devtools, architecture, scalability]
|
||||
---
|
||||
|
||||
# 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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
// 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:
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
|
||||
```javascript
|
||||
// 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:
|
||||
|
||||
```javascript
|
||||
// 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:
|
||||
|
||||
```javascript
|
||||
// Automatic per-request state isolation
|
||||
// State serialization for hydration
|
||||
// No cross-request pollution
|
||||
```
|
||||
|
||||
### 6. Plugin Ecosystem
|
||||
|
||||
Pinia supports plugins for common needs:
|
||||
|
||||
```javascript
|
||||
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:
|
||||
|
||||
```javascript
|
||||
// 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)
|
||||
|
||||
```javascript
|
||||
export const useCounterStore = defineStore('counter', {
|
||||
state: () => ({ count: 0 }),
|
||||
getters: {
|
||||
double: (state) => state.count * 2
|
||||
},
|
||||
actions: {
|
||||
increment() { this.count++ }
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Setup Style (Composition API)
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```bash
|
||||
npm install pinia
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 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')
|
||||
```
|
||||
|
||||
## Reference
|
||||
- [Vue.js - State Management](https://vuejs.org/guide/scaling-up/state-management.html)
|
||||
- [Pinia Documentation](https://pinia.vuejs.org/)
|
||||
- [Pinia vs Vuex](https://pinia.vuejs.org/introduction.html#comparison-with-vuex)
|
||||
Reference in New Issue
Block a user