Pinia状态管理技能Skill pinia

这个技能提供 Pinia 状态管理专长,用于在 JARVIS AI 助手中管理应用状态,包括系统指标、用户偏好和 HUD 配置。它专注于前端开发,使用 Vue.js 和 TypeScript 实现类型安全和高效状态管理,适用于实时数据同步和 SSR 安全场景。关键词:Pinia, Vue.js, 状态管理, TypeScript, 前端开发, JARVIS, 实时同步, SSR 安全。

前端开发 0 次安装 0 次浏览 更新于 3/15/2026

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 核心原则

  1. 测试驱动开发优先:在实现前编写存储测试
  2. 性能意识:优化订阅和计算值
  3. 类型安全:使用完整的 TypeScript 类型定义存储
  4. SSR 安全:防止请求间的状态泄漏
  5. 组合 API:使用设置存储以更好地支持 TypeScript
  6. 最小状态:仅存储必要数据,派生其余部分
  7. 操作验证:在突变前验证输入
  8. 持久化安全:从不将敏感数据持久化到 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 提供类型安全的状态管理:

  1. 测试驱动开发优先:在实现前编写存储测试
  2. 性能:优化订阅和计算值
  3. 安全:从不持久化敏感数据,隔离 SSR 状态
  4. 类型安全:使用带有完整 TypeScript 的设置存储

参考:请参阅 references/ 获取高级模式和安全示例。