link,[object Object]
Skip to content

胖组件 vs 瘦组件 ​

是什么 ​

一个 .vue 文件里,模板、状态、请求、格式化、交互逻辑全塞在一起,就是胖组件;把逻辑抽进 composable / 工具函数,组件只剩「拿到数据 → 画出来 → 报告事件」,就是瘦组件。

这不是代码洁癖。赌的是六个月后的你还敢不敢改这个文件:胖组件每改一处都要在脑子里同时装下全部逻辑,瘦组件每次只装一段。

优劣对比 ​

胖组件瘦组件 + composable
上手速度⭐⭐⭐⭐ 快,全在一个文件⭐⭐ 要跳文件看逻辑
改动安全⭐ 牵一发动全身⭐⭐⭐⭐ 只碰相关那一段
复用⭐ 复制粘贴,改两处忘一处⭐⭐⭐⭐⭐ useXxx 直接 import
测试⭐ 几乎只能手点⭐⭐⭐⭐ 纯函数/composable 可单测
适用一次性页面、 demo会活过两周的业务代码

怎么判断:抽离的信号(不是行数) ​

不要用「超过 300 行就拆」这种伪规则,用这三个信号:

  1. 同一段逻辑第二处出现 → 立刻抽成 composable。第二处出现时手感和第一处不同:你已经知道要抄哪几行了,那几行就是 useXxx 的边界。
  2. 模板里的表达式开始看不懂 → 三元套三元、items.filter(...).map(...).sort(...) 内联在模板里 → 挪进 computed,给个名字。模板应该读起来像句子,不像作业题。
  3. <script setup> 能按「请求 / 格式化 / 交互」分出明显段落 → 每段一个 composable,组件里剩下一排 const { x } = useXxx()。

反信号(别抽):

  • 只有一处使用、十行以内的逻辑——抽出去反而要多跳一个文件
  • 为了「以后可能复用」而抽——没有第二处之前,「以后」基本不来

抽成什么样 ​

最小套路:把「状态 + 改它的函数」原样搬进 useXxx,返回一个对象。

ts
// composables/useCounter.ts —— 抽之前它在组件里,抽之后谁都能用
import { ref } from 'vue'

export function useCounter(initial = 0) {
  const count = ref(initial)
  function increment() {
    count.value++
  }
  return { count, increment }
}
vue
<script setup>
import { useCounter } from '@/composables/useCounter'
const { count, increment } = useCounter()
</script>

抽完自检一条:composable 里不应该出现 DOM 和「这个页面」的概念。出现了,说明抽的是页面,不是逻辑。

结论一句话

默认写胖;出现「第二处出现 / 模板看不懂 / 脚本能分段」任一信号就抽。抽的单位是逻辑(composable),不是行数。

参考链接 ​