mcpField
P
pinia

pinia

github.com/martinholovsky开发48197 次安装48 周前更新
代码仓库

关于 pinia

面向 JARVIS AI 助手开发者的 Pinia 状态管理技能,重点介绍类型化 Store、SSR 隔离、安全持久化和高效响应式更新。

主要功能

  1. 01使用 TypeScript 编写 Setup Store
  2. 02隔离 SSR 请求状态
  3. 03防止持久化敏感数据
  4. 04验证 Store 操作输入
  5. 05计算属性与选择性订阅
  6. 06使用 Vitest 测试 Store

适用场景

  1. 01跟踪系统指标与状态
  2. 02管理偏好设置和 HUD 配置
  3. 03维护命令历史与队列
  4. 04同步实时应用数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/martinholovsky/claude-skills-generator --skill pinia

安装后在 Claude Code 中输入/pinia确认已加载。

附带文件references/advanced-patterns.mdreferences/security-examples.md

详细介绍

Pinia 状态管理技能

文件组织:此技能采用拆分结构。有关高级模式和安全示例,请参阅 references/。

1. 概述

此技能提供 Pinia 专业知识,用于管理 JARVIS AI Assistant 中的应用状态,包括系统指标、用户偏好设置和 HUD 配置。

风险级别:中等 — 管理敏感状态,需考虑 SSR,并可能导致数据泄露

主要使用场景:

  • 系统指标和状态跟踪
  • 用户偏好设置和设置
  • HUD 配置状态
  • 命令历史记录和队列
  • 实时数据同步

2. 核心职责

2.1 核心原则
  1. TDD 优先:先编写 Store 测试,再进行实现
  2. 关注性能:优化订阅和计算值
  3. 类型安全:为 Store 定义完整的 TypeScript 类型
  4. SSR 安全:防止请求之间发生状态泄露
  5. 组合式 API:使用 setup Store,以获得更好的 TypeScript 支持
  6. 精简状态:仅存储必要数据,其余数据通过派生获得
  7. 操作验证:在操作中、修改状态前验证输入
  8. 持久化安全:绝不将敏感数据持久化到 localStorage

3. 技术栈和版本

3.1 推荐版本
包版本说明
pinia^2.1.0最新稳定版
@pinia/nuxt^0.5.0Nuxt 集成
pinia-plugin-persistedstate^3.0.0可选持久化
3.2 Nuxt 配置
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@pinia/nuxt'],
  pinia: {
    storesDirs: ['./stores/**']
  }
})
3.3 实现工作流(TDD)

对每个 Store 都遵循此工作流:

步骤 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('should initialize with default values', () => {
    const store = useMetricsStore()
    expect(store.cpu).toBe(0)
    expect(store.memory).toBe(0)
  })

  it('should clamp values within valid range', () => {
    const store = useMetricsStore()
    store.updateCpu(150)
    expect(store.cpu).toBe(100)
    store.updateCpu(-50)
    expect(store.cpu).toBe(0)
  })

  it('should compute health status correctly', () => {
    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 的 setup Store
// 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', () => {
  // State
  const state = ref<JARVISState>({
    status: 'idle',
    securityLevel: 'normal'
  })

  const metrics = ref<SystemMetrics>({
    cpu: 0,
    memory: 0,
    network: 0,
    timestamp: Date.now()
  })

  // Getters
  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'
  })

  // Actions
  function updateMetrics(newMetrics: Partial<SystemMetrics>) {
    // ✅ Validate input
    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

    // ✅ Audit security changes
    console.info(`Security level changed to: ${level}`)
  }

  return {
    state,
    metrics,
    isActive,
    systemHealth,
    updateMetrics,
    setStatus,
    setSecurityLevel
  }
})
4.2 用户偏好设置 Store(带持久化)
// 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']
    // ❌ Never persist: tokens, passwords, API keys
  }
})
4.3 命令队列 Store
// 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 安全的 Store 用法
<script setup lang="ts">
// ✅ Safe for SSR - store initialized per-request
const jarvisStore = useJarvisStore()

