Behavior preserving refactor
Skill manman1414/frontend-refactor-skills/skills/behavior-preserving-refactor
React/Vue/TS 前端重构 Agent Skills(行为不变重构、拆文件、绞杀者迁移)
npx -y skills add manman1414/frontend-refactor-skills --skill behavior-preserving-refactorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Performs behavior-preserving frontend refactors in React, Vue 2/3, and TypeScript without changing UI or runtime semantics. Use when renaming components, extracting hooks/composables, simplifying JSX/templates, deduplicating props logic, inlining or extracting functions, or when the user asks for a safe refactor with no behavior change.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
5.8 KB, as published. Nobody here has run it
Behavior-Preserving Refactor (Frontend)
<!-- Author: Administrator | Date: 2026-07-05 -->Refactor structure only — zero behavior change. Applies to React (TS/TSX), Vue 2 (Options API), Vue 3 (Composition API + <script setup>).
When to use
- Rename component / prop / emit / slot
- Extract custom hook (
useXxx) or Vue composable (useXxx) - Simplify JSX or template conditions (equivalent logic only)
- Inline or extract pure helper functions
- Replace duplicated prop drilling with equivalent pattern (same data flow)
- Convert between equivalent TS patterns (type alias ↔ interface) without runtime effect
Pre-flight checklist
- [ ] Existing tests / Storybook / manual repro path identified
- [ ] Baseline: run typecheck + relevant tests before editing
- [ ] Scope written in one sentence: "Refactor X without changing Y"
- [ ] Framework detected: React | Vue 2 | Vue 3
Workflow
Step 1 — Establish baseline
Run project commands (adapt to repo):
npm run typecheck # or: vue-tsc --noEmit / tsc --noEmit
npm test -- --run # or: vitest run / jest --bail
For Vue 2 without tests: note key user flows (click path, v-model binding, emit payload shape).
Step 2 — Pick ONE refactor type
| Type | React | Vue 2 | Vue 3 |
|---|---|---|---|
| Rename symbol | Component, hook, prop | name, props, emit | defineProps, defineEmits |
| Extract logic | useXxx() hook | methods → mixin/composable* | composable in composables/ |
| Extract UI chunk | Child component, same props | Child SFC, $attrs/$listeners† | Child SFC, defineOptions + attrs |
| Simplify condition | Equivalent ternary / early return | Same in computed | Same in computed() |
| Dedupe | Shared util or hook | Shared mixin/composable | Shared composable |
* Prefer composable over mixin when touching Vue 2 — mixin only if project convention requires it.
† Vue 2.7+: prefer v-bind="$attrs" over $listeners.
Forbidden in this skill: API URL changes, v-if ↔ v-show swaps, key changes on lists, adding/removing watch side effects, React useEffect dependency changes.
Step 3 — Apply minimal diff
Rules:
- Move, don't rewrite — copy-paste first, rename second
- Preserve public surface — exported component name, prop names, emit names, slot names unchanged unless rename IS the task
- Keep keys stable — React
key, Vue:keyonv-forunchanged - Preserve reactivity — Vue: don't break
ref/reactiveunwrap; React: don't change hook call order
Step 4 — Verify equivalence
npm run typecheck
npm test -- --run
Manual spot-check (pick applicable):
- Same DOM structure for default props/state
- Same event payloads (
emit/onXxxcallback args) - Same loading / error / empty states
- Same CSS class names on root element (if tests snapshot classes)
Step 5 — Diff self-review
For every changed line, answer: "Does this change runtime behavior?"
- Yes → revert or split to a separate task
- No → keep
Framework notes
React + TypeScript
// Extract hook — behavior identical
// Before: logic inside component
// After:
function useOrderFilters(orders: Order[]) {
const [query, setQuery] = useState("");
const filtered = useMemo(
() => orders.filter((o) => o.name.includes(query)),
[orders, query]
);
return { query, setQuery, filtered };
}
- Do NOT add/remove hooks conditionally
- Do NOT change
useMemo/useCallbackdeps unless provably equivalent - Prefer
typefor props if file already usestype
Vue 2 (Options API)
- Keep
data()shape identical computedgetters must remain pure with same dependencies- When splitting components, pass equivalent props; use
.sync/v-modelsame as before - Preserve
$scopedSlots/ slot API when extracting child
Vue 3 (Composition API)
<!-- Extract composable — same refs returned -->
<script setup lang="ts">
import { useOrderFilters } from "@/composables/useOrderFilters";
const props = defineProps<{ orders: Order[] }>();
const { query, filtered } = useOrderFilters(() => props.orders);
</script>
defineProps/defineEmitstypes must match previous runtime contractwatch/watchEffect— do not add or remove in behavior-preserving refactors- Prefer
refvsreactiveconsistency with surrounding code
Output format
When reporting to the user (中文):
## 改动
- [文件] 做了什么结构变更
## 为什么
- 一句话说明 refactor 类型与收益
## 行为保证
- 哪些入口验证过(测试 / 手动路径)
## 风险
- 无 / 或列出需人工确认的边界
Anti-patterns
- ❌ "顺便"改样式、改文案、改 API 字段
- ❌ 一次 PR 里 rename + extract + 逻辑优化
- ❌ Vue 2 → Vue 3 语法迁移(用
strangler-refactor) - ❌ 无测试的大组件拆分后不跑 typecheck
Related skills
- File/package moves →
extract-and-move - Replace legacy implementation →
strangler-refactor - Tests before behavior change →
test-driven-development