name: pinia description: Pinia 状态管理用于 JARVIS 系统状态 model: sonnet risk_level: MEDIUM version: 1.0.0
Pinia 状态管理技能
文件组织:此技能使用分割结构。请参阅
references/获取高级模式和安全示例。
1. 概述
此技能提供 Pinia 专长,用于管理 JARVIS AI 助手中的应用状态,包括系统指标、用户偏好和 HUD 配置。
风险级别:中等 - 管理敏感状态、SSR 考虑、潜在数据暴露
主要用例:
- 系统指标和状态跟踪
- 用户偏好和设置
- HUD 配置状态
- 命令历史和队列
- 实时数据同步
2. 核心职责
2.1 核心原则
- 测试驱动开发优先:在实现前编写存储测试
- 性能意识:优化订阅和计算值
- 类型安全:使用完整的 TypeScript 类型定义存储
- SSR 安全:防止请求间的状态泄漏
- 组合 API:使用设置存储以更好地支持 TypeScript
- 最小状态:仅存储必要数据,派生其余部分
- 操作验证:在突变前验证输入
- 持久化安全:从不将敏感数据持久化到 localStorage
3. 技术栈和版本
3.1 推荐版本
| 包 | 版本 | 注释 |
|---|---|---|
| pinia | ^2.1.0 | 最新稳定版 |
| @pinia/nuxt | ^0.5.0 | Nuxt 集成 |
| pinia-plugin-persistedstate | ^3.0.0 | 可选持久化 |
3.2 Nuxt 配置
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@pinia/nuxt'],
pinia: {
storesDirs: ['./stores/**']
}
})
3.3 实现工作流程(测试驱动开发)
为每个存储遵循此工作流程:
步骤 1: 首先编写失败测试
// tests/stores/metrics.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useMetricsStore } from '~/stores/metrics'
describe('MetricsStore', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
it('应该用默认值初始化', () => {
const store = useMetricsStore()
expect(store.cpu).toBe(0)
expect(store.memory).toBe(0)
})
it('应该在有效范围内钳位值', () => {
const store = useMetricsStore()
store.updateCpu(150)
expect(store.cpu).toBe(100)
store.updateCpu(-50)
expect(store.cpu).toBe(0)
})
it('应该正确计算健康状态', () => {
const store = useMetricsStore()
store.updateCpu(95)
store.updateMemory(90)
expect(store.healthStatus).toBe('critical')
})
})
步骤 2: 实现最小通过代码
// stores/metrics.ts
export const useMetricsStore = defineStore('metrics', () => {
const cpu = ref(0)
const memory = ref(0)
const healthStatus = computed(() => {
const avg = (cpu.value + memory.value) / 2
if (avg > 90) return 'critical'
if (avg > 70) return 'warning'
return 'healthy'
})
function updateCpu(value: number) {
cpu.value = Math.max(0, Math.min(100, value))
}
function updateMemory(value: number) {
memory.value = Math.max(0, Math.min(100, value))
}
return { cpu, memory, healthStatus, updateCpu, updateMemory }
})
步骤 3: 遵循模式重构
- 提取验证逻辑
- 添加 TypeScript 接口
- 优化计算依赖项
步骤 4: 运行完整验证
npm run test -- --filter=stores
npm run typecheck
npm run build
4. 实现模式
4.1 使用 TypeScript 的设置存储
// stores/jarvis.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
interface SystemMetrics {
cpu: number
memory: number
network: number
timestamp: number
}
interface JARVISState {
status: 'idle' | 'listening' | 'processing' | 'responding'
securityLevel: 'normal' | 'elevated' | 'lockdown'
}
export const useJarvisStore = defineStore('jarvis', () => {
// 状态
const state = ref<JARVISState>({
status: 'idle',
securityLevel: 'normal'
})
const metrics = ref<SystemMetrics>({
cpu: 0,
memory: 0,
network: 0,
timestamp: Date.now()
})
// 获取器
const isActive = computed(() =>
state.value.status !== 'idle'
)
const systemHealth = computed(() => {
const avg = (metrics.value.cpu + metrics.value.memory) / 2
if (avg > 90) return 'critical'
if (avg > 70) return 'warning'
return 'healthy'
})
// 操作
function updateMetrics(newMetrics: Partial<SystemMetrics>) {
// ✅ 验证输入
if (newMetrics.cpu !== undefined) {
metrics.value.cpu = Math.max(0, Math.min(100, newMetrics.cpu))
}
if (newMetrics.memory !== undefined) {
metrics.value.memory = Math.max(0, Math.min(100, newMetrics.memory))
}
if (newMetrics.network !== undefined) {
metrics.value.network = Math.max(0, newMetrics.network)
}
metrics.value.timestamp = Date.now()
}
function setStatus(newStatus: JARVISState['status']) {
state.value.status = newStatus
}
function setSecurityLevel(level: JARVISState['securityLevel']) {
state.value.securityLevel = level
// ✅ 审计安全更改
console.info(`安全级别更改为: ${level}`)
}
return {
state,
metrics,
isActive,
systemHealth,
updateMetrics,
setStatus,
setSecurityLevel
}
})
4.2 用户偏好存储(带持久化)
// stores/preferences.ts
export const usePreferencesStore = defineStore('preferences', () => {
const preferences = ref({
theme: 'dark' as 'dark' | 'light',
hudOpacity: 0.8,
soundEnabled: true
})
function updatePreference<K extends keyof typeof preferences.value>(
key: K, value: typeof preferences.value[K]
) {
if (key === 'hudOpacity' && (value < 0 || value > 1)) return
preferences.value[key] = value
}
return { preferences, updatePreference }
}, {
persist: {
key: 'jarvis-preferences',
paths: ['preferences.theme', 'preferences.hudOpacity']
// ❌ 从不持久化:令牌、密码、API 密钥
}
})
4.3 命令队列存储
// stores/commands.ts
interface Command {
id: string
action: string
status: 'pending' | 'executing' | 'completed' | 'failed'
}
export const useCommandStore = defineStore('commands', () => {
const queue = ref<Command[]>([])
const history = ref<Command[]>([])
const MAX_HISTORY = 100
const pendingCommands = computed(() =>
queue.value.filter(cmd => cmd.status === 'pending')
)
function addCommand(action: string) {
const cmd: Command = { id: crypto.randomUUID(), action, status: 'pending' }
queue.value.push(cmd)
return cmd.id
}
function completeCommand(id: string, status: 'completed' | 'failed') {
const idx = queue.value.findIndex(cmd => cmd.id === id)
if (idx !== -1) {
const [cmd] = queue.value.splice(idx, 1)
cmd.status = status
history.value = [cmd, ...history.value].slice(0, MAX_HISTORY)
}
}
return { queue, history, pendingCommands, addCommand, completeCommand }
})
4.4 SSR 安全存储使用
<script setup lang="ts">
// ✅ SSR 安全 - 每个请求初始化存储
const jarvisStore = useJarvisStore()
// ✅ 在服务器上获取数据
const { data } = await useFetch('/api/metrics')
// 用获取的数据更新存储
if (data.value) {
jarvisStore.updateMetrics(data.value)
}
</script>
4.5 存储组合
// stores/dashboard.ts
export const useDashboardStore = defineStore('dashboard', () => {
// ✅ 从其他存储组合
const jarvisStore = useJarvisStore()
const commandStore = useCommandStore()
const dashboardStatus = computed(() => ({
systemHealth: jarvisStore.systemHealth,
pendingCommands: commandStore.pendingCommands.length,
isActive: jarvisStore.isActive
}))
return {
dashboardStatus
}
})
5. 安全标准
5.1 OWASP 覆盖
| OWASP 类别 | 风险 | 缓解措施 |
|---|---|---|
| A01 访问控制失效 | 中等 | 验证操作、检查权限 |
| A04 不安全设计 | 中等 | SSR 状态隔离 |
| A07 认证和授权失败 | 中等 | 从不持久化令牌 |
5.3 敏感数据处理
// ❌ 从不持久化:令牌、API 密钥、密码
// ✅ 仅在内存中存储敏感数据(无持久化选项)
const authStore = defineStore('auth', () => {
const token = ref<string | null>(null)
return { token }
})
5.5 性能模式
模式 1: 选择性订阅
// 坏 - 订阅整个存储
const store = useJarvisStore()
watch(() => store.state, () => { /* ... */ }, { deep: true })
// 好 - 订阅特定属性
const store = useJarvisStore()
watch(() => store.state.status, (newStatus) => {
console.log('状态更改:', newStatus)
})
模式 2: 计算获取器(记忆化)
// 坏 - 每次访问重新计算
function getFilteredItems() {
return items.value.filter(i => i.active)
}
// 好 - 缓存直到依赖项更改
const filteredItems = computed(() =>
items.value.filter(i => i.active)
)
模式 3: 批量更新
// 坏 - 多个反应式触发
function updateAll(data: MetricsData) {
metrics.value.cpu = data.cpu
metrics.value.memory = data.memory
metrics.value.network = data.network
}
// 好 - 单个反应式触发
function updateAll(data: MetricsData) {
metrics.value = { ...metrics.value, ...data, timestamp: Date.now() }
}
模式 4: 延迟存储初始化
// 坏 - 存储立即初始化
const heavyStore = useHeavyDataStore()
// 好 - 仅在需要时初始化
const heavyStore = ref<ReturnType<typeof useHeavyDataStore> | null>(null)
function loadHeavyData() {
if (!heavyStore.value) {
heavyStore.value = useHeavyDataStore()
}
return heavyStore.value
}
模式 5: 乐观更新
// 坏 - 等待服务器响应
async function deleteItem(id: string) {
await api.delete(`/items/${id}`)
items.value = items.value.filter(i => i.id !== id)
}
// 好 - 立即更新,错误时回滚
async function deleteItem(id: string) {
const backup = [...items.value]
items.value = items.value.filter(i => i.id !== id)
try {
await api.delete(`/items/${id}`)
} catch (error) {
items.value = backup // 回滚
throw error
}
}
6. 测试与质量
请参阅 第 3.3 节 获取使用 vitest 示例的完整测试驱动开发工作流程。
8. 常见反模式
安全反模式
// ❌ 全局状态在 SSR 用户间泄漏
const state = reactive({ user: null })
// ✅ Pinia 隔离每个请求
export const useUserStore = defineStore('user', () => {
const user = ref(null)
return { user }
})
// ❌ 从不持久化认证令牌(XSS 风险)
persist: { paths: ['authToken'] }
// ✅ 使用 httpOnly cookie 进行认证
性能反模式
请参阅 第 5.5 节 获取带有好/坏示例的详细性能模式。
13. 预实现检查清单
阶段 1: 编码前
- [ ] 使用 TypeScript 类型设计存储接口
- [ ] 创建带有失败测试的测试文件
- [ ] 识别安全要求(持久化、SSR)
- [ ] 为用例选择性能模式
阶段 2: 实现期间
- [ ] 每个功能添加后测试通过
- [ ] 操作验证所有输入
- [ ] 计算值使用最小依赖项
- [ ] 持久化状态中无敏感数据
- [ ] SSR 状态正确隔离
阶段 3: 提交前
- [ ] 所有存储测试通过:
npm run test -- --filter=stores - [ ] 类型检查通过:
npm run typecheck - [ ] 构建成功:
npm run build - [ ] Pinia 外无全局状态
- [ ] 状态形状在类型中记录
14. 总结
Pinia 为 JARVIS 提供类型安全的状态管理:
- 测试驱动开发优先:在实现前编写存储测试
- 性能:优化订阅和计算值
- 安全:从不持久化敏感数据,隔离 SSR 状态
- 类型安全:使用带有完整 TypeScript 的设置存储
参考:请参阅 references/ 获取高级模式和安全示例。