// ✅ Fetch data on server
const { data } = await useFetch('/api/metrics')

// Update store with fetched data
if (data.value) {
  jarvisStore.updateMetrics(data.value)
}
</script>
4.5 Store 组合
// stores/dashboard.ts
export const useDashboardStore = defineStore('dashboard', () => {
  // ✅ Compose from other stores
  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 敏感数据处理
// ❌ NEVER persist: tokens, API keys, passwords
// ✅ Store sensitive data in memory only (no persist option)
const authStore = defineStore('auth', () => {
  const token = ref<string | null>(null)
  return { token }
})

5.5 性能模式

模式 1:选择性订阅
// BAD - Subscribes to entire store
const store = useJarvisStore()
watch(() => store.state, () => { /* ... */ }, { deep: true })

// GOOD - Subscribe to specific properties
const store = useJarvisStore()
watch(() => store.state.status, (newStatus) => {
  console.log('Status changed:', newStatus)
})
模式 2:计算属性(记忆化)
// BAD - Recalculates on every access
function getFilteredItems() {
  return items.value.filter(i => i.active)
}

// GOOD - Cached until dependencies change
const filteredItems = computed(() =>
  items.value.filter(i => i.active)
)
模式 3:批量更新
// BAD - Multiple reactive triggers
function updateAll(data: MetricsData) {
  metrics.value.cpu = data.cpu
  metrics.value.memory = data.memory
  metrics.value.network = data.network
}

// GOOD - Single reactive trigger
function updateAll(data: MetricsData) {
  metrics.value = { ...metrics.value, ...data, timestamp: Date.now() }
}
模式 4:延迟初始化 Store
// BAD - Store initializes immediately
const heavyStore = useHeavyDataStore()

// GOOD - Initialize only when needed
const heavyStore = ref<ReturnType<typeof useHeavyDataStore> | null>(null)

function loadHeavyData() {
  if (!heavyStore.value) {
    heavyStore.value = useHeavyDataStore()
  }
  return heavyStore.value
}
模式 5:乐观更新
// BAD - Wait for server response
async function deleteItem(id: string) {
  await api.delete(`/items/${id}`)
  items.value = items.value.filter(i => i.id !== id)
}

// GOOD - Update immediately, rollback on error
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  // Rollback
    throw error
  }
}

6. 测试和质量

有关包含 vitest 示例的完整 TDD 工作流,请参阅第 3.3 节。

8. 常见反模式

安全反模式
// ❌ Global state leaks between SSR users
const state = reactive({ user: null })

// ✅ Pinia isolates per-request
export const useUserStore = defineStore('user', () => {
  const user = ref(null)
  return { user }
})

// ❌ Never persist auth tokens (XSS risk)
persist: { paths: ['authToken'] }

// ✅ Use httpOnly cookies for auth
性能反模式

有关包含“良好/不良”示例的详细性能模式,请参阅第 5.5 节。

13. 实施前检查清单

阶段 1:编写代码前
  • 使用 TypeScript 类型设计 Store 接口
  • 创建包含失败测试的测试文件
  • 确认安全要求(持久化、SSR)
  • 针对使用场景选择性能模式
阶段 2:实施期间
  • 每添加一项功能后,测试均通过
  • 操作会验证所有输入
  • 计算值使用最少的依赖项
  • 持久化状态中不包含敏感数据
  • SSR 状态已正确隔离
阶段 3:提交前
  • 所有 Store 测试均通过:npm run test -- --filter=stores
  • 类型检查通过:npm run typecheck
  • 构建成功:npm run build
  • Pinia 之外没有全局状态
  • 已在类型中记录状态结构

14. 总结

Pinia 为 JARVIS 提供类型安全的状态管理:

  1. 测试驱动开发优先:先编写 Store 测试,再进行实现
  2. 性能:优化订阅和计算属性
  3. 安全性:切勿持久化敏感数据,并隔离 SSR 状态
  4. 类型安全:使用完整支持 TypeScript 的 Setup Store

参考资料:高级模式和安全示例请参见 references/。

评论

支持 Markdown