name: vue-nuxt description: Vue 3 和 Nuxt 3 用于 JARVIS AI 助手用户界面开发,采用安全优先模式 model: sonnet risk_level: 中等 version: 1.0.0
Vue 3 / Nuxt 3 开发技能
文件组织:此技能采用分割结构。查看
references/获取高级模式和安全性示例。
1. 概述
此技能提供使用 Vue 3 和 Nuxt 3 构建 JARVIS AI 助手用户界面的专业知识。它专注于创建响应式、高性能的 3D HUD 界面,采用安全优先的开发实践。
风险等级:中等 - 处理用户输入,渲染动态内容,潜在 XSS 向量
主要用例:
- 为 JARVIS 界面构建响应式 3D HUD 组件
- 服务器端渲染以实现初始加载性能
- 客户端状态管理集成
- 安全处理用户输入和 API 响应
2. 核心职责
2.1 基本原则
- 测试驱动开发优先:先写测试再实现 - 红/绿/重构循环
- 性能意识:使用计算属性、浅引用、懒加载组件以实现最佳响应式
- 组合 API 优先:使用 Vue 3 组合 API 和
<script setup>以获得更好的 TypeScript 推断和代码组织 - 服务器端安全:利用 Nuxt 的服务器路由处理敏感操作,永不向客户端暴露秘密
- 响应式状态安全:使用
ref()和reactive()并适当类型化以防止状态损坏 - 输入清理:始终在渲染或处理前清理用户输入
- 性能优化:实现懒加载、代码分割和高效响应式以优化 3D HUD 性能
- 类型安全:全程强制使用 TypeScript 以在编译时检测错误
- 安全默认设置:配置 CSP 头部,默认禁用危险功能
3. 技术栈与版本
3.1 推荐版本
| 包 | 版本 | 安全说明 |
|---|---|---|
| Vue | ^3.4.0 | 最新稳定版,改进了响应式 |
| Nuxt | ^3.12.4+ | 关键:修复 CVE-2024-34344 RCE |
| @nuxt/devtools | ^1.3.9+ | 关键:修复 CVE-2024-23657 |
| vite | ^5.0.0 | 最新版,含安全补丁 |
3.2 安全关键依赖项
{
"dependencies": {
"nuxt": "^3.12.4",
"vue": "^3.4.0",
"dompurify": "^3.0.6",
"isomorphic-dompurify": "^2.0.0"
},
"devDependencies": {
"@nuxt/devtools": "^1.3.9",
"eslint-plugin-vue": "^9.0.0",
"eslint-plugin-security": "^2.0.0"
}
}
4. 实现模式
4.1 安全组件结构
<script setup lang="ts">
// ✅ 类型安全的属性与验证
interface Props {
hudData: HUDDisplayData
userId: string
}
const props = defineProps<Props>()
// ✅ 发出带类型负载的事件
const emit = defineEmits<{
'update:status': [status: string]
'command:execute': [command: JARVISCommand]
}>()
// ✅ 安全引用初始化
const displayState = ref<HUDState>({
isActive: false,
securityLevel: 'standard'
})
</script>
<template>
<!-- ✅ 使用 v-text 处理用户内容以防止 XSS -->
<div class="hud-panel">
<span v-text="props.hudData.title" />
</div>
</template>
4.2 输入清理模式
// composables/useSanitize.ts
import DOMPurify from 'isomorphic-dompurify'
export function useSanitize() {
const sanitizeHTML = (dirty: string): string => {
// ✅ 对任何 HTML 内容进行严格清理
return DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'span'],
ALLOWED_ATTR: ['class']
})
}
const sanitizeText = (input: string): string => {
// ✅ 为纯文本移除所有 HTML
return DOMPurify.sanitize(input, { ALLOWED_TAGS: [] })
}
return { sanitizeHTML, sanitizeText }
}
4.3 安全 API 路由模式
// server/api/jarvis/command.post.ts
import { z } from 'zod'
// ✅ 定义严格模式用于命令验证
const commandSchema = z.object({
action: z.enum(['status', 'control', 'query']),
target: z.string().max(100).regex(/^[a-zA-Z0-9-_]+$/),
parameters: z.record(z.string()).optional()
})
export default defineEventHandler(async (event) => {
const body = await readBody(event)
// ✅ 根据模式验证输入
const result = commandSchema.safeParse(body)
if (!result.success) {
throw createError({
statusCode: 400,
message: '无效命令格式' // ✅ 通用错误消息
})
}
// ✅ 处理已验证命令
const command = result.data
// 永不记录敏感数据
console.log(`处理命令:${command.action}`)
return { success: true, commandId: generateSecureId() }
})
4.4 安全环境配置
// nuxt.config.ts
export default defineNuxtConfig({
// ✅ 安全头部
routeRules: {
'/**': {
headers: {
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block'
}
}
},
// ✅ 运行时配置 - 秘密保持服务器端
runtimeConfig: {
apiSecret: process.env.API_SECRET, // 仅服务器
public: {
apiBase: '/api' // 客户端可访问
}
},
// ✅ 在生产中禁用开发工具
devtools: { enabled: process.env.NODE_ENV === 'development' }
})
4.5 3D HUD 组件集成
<script setup lang="ts">
// components/HUDDisplay.vue
import { TresCanvas } from '@tresjs/core'
const props = defineProps<{
metrics: SystemMetrics
}>()
// ✅ 在渲染前验证指标
const validatedMetrics = computed(() => {
return {
cpu: Math.min(100, Math.max(0, props.metrics.cpu)),
memory: Math.min(100, Math.max(0, props.metrics.memory)),
status: sanitizeText(props.metrics.status)
}
})
</script>
<template>
<TresCanvas>
<HUDMetricsDisplay :data="validatedMetrics" />
</TresCanvas>
</template>
5. 实现工作流程(测试驱动开发)
5.1 步骤 1:先写失败的测试
始终从编写定义预期行为的测试开始:
// tests/components/VoiceIndicator.test.ts
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import VoiceIndicator from '@/components/VoiceIndicator.vue'
describe('VoiceIndicator', () => {
it('默认显示空闲状态', () => {
const wrapper = mount(VoiceIndicator)
expect(wrapper.find('.indicator').classes()).toContain('idle')
expect(wrapper.text()).toContain('Ready')
})
it('在激活时显示监听状态', async () => {
const wrapper = mount(VoiceIndicator, {
props: { isListening: true }
})
expect(wrapper.find('.indicator').classes()).toContain('listening')
expect(wrapper.find('.pulse-animation').exists()).toBe(true)
})
it('在按下 Esc 键时发出取消事件', async () => {
const wrapper = mount(VoiceIndicator, {
props: { isListening: true }
})
await wrapper.trigger('keydown.escape')
expect(wrapper.emitted('cancel')).toBeTruthy()
})
})
5.2 步骤 2:实现最小代码以通过测试
仅编写足够代码以使测试通过:
<script setup lang="ts">
const props = defineProps<{ isListening?: boolean }>()
const emit = defineEmits<{ 'cancel': [] }>()
const handleKeydown = (e: KeyboardEvent) => {
if (e.key === 'Escape') emit('cancel')
}
</script>
<template>
<div
class="indicator"
:class="isListening ? 'listening' : 'idle'"
@keydown="handleKeydown"
tabindex="0"
>
<span v-if="!isListening">Ready</span>
<div v-else class="pulse-animation" />
</div>
</template>
5.3 步骤 3:如有需要,重构
测试通过后,在不改变行为的情况下改进代码质量。每次重构后重新运行测试。
5.4 步骤 4:运行全面验证
# 提交前运行所有验证步骤
npx vitest run # 单元测试
npx eslint . --ext .vue,.ts # 代码检查
npx nuxi typecheck # 类型检查
npm run build # 构建验证
6. 性能模式
6.1 用于派生状态的计算属性
// ❌ 坏 - 每次渲染都在模板中重新计算
<template>
<div>{{ items.filter(i => i.active).length }} active</div>
</template>
// ✅ 好 - 缓存直到依赖项改变
const activeCount = computed(() => items.value.filter(i => i.active).length)
<template>
<div>{{ activeCount }} active</div>
</template>
6.2 浅引用用于大型对象
// ❌ 坏 - 对大型 3D 数据进行深度响应式
const meshData = ref<MeshData>({ vertices: new Float32Array(100000), ... })
// ✅ 好 - 浅响应式,手动触发
const meshData = shallowRef<MeshData>({ vertices: new Float32Array(100000), ... })
// 显式触发更新
meshData.value = { ...newData }
triggerRef(meshData)
6.3 定义异步组件用于懒加载
// ❌ 坏 - 所有组件预先加载
import HeavyChart from '@/components/HeavyChart.vue'
// ✅ 好 - 仅在需要时加载
const HeavyChart = defineAsyncComponent(() =>
import('@/components/HeavyChart.vue')
)
// 带加载状态
const HeavyChart = defineAsyncComponent({
loader: () => import('@/components/HeavyChart.vue'),
loadingComponent: LoadingSpinner,
delay: 200
})
6.4 v-memo 用于列表优化
<!-- ❌ 坏 - 任何更改都重新渲染所有项 -->
<div v-for="item in items" :key="item.id">
<ExpensiveComponent :data="item" />
</div>
<!-- ✅ 好 - 如果项未改变则跳过重新渲染 -->
<div v-for="item in items" :key="item.id" v-memo="[item.id, item.updated]">
<ExpensiveComponent :data="item" />
</div>
6.5 虚拟滚动用于长列表
<script setup lang="ts">
import { useVirtualList } from '@vueuse/core'
const { list, containerProps, wrapperProps } = useVirtualList(
items,
{ itemHeight: 50 }
)
</script>
<template>
<!-- ✅ 仅渲染可见项 -->
<div v-bind="containerProps" class="h-[400px] overflow-auto">
<div v-bind="wrapperProps">
<div v-for="{ data, index } in list" :key="index">
{{ data.name }}
</div>
</div>
</div>
</template>
6.6 防抖监视器
// ❌ 坏 - 每次击键都触发
watch(searchQuery, async (query) => {
results.value = await searchAPI(query)
})
// ✅ 好 - 防抖以减少 API 调用
import { watchDebounced } from '@vueuse/core'
watchDebounced(
searchQuery,
async (query) => {
results.value = await searchAPI(query)
},
{ debounce: 300 }
)
// 手动防抖替代方案
watch(searchQuery, useDebounceFn(async (query) => {
results.value = await searchAPI(query)
}, 300))
7. 安全标准
7.1 已知漏洞(CVE 研究)
| CVE | 严重性 | 描述 | 缓解措施 |
|---|---|---|---|
| CVE-2024-34344 | 高 | 通过测试组件的 Nuxt RCE | 更新到 Nuxt 3.12.4+ |
| CVE-2024-23657 | 高 | Devtools 路径遍历/RCE | 更新 devtools 到 1.3.9+ |
| CVE-2023-3224 | 关键 | 开发服务器代码注入 | 更新到 Nuxt 3.4.4+,永不暴露开发服务器 |
参见:references/security-examples.md 获取详细缓解代码
5.2 OWASP Top 10 覆盖
| OWASP 类别 | 风险 | 缓解策略 |
|---|---|---|
| A01 访问控制损坏 | 高 | 服务器端路由守卫,中间件认证 |
| A03 注入 | 高 | 使用 Zod 进行输入验证,参数化查询 |
| A05 安全配置错误 | 中等 | CSP 头部,安全 nuxt.config |
| A07 XSS | 高 | v-text 指令,DOMPurify 清理 |
5.3 输入验证框架
// ❌ 危险 - 直接 v-html 与用户输入
<div v-html="userMessage" />
// ✅ 安全 - 清理的 HTML 或纯文本
<div v-html="sanitizeHTML(userMessage)" />
<span v-text="userMessage" />
5.4 认证中间件
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
const { authenticated } = useAuthState()
if (!authenticated.value && to.meta.requiresAuth) {
return navigateTo('/login')
}
})
6. 测试与质量
6.1 安全测试
// tests/security/xss.test.ts
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import HUDPanel from '@/components/HUDPanel.vue'
describe('XSS 预防', () => {
it('应清理恶意输入', () => {
const wrapper = mount(HUDPanel, {
props: {
title: '<script>alert("xss")</script>Hello'
}
})
expect(wrapper.html()).not.toContain('<script>')
expect(wrapper.text()).toContain('Hello')
})
})
6.2 组件测试
// tests/components/HUDDisplay.test.ts
describe('HUDDisplay', () => {
it('验证指标边界', () => {
const wrapper = mount(HUDDisplay, {
props: {
metrics: { cpu: 150, memory: -10, status: 'active' }
}
})
// 应将值钳制到有效范围
expect(wrapper.vm.validatedMetrics.cpu).toBe(100)
expect(wrapper.vm.validatedMetrics.memory).toBe(0)
})
})
7. 常见模式 / 工作流程
7.1 JARVIS HUD 组件工作流程
- 定义 TypeScript 接口 用于所有数据结构
- 创建可组合项 用于共享逻辑
- 实现组件 使用组合 API
- 添加输入验证 在组件边界
- 编写安全测试 用于 XSS/注入
- 集成 3D 场景 通过 TresJS
7.2 API 集成工作流程
- 定义 Zod 模式 用于请求/响应
- 创建服务器路由 带验证
- 实现客户端可组合项 带错误处理
- 添加加载/错误状态 到 UI
- 测试错误情况 和边缘条件
8. 常见错误与反模式
8.1 关键安全反模式
绝不:使用未清理输入的 v-html
<!-- ❌ 危险 - XSS 漏洞 -->
<div v-html="userProvidedContent" />
<!-- ✅ 安全 - 清理的内容 -->
<div v-html="sanitizeHTML(userProvidedContent)" />
<!-- ✅ 最佳 - 当不需要 HTML 时使用纯文本 -->
<span v-text="userProvidedContent" />
绝不:在客户端代码中暴露秘密
// ❌ 危险 - 公共配置中的秘密
runtimeConfig: {
public: {
apiKey: process.env.API_KEY // 暴露给客户端!
}
}
// ✅ 安全 - 秘密保持服务器端
runtimeConfig: {
apiKey: process.env.API_KEY, // 仅服务器
public: {
apiBase: '/api'
}
}
绝不:仅信任客户端验证
// ❌ 危险 - 仅客户端验证
const handleSubmit = () => {
if (isValidEmail(email.value)) {
$fetch('/api/subscribe', { body: { email: email.value } })
}
}
// ✅ 安全 - 服务器端验证
// server/api/subscribe.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const result = emailSchema.safeParse(body)
if (!result.success) {
throw createError({ statusCode: 400, message: '无效邮箱' })
}
// 处理已验证邮箱
})
8.2 性能反模式
避免:在计算属性中使用响应式数组
// ❌ 坏 - 每次访问都创建新数组
const filtered = computed(() => {
return items.value.filter(i => i.active).sort()
})
// ✅ 好 - 带稳定引用的记忆化
const filtered = computed(() => {
const result = items.value.filter(i => i.active)
result.sort((a, b) => a.name.localeCompare(b.name))
return result
})
9. 快速参考
基本命令
# 开发
npx nuxi dev --host # 永不暴露到公共网络!
# 安全审计
npm audit --audit-level=high
npx nuxi typecheck
# 生产构建
npx nuxi build
关键可组合项
// 状态管理
const state = useState<T>('key', () => initialValue)
// 运行时配置访问
const config = useRuntimeConfig()
// 路由导航
const router = useRouter()
await navigateTo('/path')
13. 预部署检查清单
安全验证
- [ ] Nuxt 版本 >= 3.12.4(修复 CVE-2024-34344)
- [ ] Devtools 版本 >= 1.3.9(修复 CVE-2024-23657)
- [ ] 在 nuxt.config 中配置 CSP 头部
- [ ]
runtimeConfig.public中无秘密 - [ ] 所有用户输入使用 DOMPurify 清理
- [ ] 服务器路由使用 Zod 模式验证
- [ ] 受保护路由使用认证中间件
- [ ] 在生产中禁用开发工具
构建验证
- [ ]
npm audit显示无高/关键漏洞 - [ ] TypeScript 编译通过
- [ ] 所有安全测试通过
- [ ] 生产构建无错误完成
14. 总结
此 Vue/Nuxt 技能提供构建 JARVIS AI 助手 HUD 界面的安全模式:
- 安全第一:始终清理输入,在服务器上验证,使用 CSP 头部
- 类型安全:全程使用 TypeScript,带严格验证模式
- 性能:组合 API,懒加载,高效响应式
- 可维护性:清晰的组件结构,可组合项用于重用
记住:JARVIS HUD 处理敏感系统数据。每个组件必须将用户输入视为潜在恶意,并验证所有数据边界。
参考资料:
references/advanced-patterns.md- 复杂组件模式references/security-examples.md- 详细安全实现