vue-sfc-split 巨型 SFC 拆分

2026-07-13 02:000

定位

把体量过大的 Vue 单文件组件(动辄 800~2000 行的 .vue)拆成「平铺入口 .vue + 同级同名专属目录」,把输入面板、结果展示块、纯逻辑、scoped 样式各归其位,能复用的沉淀到项目公共目录,并保证路由身份、行为、lint 全部不变

对话里说「这个 vue 文件太大了」「超过一千行」「拆成组件」「页面变成目录」「把样式抽出去」「组件化 / 瘦身 / 模块化」等都会触发。

为什么要专门的 skill 来拆

在用了**文件路由(unplugin-vue-router)或组件自动导入(unplugin-vue-components)**的项目里,手拆极易踩这几个坑:

  • index.vue 撞路由名报错;
  • 子组件被误扫成路由,破坏路由树;
  • 丢了 definePage meta 导致菜单项消失;
  • 漏 import 导致运行时空白,而 lint 和 vue-tsc 静默通过(最阴险)。

先探测项目用了什么

动手前先确认,这决定哪些硬约束对你生效:

# 文件路由?(约束 1/2/3 生效)
grep -rn "unplugin-vue-router\|VueRouter(" vite.config.* 2>/dev/null
# 组件自动导入?(约束 4 生效)
grep -rn "unplugin-vue-components\|Components(" vite.config.* 2>/dev/null

两个都没有(手写路由表 + 手动 import)→ 只需遵守目标结构和决策表,硬约束可跳过。

目标结构

video-trim.vue 为例,拆后:

pages/
├── video-trim.vue                  ← 入口,平铺保留原地,不改文件名/位置
└── video-trim/                     ← 同级同名专属目录
    ├── composables/
    │   └── use-trim-range.ts        ← 仅本页面用的 hook
    ├── trim-args.ts                 ← 仅本页面用的纯函数/类型
    ├── video-trim-scoped.css        ← 从入口 <style scoped> 抽出
    └── components/                  ← 仅本页面用的子组件(.vue 必须在这里)
        └── trim-range-panel.vue

四条硬约束

  1. 入口必须平铺 <页面名>.vue,禁止 <页面名>/index.vue(仅文件路由)——否则被当成嵌套路由,触发「子路由不能与祖先路由同名」报错。
  2. 专属 .vue 必须放在 components/ 段下(仅文件路由)——文件路由通常配 exclude: ['**/components/**'],放在 components 外会被误扫成一条多余路由。.ts / .css 放专属目录任意位置都安全。
  3. 不能丢路由 / 菜单身份——入口里的 defineOptions({ name })definePage({ meta }) 是路由名 + 菜单标题 / 图标 / 排序来源,拆分只搬代码,这两块原样留在入口。
  4. 专属子组件不会被自动导入,必须显式 import(仅组件自动导入)——<页面名>/components/** 不在自动导入扫描范围,漏了 import 本地 lint / vue-tsc 静默通过,只有浏览器运行时报 Failed to resolve component + 该块空白。

拆分 ≠ 搬家,是分层 + 复用

不要把 1000 行原样切成 5 个文件了事。边拆边问三件事:

  1. 这块逻辑 / 组件,项目里是不是已经有公共件?→ 直接换掉,删掉自己这份;
  2. 这块会被 ≥2 个页面用吗?→ 沉淀到项目公共目录;
  3. 这块只有本页面用?→ 放专属目录 <页面名>/

先查复用:动手前 grep 一遍项目现有公共件目录(src/components/src/composables/src/utils/),别把已存在的东西又拆一份(文件大小格式化、下载触发、防抖节流这类最常被重复造)。

决策表

拆出来的东西复用范围落点
输入面板 / 结果块 / 配置区(.vue)仅本页面<页面名>/components/
同上≥2 个页面项目公共组件目录(自动导入范围内)
组合式逻辑(useXxx)仅本页面<页面名>/composables/use-xxx.ts
纯函数 / 类型 / 常量仅本页面<页面名>/xxx.ts
<style scoped> 样式仅本页面<页面名>/<页面名>-scoped.css,入口 import
设计 token / 通用卡片全局用项目已有的全局样式与 CSS 变量,别重写

验证

结构自检脚本(传平铺入口的路径):

bash ${CLAUDE_PLUGIN_ROOT}/skills/vue-sfc-split/scripts/check-sfc-split.sh <平铺入口.vue 的路径>

脚本会先探测项目是否用了文件路由,只对适用的约束报错。检查项:专属组件模板用了但没 import(❌ 最重要)、孤儿组件(⚠️)、抽出 css 未加页面前缀的通用类名(⚠️)、入口行数是否收敛;文件路由项目额外查无 index.vue、专属 .vue 都在 components/ 下、路由 / 菜单身份未丢。

自检脚本的漏 import 检查只覆盖专属目录内的组件,拆完仍建议在浏览器实际打开该页面,确认每个外移块正常渲染、控制台无 Failed to resolve component

行为零变更原则

拆分是纯搬运 + 接线,不顺手改逻辑、不「优化」算法、不调默认值。发现可改进点,先把拆分做完、验证通过,再单独提改进——把「重构结构」和「改行为」混在一起,出 bug 时无法判断是哪类改动引入的。

相关

💬留言板0

还没有留言,来发表第一条吧~