plugins/languages/javascript/skills/vue/SKILL.md
Vue 3.5开发规范:Composition API组合式API、Vapor mode高性能模式、Pinia状态管理、Nuxt 4全栈框架。开发Vue组件、SFC单文件组件、响应式数据、路由页面时加载。
npx skillsauth add lazygophers/ccplugin plugins/languages/javascript/skills/vueInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Agent | 说明 | | ----- | ---- | | dev | JavaScript 开发专家 | | test | JavaScript 测试专家 |
| 场景 | Skill | 说明 | |------|-------|------| | 核心规范 | Skills(javascript:core) | ES2025-2026 标准、ESM、工具链 | | 异步编程 | Skills(javascript:async) | async/await、Promise | | 安全编码 | Skills(javascript:security) | XSS 防护、Zod 验证 |
<script setup><script setup>
import { ref, computed, onMounted, watch } from 'vue';
const users = ref([]);
const loading = ref(true);
const searchQuery = ref('');
const filteredUsers = computed(() =>
users.value.filter(user =>
user.name.toLowerCase().includes(searchQuery.value.toLowerCase())
)
);
onMounted(async () => {
try {
const response = await fetch('/api/users');
users.value = await response.json();
} catch (error) {
console.error('Failed to fetch users:', error);
} finally {
loading.value = false;
}
});
</script>
<template>
<input v-model="searchQuery" placeholder="Search users..." />
<div v-if="loading">Loading...</div>
<UserList v-else :users="filteredUsers" />
</template>
<script setup>
// defineProps 解构(Vue 3.5)- 保持响应式
const { name, count = 0 } = defineProps({
name: String,
count: { type: Number, default: 0 },
});
// useTemplateRef(Vue 3.5)- 类型安全的模板引用
import { useTemplateRef, onMounted } from 'vue';
const inputRef = useTemplateRef('input');
onMounted(() => {
inputRef.value?.focus();
});
</script>
<template>
<input ref="input" />
<p>{{ name }}: {{ count }}</p>
</template>
// composables/useUser.js
import { ref, watch } from 'vue';
export function useUser(userId) {
const user = ref(null);
const loading = ref(true);
const error = ref(null);
watch(userId, async (id) => {
if (!id) return;
loading.value = true;
error.value = null;
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
user.value = await response.json();
} catch (e) {
error.value = e;
} finally {
loading.value = false;
}
}, { immediate: true });
return { user, loading, error };
}
// composables/useAbortFetch.js
import { ref, onUnmounted } from 'vue';
export function useAbortFetch() {
let controller = null;
async function fetchData(url) {
controller?.abort();
controller = new AbortController();
const response = await fetch(url, { signal: controller.signal });
return response.json();
}
onUnmounted(() => controller?.abort());
return { fetchData };
}
// stores/user.js
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useUserStore = defineStore('user', () => {
// State
const users = ref([]);
const currentUser = ref(null);
// Getters
const activeUsers = computed(() =>
users.value.filter(u => u.isActive)
);
// Actions
async function fetchUsers() {
const response = await fetch('/api/users');
users.value = await response.json();
}
async function login(credentials) {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials),
});
currentUser.value = await response.json();
}
return { users, currentUser, activeUsers, fetchUsers, login };
});
// nuxt.config.js
export default defineNuxtConfig({
compatibilityDate: '2024-11-01',
future: { compatibilityVersion: 4 },
modules: ['@pinia/nuxt'],
});
// pages/users/[id].vue - 自动路由
<script setup>
const route = useRoute();
const { data: user } = await useFetch(`/api/users/${route.params.id}`);
</script>
// server/api/users/[id].get.js - Server API
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id');
return await db.users.findById(id);
});
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: () => import('./pages/Home.vue'), // 懒加载
},
{
path: '/users/:id',
component: () => import('./pages/UserDetail.vue'),
props: true, // 路由参数作为 props
},
],
});
// 导航守卫
router.beforeEach((to, from) => {
const store = useUserStore();
if (to.meta.requiresAuth && !store.currentUser) {
return { name: 'login' };
}
});
| 现象 | 问题 | 严重程度 |
|------|------|---------|
| Options API | 应使用 Composition API + <script setup> | 高 |
| Vuex | 应迁移到 Pinia | 中 |
| this.$refs | 应使用 useTemplateRef()(Vue 3.5)| 中 |
| Mixin | 应使用组合式函数替代 | 高 |
| v-html 无清理 | 必须使用 DOMPurify 清理 | 高 |
| 无 AbortController | 应在 onUnmounted 中取消请求 | 中 |
| Vue 2 语法 | 应迁移到 Vue 3.5 | 高 |
<script setup>defineProps 解构保持响应式useTemplateRef() 替代字符串 ref() => import())onUnmounted 清理 AbortController 和计时器v-html 必须配合 DOMPurifytools
UI/UX 与布局设计——做界面布局/结构/导航/组件/交互的设计决策。触发:做UI/UX/布局/排版/导航/组件/交互/栅格/响应式/图表选型/字体配对。按媒介路由 HTML/Web、原生 App(iOS/Android/桌面)、CLI、TUI。需后端动态系统不适用;配色/主题/色板走姊妹 skill design-color。
tools
主题与配色设计——做颜色搭配/调色板/主题/品牌色阶/暗模式的设计决策。触发:选配色/调色/主题/色板/品牌色/暗模式/对比度/色盲/UI风格。按媒介路由 HTML/Web(CSS变量)、原生App(平台token)、CLI(ANSI)、TUI(真彩/256/16降级)。保证可访问性(对比度/色盲安全)。需后端动态系统不适用;UI/UX 布局/组件/交互走姊妹 skill design-uiux。
tools
跨任意组件(plugin/skill/agent/command)的验证驱动优化循环纪律 skill。当用户要优化某个已有组件却无明确方向、或要防止改了反而更差(自评乐观偏差 / 多维同改归因失效 / 为凑分加废话膨胀)、或要把一套通用「评分→单变量改→改后验证严格更好才留否则回滚→触顶停」的纪律套到任意组件上时使用。管优化过程本身的纪律(validation gate / ratchet / 独立验证 / 触顶停),不评单组件深度(交 skill-dev),不查插件接线(交 plugin-dev)。仅手动 /optimize-any 触发。
data-ai
两层规则记忆 (基于 .skein/spec)。planning 时 recall 召回相关规则、task finish 后 sediment 沉淀学习 + prune 自动精简过期/重复/断链规则。core 常驻硬规 + recall 按需召回, 经判定门自动写盘 (不逐次问用户)。产出 .skein/spec 下 core/recall 规则文件 + index。另支持空仓 bootstrap 播种规则基线、记忆大面积失效 (大重构/换栈) 时 reconstruct 可逆归档后按项目类型分型重建、maintain 手动体检 (超预算/stale/断链/重复/废弃, --apply 自动修复)、auto-fix (Stop hook 写 .pending-fix 标记 → main 派 skein-specer bg 跑 maintain --apply 全自动修, 断链只报告)。