plugins/languages/typescript/skills/vue/SKILL.md
TypeScript / JavaScript Vue 3.5+ 开发规范,覆盖 Composition API + `<script setup lang="ts">`、Vapor Mode 高性能编译、defineProps 解构 + reactive props、useTemplateRef、defineModel、onWatcherCleanup、Pinia 2 setup-store、Nuxt 4 全栈、Vue Router 4 懒加载、VueUse 12、Volar / vue-tsc 类型推断。Use when 开发 Vue 组件、SFC、composables、stores、Nuxt 页面,或用户提到 "Vue"、"Composition API"、"Pinia"、"Nuxt"、"script setup"、"reactive"、"ref"、"computed"、"SFC"、"v-model"。
npx skillsauth add lazygophers/ccplugin typescript-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.
本 skill 同时覆盖 JavaScript 项目;示例以 <script setup lang="ts"> 为主,JS 项目去掉 lang="ts" 与类型即可。
typescript-core — ESM/Vite/Biometypescript-async — AbortController + onUnmountedtypescript-security — v-html + DOMPurify<script setup lang="ts"> (默认)<script setup lang="ts">
import { ref, computed, onMounted, useTemplateRef } from 'vue';
interface User { id: string; name: string }
const users = ref<User[]>([]);
const query = ref('');
const loading = ref(true);
const filtered = computed(() =>
users.value.filter(u => u.name.includes(query.value))
);
const inputRef = useTemplateRef<HTMLInputElement>('input');
onMounted(async () => {
try {
const r = await fetch('/api/users');
if (!r.ok) throw new Error(`HTTP ${r.status}`);
users.value = await r.json();
} catch (e) {
console.error(e);
} finally {
loading.value = false;
inputRef.value?.focus();
}
});
</script>
<template>
<input ref="input" v-model="query" />
<p v-if="loading">Loading…</p>
<UserList v-else :users="filtered" />
</template>
<script setup lang="ts">
// defineProps 解构 — 解构后仍响应式 (3.5+)
const { name, count = 0 } = defineProps<{
name: string;
count?: number;
}>();
// useTemplateRef — 类型安全的 ref (3.5+, 代替字符串 `$refs`)
const list = useTemplateRef<HTMLUListElement>('list');
// defineModel — 双向绑定 (3.4+)
const value = defineModel<string>({ required: true });
// onWatcherCleanup (3.5+) — 替代 watch 内的 onCleanup 参数
import { watch, onWatcherCleanup } from 'vue';
watch(id, async (newId) => {
const ctrl = new AbortController();
onWatcherCleanup(() => ctrl.abort());
const r = await fetch(`/api/u/${newId}`, { signal: ctrl.signal });
});
</script>
无虚拟 DOM、直接生成命令式代码,性能近似 Solid。适合性能敏感页面或微前端组件。
// vite.config.ts
import vue from '@vitejs/plugin-vue';
export default {
plugins: [vue({ features: { vaporMode: true } })],
};
<script setup lang="ts" vapor>
// 该 SFC 编译为 Vapor 模式
</script>
// composables/useFetch.ts
import { ref, watchEffect, onScopeDispose, type Ref } from 'vue';
export function useFetch<T>(url: string | (() => string)) {
const data = ref<T | null>(null);
const error = ref<Error | null>(null);
const loading = ref(true);
let ctrl: AbortController | undefined;
watchEffect(async () => {
ctrl?.abort();
ctrl = new AbortController();
loading.value = true;
try {
const u = typeof url === 'function' ? url() : url;
const r = await fetch(u, { signal: ctrl.signal });
if (!r.ok) throw new Error(`HTTP ${r.status}`);
data.value = await r.json() as T;
} catch (e) {
if ((e as Error).name !== 'AbortError') error.value = e as Error;
} finally {
loading.value = false;
}
});
onScopeDispose(() => ctrl?.abort());
return { data, error, loading };
}
// stores/user.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useUserStore = defineStore('user', () => {
const current = ref<User | null>(null);
const list = ref<User[]>([]);
const isLoggedIn = computed(() => !!current.value);
async function login(creds: { email: string; password: string }) {
const r = await fetch('/api/login', { method: 'POST', body: JSON.stringify(creds) });
current.value = await r.json();
}
function logout() { current.value = null; }
return { current, list, isLoggedIn, login, logout };
});
// nuxt.config.ts
export default defineNuxtConfig({
compatibilityDate: '2025-01-01',
future: { compatibilityVersion: 4 },
modules: ['@pinia/nuxt', '@vueuse/nuxt'],
experimental: { typedPages: true },
});
<!-- pages/users/[id].vue -->
<script setup lang="ts">
const route = useRoute();
const { data: user, error } = await useFetch(`/api/users/${route.params.id}`);
</script>
// server/api/users/[id].get.ts — Nitro server route
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: '/u/:id', component: () => import('./pages/User.vue'), props: true },
],
});
router.beforeEach((to) => {
const store = useUserStore();
if (to.meta.requiresAuth && !store.isLoggedIn) return { name: 'login' };
});
把 <script setup lang="ts"> 改回 <script setup>,删去类型注解;其它写法(API、composable 结构、Pinia setup store)全部一致。Volar 在 <script setup> 中亦能通过 JSDoc 提供类型提示:
<script setup>
import { ref, computed } from 'vue';
/** @type {import('vue').Ref<User[]>} */
const users = ref([]);
</script>
| 现象 | 应改 | 严重 |
|------|------|------|
| Options API 新代码 | Composition API + <script setup> | 高 |
| Vuex | Pinia 2 setup store | 中 |
| 字符串 ref="..." + $refs | useTemplateRef | 中 |
| Mixin | composable | 高 |
| v-html 无清理 | DOMPurify | 高 |
| watch 内未清理副作用 | onWatcherCleanup | 中 |
| 路由组件不 lazy | () => import(...) | 中 |
| Vue 2 语法 (Vue.extend 等) | Vue 3.5 | 高 |
<script setup> + Composition API (TS 项目用 lang="ts")defineProps 解构 (响应式)useTemplateRef 而非字符串 refonUnmounted / onScopeDispose 清理 AbortController / intervalv-html 经 DOMPurifytools
--- name: trellisx-workspace description: 维护 `.trellis/task.md` 任务看板 —— trellis 缺的跨任务总览。**一个表格, 一行一个任务**, 列为 id/名称/描述/状态/阶段/进度/worktree (状态/阶段中文显示)。在 task create/start/阶段切换/archive 后**及时更新**对应行; 并**自动清理超 7 天的已完成行**防膨胀。保持看板与 task.json 实时一致。 when_to_use: 维护 / 创建 / 更新 `.trellis/task.md` 任务看板时; task 生命周期任一节点 (create/start/阶段推进/archive) 之后同步看板时; 用户问"当前有哪些任务 / 任务进度 / 任务看板"时。被 trellisx-flow 与 trellisx-apply 注入的流程引用。 user-invocable: true argument-hint: [show|update|sync|cleanup ...] [task id] arguments:
testing
强制以 Trellis task 闭环处理用户指定的请求 (自判新建/并入 → plan→exec→check→finish 全程不跳步)。**仅用户显式主动调用** (/trellisx-flow 或明确要求"强制走 task 处理这个"); **禁止自动 / 被动 / 推断式调用** —— 不要因为某个请求"看起来该建 task"就自动触发本 skill, 那是 apply 注入的 no_task 倾向的职责。
testing
把 强推task + subtask拆分 + worktree隔离 + 闭环收尾 四维度增量注入当前项目 .trellis/ (workflow.md 的 no_task/planning/in_progress 块 + spec 背书文档 + trellis 生命周期 hook worktree 自动化)。强推 task 与闭环为纯 prompt 软约束 (非平台 hook 硬拦截)。**纯增量追加, 绝不替换 trellis 原生文本** (no_task 分类+征同意/check/finish/前缀全保留)。幂等 (marker 包裹)。
development
Claude Code 会话历史整理 — 扫 ~/.claude/projects/**/*.jsonl 全部 session transcripts, 提取学习增量 (用户校正/决策/踩坑/L0 规则) → 全局记忆库 ~/.cortex/.wiki/memory/. 默认 --apply 落盘 (--dry-run opt-in 仅出 JSON plan 预览). 与 cortex-extract (L4-inbox 内部) 互补.