Vue Giant File Refactoring Workflow
Trigger
When encountering Vue files >500 lines, especially complex page components or massive single-file components.
Phase 1: Blueprint-First Mode
DO NOT MODIFY CODE. Output a detailed architectural blueprint containing:
- Composables Extraction Matrix: List each proposed composable, its responsibilities, input/output props, and key functions.
- UI Sub-Components: List each proposed dumb component with props/emits signatures.
- File Structure: Show the target directory layout.
- State Flow Diagram: ASCII diagram showing data flow between composables and components.
- Risk & Constraints: Explicitly state what MUST be preserved (provide/inject, specific API params, business logic).
- Wait for [APPROVE] from the user before writing any code.
Phase 2: Step-by-Step Execution
After approval, execute in strict order:
- Create composables (data → state → interaction → layout)
- Create UI sub-components
- Refactor main file (replace script with composable imports, keep template & styles intact initially)
- Output completion report with line-count comparison.
Critical Rules
- Zero Business Logic Changes: API parameters, role checks, and data processing must be identical.
- Console Cleanup: Remove debug
console.logstatements, keepconsole.warn/error. - Preserve External Contracts:
defineExpose,provide, event listeners (uni.$on/$off) must remain functional. - Style Preservation: Keep CSS in main file initially; extract only if component becomes fully self-contained.
- Verification Note: Always end with simulator verification checklist.
Composable Patterns
use*Data.js: Data fetching, filtering, grid generationuse*Interaction.js: Click routing, API calls, modal stateuse*Layout.js: DOM measurements, drag/drop, responsive calculationsuse*Status.js: Complex computed properties, state machines, visual status calculation
Feature-Scoped Composable Placement
For uni-app / feature-based projects, place composables under the feature directory:
pages/course/
├── composables/ ← Feature-scoped (useCourseTableData.js, useTeacherCellInteraction.js)
├── components/ ← Feature-scoped sub-components
└── course.vue ← Main page
Do NOT place feature-specific composables in the global composables/ directory at project root. Only truly shared composables (like useI18n, useAuth) belong there.
Reuse Evaluation Matrix
Before creating a new composable, check if an existing one can be reused:
- Same semantic meaning → Reuse (e.g.,
useCourseTableutility functions shared between student/teacher) - Same interface, different logic → Do NOT reuse; create feature-specific version (e.g.,
useTeacherTableDatavsuseCourseTableData—isMyTimeSlotlogic is fundamentally different) - Forced reuse creates spaghetti code → Separate composables, share utility functions instead
Uni-app / WeChat Mini Program Specific Patterns
- Cross-component data flow: Use
provide/injectfor deeply nested table/grid data instead of prop-drilling. Define keys inconstants/inject-keys.js(e.g.,SCHEDULE_DATA_KEY). - Lifecycle safety:
onShowfires on every page revisit;onMountedonly once. Never duplicate heavy computations in both. PreferonShowfor state sync,onMountedfor one-time setup. - Event bus cleanup: Every
uni.$on('event', handler)MUST have a matchinguni.$off('event', handler)inonUnmounted. Memory leaks are silent but fatal in long mini-program sessions. - Template-first refactoring: When extracting from 3000+ line files, keep the
<template>and<style>blocks intact initially. Only replace<script>with composable imports. This prevents DOM breaking during the transition. - Dual-Role Strategy: When a feature has fundamentally different logic for different user roles (e.g., Student vs Teacher course tables), never use boolean flags (
isTeacher ? ... : ...). Create parallel composables (useStudentTableData.js,useTeacherTableData.js). Share only the pure utility layer.
Inline Style → CSS Class Migration Pattern
When converting inline style="..." to SCSS classes:
- Check if a class already exists with similar rules
- If not, add the CSS rule to the component's
<style>block - Critical: Verify the class name isn't already used elsewhere in the template before repurposing (e.g.,
grid-canvas-viewportvsgrid-x-container— both exist and serve different purposes)
Emergency Recovery Patterns (when refactoring breaks compilation)
1. The "Double Script Block" Trap
When migrating from Options API to <script setup>, patch might leave the old export default { ... } block intact after the new </script>, creating two script blocks.
Symptom: ViteError: Invalid end tag or Multiple default exports.
Fix:
lines = open("file.vue").readlines()
first_script_end = next(i for i, l in enumerate(lines) if "</script>" in l)
style_start = next(i for i, l in enumerate(lines) if "<style" in l)
# Keep everything up to </script>, and everything from <style> onwards
new_lines = lines[:first_script_end+1] + lines[style_start:]
open("file.vue", "w").writelines(new_lines)
2. Truncated <template> Block
Vite's SFC parser requires a strictly closed <template>. If the template is cut off or missing </template>, compilation fails instantly.
Symptom: [plugin:vite:vue] Element is missing end tag.
Fix: Manually reconstruct the closing tags (</template>, </view>, </ScheduleTable>) and ensure the template block ends exactly before <script setup>. Never use patch to rewrite an entire 3000+ line file; use write_file with fully assembled content, or surgical line-range deletions as above.
3. Composable Destructuring Alias Mismatch
When extracting logic to composables, the exported function names must exactly match the destructured names in the main file.
Symptom: TypeError: handleX is not a function or ReferenceError.
Fix:
// In composable:
return { loadMoreLessonHistory } // exported name
// In main file:
const { loadMoreLessonHistory: handleLoadMoreLessonHistory } = useComposable() // alias in template
Ensure the alias matches exactly what the <template> expects.
UI Component Resolution Check
Before writing templates for extracted sub-components, verify the exact component names exist in uni_modules/.
Symptom: Could not resolve "uni_modules/uview-pro/components/u-loading-icon/..."
Context: UI libraries (like uview-pro) often change component names across versions (e.g., u-loading-icon might be just u-loading). Blindly using names from old code or AI hallucinations breaks Vite.
Fix:
# Check if the component directory exists
ls uni_modules/uview-pro/components/ | grep loading
# Use the actual Vue component name found in the directory
Post-Refactor Verification (Critical)
After modifying the main file, always verify no orphaned code remains between </script> and <style>:
# Check for orphaned code after </script> (common when patching giant files)
grep -n "</script>" file.vue # Should appear exactly once
grep -n "<style" file.vue # Should appear exactly once
If </script> appears twice or there's code between </script> and <style>, the old script block wasn't fully replaced. Use a surgical line-range deletion to remove it:
lines = open("file.vue").readlines()
# Keep lines before first </script> + lines after <style>
This is especially critical when using patch on files >1000 lines — partial view edits can leave orphaned Options API code after the new <script setup>.
Production Hygiene Checklist (auto-apply during refactoring)
- Wrap all
console.log/info/debugwithif (isDev)condition (import from@/utils/http/config) - Add HTTP timeout (default 15s) + timeout error handling in
client.js - Replace hardcoded color values (
#4C83FF,#FF5A5A) with CSS variables (var(--chess-*)) - Fix font sizes < 24rpx → minimum 22rpx
- Add empty state (
u-empty) to all list components - Verify all
uni.$onhave correspondinguni.$offinonUnmounted - 401 Interceptor: Ensure
response.jshandlesstatusCode === 401→ clear token, show i18n toast,uni.reLaunchto login page - Environment Config: Use
.env.development/.env.productionwithprocess.env.NODE_ENV. Never hardcodeisTest = truein production.