ECC/vue-review全流程实战:Vue 3 代码审查的响应式、Composable、模板安全与性能检查清单
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
/vue-review是 ECC(The agent harness performance optimization system)提供的 Vue 专项代码审查命令,它负责调用vue-reviewer智能体,围绕 Composition API 正确性、响应式机制、composable 模式、模板安全、无障碍(a11y)与 Vue 特有性能问题展开审查。无论你正在合并 Vue PR、审计模板v-html/URL 绑定的安全性,还是验证一个新 composable 与 Pinia store 的正确性,本文都基于 commands/vue-review.md 及其配套源码,给出可复现的命令、分级检查清单与验收标准。读完你既可以独立完成一次/vue-review会话,也能把 CRITICAL/HIGH/MEDIUM 三级检查项固化进团队自己的 Vue 工程化流程。
命令做什么:一次/vue-review的完整生命周期
/vue-review不是"意见汇总",而是编排了一条可审计的流水线,文档中定义的五个步骤缺一不可:
- 识别 Vue 变更:通过
git diff找出修改过的.vue文件,以及引入 Vue 依赖的.ts/.js文件; - 运行 Lint:以
eslint-plugin-vue执行eslint(工程如使用 flat config,应覆盖vue/vue3-recommended规则集); - 类型检查:运行
vue-tsc --noEmit或工程规范化的 typecheck 命令; - 只审查 Vue 专属车道:响应式(reactivity)、composable、模板安全、无障碍、Vue 特有性能;
- 生成分级报告:按 CRITICAL / HIGH / MEDIUM 三个严重级别归类问题,输出结论。
对应的智能体定义位于 agents/vue-reviewer.md,其工具权限为Read, Grep, Glob, Bash,并且有明确的约束:只报告问题、不做重构改写。在拉取 PR 审查时,vue-reviewer 会先通过gh pr view --json baseRefName确认真实基线分支(不硬编码main),并在合并状态为红灯或存在冲突时先停手上报。这一"先确认仓库状态、再开始审查"的设计,保证了报告不会建立在一个过期的 diff 之上。
何时使用:适用范围与边界判断
文档明确列出适用场景:
- PR 或 commit 触及
.vue文件; - 编写或修改 Vue 组件、composable 或 Pinia store 之后;
- 合并 Vue 代码之前;
- 审计模板安全(
v-html、URL 绑定); - 审查新 composable 的正确性;
- 审计 Vue Router 守卫与导航;
- 审查 Nuxt 服务端路由或 SSR 专属代码。
同时它划定了边界:纯.ts/.js变更且不引入任何 Vue 时,应改用/code-review(通用未提交变更/PR 审查)或直接调用typescript-reviewer。这一点在 agents/vue-reviewer.md 中也被复述为运行协议的一部分——先建立 diff 范围,若无.vue或 Vue 相关变更,则把现场交给typescript-reviewer并停止。
在 ECC 的命令注册与智能体路由中也保留了这个名称:可以在 COMMANDS-QUICK-REF.md 查到它的定位说明("Vue.js — Composition API correctness, reactivity, composable patterns, template security, accessibility, performance"),在 agent.yaml 中它同样作为命令入口被挂载。
与/code-review、TypeScript 审查的分工
Vue 工程通常同时存在.vue与大量.ts/.js,为避免重复审查与结论冲突,ECC 通过"车道(lane)"模型切分责任,vue-review 文档给出如下对照:
| 工具 | 审查范围 |
|---|---|
vue-reviewer(即本命令) | 响应式、composable、模板安全、a11y、Vue 性能、Pinia/Router |
typescript-reviewer | 通用 TS/JS——any滥用、async 正确性、Node 安全 |
security-reviewer | 项目级安全审计 |
/code-review | 通用未提交变更或 PR 审查 |
在 agents/vue-reviewer.md 中这张分工表更细化为逐行对照:any滥用、as断言、strict-null 违例、Promise/async 正确性、innerHTML泛化 XSS 归typescript-reviewer;而响应式正确性、v-html/模板注入、composable 规则、props/emits/slots 契约、Vue Router 守卫、Pinia 模式、无障碍、v-memo/shallowRef/v-once、SSR 安全与v-forkey 稳定性则全部归vue-reviewer。因此文档要求:凡触及.vue的 PR,同时调用两个审查器,二者结论按设计不重叠。
审查类别与分级检查清单(可复制的核心资产)
vue-review.md 将检查项收敛为三档。下面把命令文档与 vue-reviewer 智能体文档中的细节合并为可直接当 checklist 使用的清单。
CRITICAL(必须修复)
| 检查项 | 为什么致命 |
|---|---|
v-html拼接未消毒的用户输入 | Vue 的文本插值{{ }}与属性绑定(:title)会自动转义,但v-html绕过转义,等价于 React 的dangerouslySetInnerHTML,是直接 XSS 向量 |
:href/:src绑定未校验的用户 URL | javascript:与data:协议会直接执行代码;只应放行http/https/mailto |
客户端 bundle 泄露密钥(VITE_*、NuxtpublicruntimeConfig) | 凡import.meta.env.VITE_*与useRuntimeConfig().public内容都会随浏览器下发,任何 XSS 都可读取 |
| Nuxt Nitro 服务端端点缺少输入校验 | server/api/、server/routes/直接消费 body/query/params 而不做 schema 校验(如 zod/valibot) |
用localStorage/sessionStorage存会话令牌 | 存储对一切 XSS 可见,应使用 httpOnly Cookie |
| Vue < 3.5 解构响应式 props | const { title, count } = defineProps(...)拿到的是快照副本,不再响应;应改用toRefs()或经props.xxx访问 |
整体替换reactive()对象 | state = newState会破坏响应性(watcher 失效),应就地改属性或用Object.assign(state, newState) |
watcher 源跟踪 ref 对象而非.value | watch(() => myRef, ...)监听的永远是这个没变的 ref 容器;必须是watch(() => myRef.value, ...) |
补充两个与版本相关的响应式要点(来自 vue-reviewer 智能体与 skills/vue-patterns/SKILL.md):Vue 3.5+ 已默认稳定启用 Reactive Props Destructure,解构变量自动响应,但不能直接watch(count, ...),会触发编译错误,必须用 getter 包装watch(() => count, ...);reactive()只对对象/数组有效,原始值请用ref()。
HIGH(应当修复)
Composable 类
- 模块作用域副作用:在
setup/组件生命周期外初始化状态、启动定时器、订阅事件,副作用会在所有组件实例间共享; - 缺少清理:composable 内的
watch、watchEffect、事件监听、interval、fetch 请求,必须在返回的 teardown 或onUnmounted中清理(Vue 3.5+ 可用onWatcherCleanup); - 接收响应式 ref 却只读一次
.value存快照,源变化不再传导; - 返回普通对象/原始值而非
ref/reactive/computed,消费方失去响应性; - 未以
use前缀命名,破坏 lint 检测与 Vue 约定。
模板安全与正确性类
v-for无:key;或:key="index"——重排/增删时状态挂到错误行,应用稳定数据库 ID;- 同一元素上同时使用
v-if+v-for——v-if对每一项先求值,几乎总是逻辑错误,应改用<template v-for>内嵌v-if或 computed 过滤列表; v-model绑定到无 setter 的 computed——用户输入被静默丢弃,必须同时提供get与set;v-bind="$attrs"却未设inheritAttrs: false——属性同时落到根元素与被转发目标上。
组件架构类
- 单文件组件超过 300 行(template+script),应拆分;
- 直接修改 props(Vue 开发模式会告警),应通过
defineEmits上抛或用v-model双向绑定; - 缺少 prop 校验(至少要有
type,按需required/default); - 事件用 camelCase 命名(模板中建议 kebab-case);
- 用
document.querySelector/裸ref操作 DOM——应使用 template ref +useTemplateRef。
Vue Router / Pinia / SSR(Nuxt)类
- 路由守卫(
beforeEnter、beforeEach)返回false却不提供跳转替代,用户被卡死,应重定向或给出原因; - 在 setup 顶层解构
useRoute().params——同一组件内路由参数变化时解构拿到的是快照,应经toRefs(useRoute().params)或computed(); - Pinia 复杂业务变更散落在组件中——应收敛进 actions 或用分组
$patch(),保持 devtools 时间线与状态流转可读;在 Pinia state 中存储非可序列化数据(SSR 水合与持久化会丢数据); - Nuxt 场景:无
process.client守卫或onMounted直接用浏览器 API(window/document/localStorage会让服务端构建崩溃);useAsyncData/useFetch缺key导致重复请求、缓存去重失效;用<ClientOnly>包裹 SEO 关键内容;漏写definePageMeta。
MEDIUM(视情况考虑)
- 新代码仍用 Options API / Mixins(Vue 3 生态已标准化于 Composition API 与 composable);
- 组件超过 300 行、
v-show与v-if使用场景倒置(频繁切换用v-show,稀有/昂贵渲染用v-if); <KeepAlive>缺:max,缓存无界增长;- 大体积整体替换数据未用
shallowRef(ref()的深层响应化对整块替换的大数组/大对象开销高); - 非平凡表单手写校验而非 VeeValidate/FormKit 等成熟方案;
defineExpose暴露超出必要;- 使用
v-bind="$attrs"时未关闭inheritAttrs; - 有状态内容误用
v-once导致显示陈旧;v-memo用在很少变化的列表(先 profile 再上); - 表单缺
<form>+@submit.prevent、<select>的v-model缺:value、防抖用手写setTimeout而非useDebounceFn; - Vue 3.5+ 用普通 ref 做模板引用,而非支持动态 id、类型更安全的
useTemplateRef。
自动化检查命令:可复制的执行集
vue-review 文档给出与 vue-reviewer 智能体几乎一致的诊断命令,接入 CI 或本地均可直接复用:
# Lint(必跑) npx eslint . --ext .vue,.ts,.js # Vue 专属类型检查 vue-tsc --noEmit # 定向安全规则 npx eslint . --rule 'vue/no-v-html: warn' \ --rule 'vue/no-template-target-blank: error' # 供应链检查 npm auditvue-reviewer 智能体还补充了三条按项目约定收口的命令以及常用强化规则:
npm run typecheck --if-present # 尊重项目规范化命令 npx eslint . --rule 'vue/multi-word-component-names: error' npx eslint . --rule 'vue/require-default-prop: warn' npx prettier --check .在 hooks 侧,rules/vue/hooks.md 给出了工程化落地建议:*.vue、*.ts、*.tsx文件保存后(PostToolUse)先按改动文件执行eslint --fix与prettier --write,最后再跑项目级vue-tsc --noEmit(需要防抖/限定范围,避免每次击键保存拖垮编辑器)。要点是普通tsc读不懂.vueSFC,Vue 工程必须用vue-tsc;lint 走 flat config 的vue/vue3-recommended,格式化建议"Prettier 走 ESLint"避免双重格式化互搏。
文档还提示:若工程未配置eslint-plugin-vue或vue-tsc,审查会把该缺口标为 HIGH 配置问题并继续执行,而不是中断整场审查。
一次真实的报告长什么样
vue-review.md 给出了完整的对话式示例,值得原样复刻成团队模板:
User: /vue-review Agent: # Vue Code Review Report ## Files Reviewed - src/components/UserCard.vue (modified) - src/composables/useUser.ts (new) - src/stores/useUserStore.ts (modified) ## Lint Results PASS: eslint clean PASS: vue-tsc clean ## Issues Found [CRITICAL] Unsanitized v-html File: src/components/UserCard.vue:15 Issue: User-controlled bio rendered as raw HTML via v-html. Why: XSS via stored script tags in user input. Fix: Sanitize with DOMPurify or render as text:对应的修复示范(vue-review.md 原例):
<script setup> import DOMPurify from "dompurify"; const safeBio = computed(() => DOMPurify.sanitize(user.bio)); </script> <template> <div v-html="safeBio" /> </template>v-html的约束在 rules/vue/security.md 中有更完整表述:Vue 只会自动转义文本插值{{ }}与动态属性绑定(如:title),下列向量不受保护——绝不把不可信内容当组件模板、禁止运行时的用户输入模板编译、警惕用户控制的:is;即使改用渲染函数或 scoped slot,把用户 HTML 传入h()的innerHTML仍是换皮v-html,必须先消毒;:style绑定用户输入同样危险(CSS 数据渗出),应使用白名单属性的对象语法。推荐的工程惯例是在持久化前由后端完成消毒。
第二个 HIGH 示例(composable watcher 清理):
watch(userId, async (newId, _old, onCleanup) => { const controller = new AbortController(); onCleanup(() => controller.abort()); const data = await fetch(`/api/users/${newId}`, { signal: controller.signal }); user.value = await data.json(); });Vue 3.5+ 还提供了可在任意位置全局导入的onWatcherCleanup(),但它必须在 watcher 回调内同步调用:
import { watch, onWatcherCleanup } from "vue"; watch(userId, async (newId) => { const controller = new AbortController(); onWatcherCleanup(() => controller.abort()); // ... fetch with signal });报告结尾汇总三档计数并给出结论。vue-reviewer 智能体要求的每条 issue 固定四要素——[SEVERITY] 标题、File: 文件路径:行号、Issue: 一句话描述、Why: 影响、Fix: 具体改法,并附上总结表(Severity / Count / Status / Verdict),确保问题可定位、可评审、可回溯。
审批口径与团队落地
vue-review 的验收标准非常干脆:
| 状态 | 条件 |
|---|---|
| PASS: Approve | 无 CRITICAL 或 HIGH 问题 |
| WARNING: Warning | 仅剩 MEDIUM 问题(谨慎合并) |
| FAIL: Block | 发现 CRITICAL 或 HIGH 问题 |
与其它命令的衔接顺序(供 CI 编排或手工流程复用):构建坏了先修构建 → 跑测试确认组件测试通过 → 合并 Vue 代码前跑/vue-review→ 同一 PR 的非 Vue 关注点交给/code-review。
把 vue-review 的等级体系倒推进工程本身,就是一套可内化的质量门禁(quality gate):CRITICAL 对应 XSS、密钥泄露与响应性破坏,属于合并红线;HIGH 大多可用 lint 规则半自动拦截——这正是 rules/vue/coding-style.md 的价值,它把同一套纪律写成可执行的 SFC 结构规范(<script setup>→<template>→<style scoped>固定顺序)、响应式纪律(脚本内经.value变更、reactive对象不可整体重赋值、不经toRefs/storeToRefs的解构会静默丢响应性)、computed/watcher 纪律(computed 必须纯函数、watch 传 getter 而非裸对象、watchEffect 在首个await后停止追踪依赖)以及宏与模板纪律(每个v-for都要稳定唯一原始值 key、绝不同元素混用v-if+v-for)。
审查之外的自我延伸
vue-review.md 末尾给出了四条延伸路径,全部在仓库内可继续深挖:
- Agent:agents/vue-reviewer.md——上文所有分级清单的完整源头,附 Prompt Defense Baseline(抵御越权/注入类提示词攻击)与输出格式契约;
- Companion agent:agents/typescript-reviewer.md——Vue 相关 TS/JS 审查时并行运行;
- Skills:skills/vue-patterns/SKILL.md——Vue 3 Composition API 正向模式库,含 Pinia setup store 全量示例、Router 守卫与响应式路由参数、Nuxt
useAsyncData/useFetchkey 去重、NuxtruntimeConfig服务端/客户端切分、Vue 3.5+ 新 API(reactive props destructure、useTemplateRef、onWatcherCleanup、useId、<Teleport defer>、hydrateOnVisible惰性水合); - Rules:rules/vue/ 目录——security.md、hooks.md、patterns.md(composable 输入用
MaybeRefOrGetter统一toValue归一、vue-query 的 query key 必须放 ref 本体而非.value等反直觉要点)、coding-style.md、testing.md,共同构成可被 hook 在每次文件保存后自动触发的守门员。
换句话说,/vue-review是 ECC 把"资深 Vue 工程师审查心智"工程化的结果:命令定义流程、智能体给出分级专业判断、rules 把纪律变成 hook、skills 给出正面范式。实际使用只需在对话中发起/vue-review,然后在合并前依 PASS / WARNING / FAIL 三档收敛即可——它不替你写代码,但能让你带着同一套标准,把每次 Vue 合并的 XSS、响应性事故与隐藏性能债挡在 main 之外。
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考