| name | vue-component-refactoring |
| category | software-development |
| description | Refactor giant Vue single-file components (>500 lines) into maintainable structure using composables and sub-components. Covers blueprint-first analysis, extraction patterns, and verification steps for Vue 3 / Uni-app projects. |
Vue Component Refactoring Skill
Use when encountering Vue SFCs exceeding 500 lines (especially 1000+ line "monster" files).
Trigger Conditions
- Single
.vue file exceeds 500 lines
- 50+ methods/functions in one
<script> block
- Mixed API styles (Options API + Composition API in same file)
- Multiple responsibilities in one component (data, UI, interaction, API calls)
Refactoring Protocol
Phase 1: Blueprint-First Analysis (NO CODE CHANGES)
-
Read the full file and map its structure:
- Template line range
- Script line range
- Style line range
- Count: functions, refs, computeds, watches, console.logs
-
Identify logical boundaries for extraction:
- Data/Grid generation →
useComponentData.js
- Status/State calculations →
useComponentStatus.js
- Click/Interaction handlers →
useComponentInteraction.js
- Drag/Drop/Animation →
useComponentDrag.js
- API calls → keep in interaction composable or separate
useComponentApi.js
-
Identify UI sub-components:
- Complex popup →
ComponentPopup.vue
- Repeated v-for items →
ComponentItem.vue
- Complex cards →
ComponentCard.vue
-
Design Props/Emits flow:
- Parent → Child: data, state flags, configuration
- Child → Parent:
@confirm, @cancel, @update:show, custom events
-
Output blueprint and wait for [APPROVE] before coding.
Phase 2: Step-by-Step Execution
Order of creation:
composables/useComponentData.js — data generation
composables/useComponentStatus.js — computed state
composables/useComponentInteraction.js — event handlers
composables/useComponentDrag.js — (if applicable)
- UI sub-components (popups, cards, items)
- Final: Rewrite main component's
<script> section
File creation pattern (use Python scripts for bulk creation):
import os
BASE = os.path.expanduser("~/project/src/composables")
os.makedirs(BASE, exist_ok=True)
with open(os.path.join(BASE, 'useComponentData.js'), 'w') as f:
f.write(content)
Phase 3: Main Component Rewrite
Strategy: Replace ONLY the <script> section, preserve <template> and <style> unchanged.
template = lines[:script_start]
style = lines[style_start:]
new_script = '''<script setup>
import { useComponentData } from '../composables/useComponentData'
import { useComponentStatus } from '../composables/useComponentStatus'
// ... more imports
const props = defineProps({...})
const emit = defineEmits([...])
const { ... } = useComponentData(props)
const { ... } = useComponentStatus()
// Keep template-referenced functions/refs here
defineExpose({...})
</script>'''
new_content = f'{template}\n{new_script}\n{style}'
Phase 4: Verification
-
Syntax balance check:
open_braces = script_content.count('{')
close_braces = script_content.count('}')
assert open_braces == close_braces
-
Template ref audit:
- Extract all variable references from template (
{{ var }}, :prop="var", v-if="var")
- Verify each referenced variable exists in the new script
-
Critical checks:
defineExpose preserved (for parent component access)
provide/inject pairs preserved
- All event handlers wired correctly
- Props types match original
Pitfalls
- DO NOT change template logic in first pass — preserve DOM structure, only replace script
- DO NOT delete business logic — API parameters, role checks, type judgments must remain
- Watch for
uni.$emit/$on — ensure event bus calls are preserved or migrated properly
cloneDeep usage — if drag/drop uses deep clone, keep it in the drag composable
provide/inject — keep in main component, don't move to composables
- Mixed API style — if original uses
data() + methods(), convert ALL to Composition API (ref/computed) in the refactor
- console.log cleanup — remove debug logs during refactor (use conditional logger if needed)
Target Structure After Refactor
components/
├── ComponentName.vue [~300 lines] main assembly layer
├── ComponentPopup.vue [~100-300 lines] extracted popup
└── ComponentCard.vue [~100-200 lines] extracted card
composables/
├── useComponentData.js [~100-250 lines]
├── useComponentStatus.js [~150-400 lines]
├── useComponentInteraction.js [~150-450 lines]
└── useComponentDrag.js [~100-200 lines] (optional)
Success Metrics
- Main component reduced to <400 lines
- Each composable has single responsibility
- All template refs resolve correctly
- Business logic unchanged (verified by test or manual check)
- Zero console.log remaining in production code