# Vue Giant File Refactoring

> Refactor Vue 3 giant files (>500 lines) using Blueprint-First Mode. Extract composables and UI sub-components while preserving 100% business logic.

- Skill: `ichichuang/vue-giant-file-refactoring` (Agent Skill)
- Install (CLI): `npx skillmds add ichichuang/vue-giant-file-refactoring`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ichichuang/vue-giant-file-refactoring/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: ichichuang (https://skillmd.com/u/ichichuang)
- Updated: 2026-08-19
- Page: https://skillmd.com/skills/ichichuang/vue-giant-file-refactoring

---


# 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:
1. **Composables Extraction Matrix**: List each proposed composable, its responsibilities, input/output props, and key functions.
2. **UI Sub-Components**: List each proposed dumb component with props/emits signatures.
3. **File Structure**: Show the target directory layout.
4. **State Flow Diagram**: ASCII diagram showing data flow between composables and components.
5. **Risk & Constraints**: Explicitly state what MUST be preserved (provide/inject, specific API params, business logic).
6. **Wait for [APPROVE]** from the user before writing any code.

## Phase 2: Step-by-Step Execution
After approval, execute in strict order:
1. Create composables (data → state → interaction → layout)
2. Create UI sub-components
3. Refactor main file (replace script with composable imports, keep template & styles intact initially)
4. 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.log` statements, keep `console.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 generation
- `use*Interaction.js`: Click routing, API calls, modal state
- `use*Layout.js`: DOM measurements, drag/drop, responsive calculations
- `use*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:
1. **Same semantic meaning** → Reuse (e.g., `useCourseTable` utility functions shared between student/teacher)
2. **Same interface, different logic** → Do NOT reuse; create feature-specific version (e.g., `useTeacherTableData` vs `useCourseTableData` — `isMyTimeSlot` logic is fundamentally different)
3. **Forced reuse creates spaghetti code** → Separate composables, share utility functions instead

## Uni-app / WeChat Mini Program Specific Patterns
- **Cross-component data flow**: Use `provide/inject` for deeply nested table/grid data instead of prop-drilling. Define keys in `constants/inject-keys.js` (e.g., `SCHEDULE_DATA_KEY`).
- **Lifecycle safety**: `onShow` fires on every page revisit; `onMounted` only once. Never duplicate heavy computations in both. Prefer `onShow` for state sync, `onMounted` for one-time setup.
- **Event bus cleanup**: Every `uni.$on('event', handler)` MUST have a matching `uni.$off('event', handler)` in `onUnmounted`. 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:
1. Check if a class already exists with similar rules
2. If not, add the CSS rule to the component's `<style>` block
3. **Critical**: Verify the class name isn't already used elsewhere in the template before repurposing (e.g., `grid-canvas-viewport` vs `grid-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**: 
```python
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**: 
```js
// 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**:
```bash
# 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>`:
```bash
# 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:
```python
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/debug` with `if (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.$on` have corresponding `uni.$off` in `onUnmounted`
- [ ] **401 Interceptor**: Ensure `response.js` handles `statusCode === 401` → clear token, show i18n toast, `uni.reLaunch` to login page
- [ ] **Environment Config**: Use `.env.development` / `.env.production` with `process.env.NODE_ENV`. Never hardcode `isTest = true` in production.
