为什么写 Vue 强烈建议用 Setup?除了复用,更是代码组织

本文是系列文章的上篇,建议搭配食用

很多文章在讲 Vue Setup 引入了新语法,以及新语法怎么用。

本文想聊聊 Vue 为什么 引入 Setup。

💡 核心观点 :Composition API 的真正价值,不仅仅是逻辑复用,更是一场代码组织方式的进化

组件里的"寻宝游戏"

先回想一个熟悉的场景。

当你需要修改一个"用户信息模块"的功能时,在 Options API 的组件里,你需要经历什么?

  1. data 里找 user 状态定义。
  2. methods 里找 fetchUser 方法实现。
  3. mounted 里找初始化调用。
  4. watch 里找 userId 变化后的监听逻辑。

对于稍大一些的组件,页面反复滚动,思维上下横跳是开发常态。

html 复制代码
// ❌ Options API 实现:同一功能被强制拆散

export default {
  data() {
    return {
      // 🔴 用户模块
      user: null,
      loading: false,
      // 🟢 搜索模块
      keyword: '',
      results: [],
    }
  },

  methods: {
    // 🔴 用户模块
    async fetchUser() { /* ... */ },
    // 🟢 搜索模块
    async search() { /* ... */ },
  },

  watch: {
    // 🔴 用户模块(字符串写法更隐式!)
    userId: 'fetchUser',
    // 🟢 搜索模块
    keyword: 'search',
  },

  mounted() {
    // 🔴 用户模块
    this.fetchUser()
  },
}

这不是你的问题,是范式的问题。

Options API 强制我们按"选项类型"组织代码 (把状态放一起、把方法放一起)。但人类的大脑是按"业务逻辑"思考的(用户模块、搜索模块、表单模块)。

当组件简单时,这种分散无关痛痒。但当组件变大, "框架的分类方式"就成了"代码组织的天花板" 。我们不是在写业务,而是在填框架定义的表格。

理想结构:按"业务能力"聚合

抛开 Vue 的语法限制,我们心中理想的组件结构,应该是按"逻辑关注点(Logical Concerns)"聚合的。

同一个功能的状态、方法、副作用、生命周期,应该物理相邻

让我们将上面的 Options 功能全部打散,看看这段伪代码,这是我们在没有框架限制时,最自然的写法:

rust 复制代码
// ✅ 我们理想中的代码结构:按功能块排列

// 🔴 用户模块
状态:{ user, loading }
方法:{ fetchUser }
监听:{ userId -> fetchUser }
生命周期:{ mounted -> fetchUser }

// 🟢 搜索模块
状态:{ keyword, results }
方法:{ search }
监听:{ keyword -> search }
  • 代码都写在一起
  • 按功能块组织

在这种结构下:

  1. 内聚性:修改"用户功能",只需关注第一个代码块,无需跳转。
  2. 可读性:新人接手,看代码块注释就知道有哪些功能模块。
  3. 可维护性:删除功能,直接删掉一个块,不会残留碎片。

Vue 引入 Setup,就是为了让我们能写出接近这种理想结构的代码。

Setup 与工具函数

Vue 的 setup 函数,就是让你写所有代码的地方。

附带提供的一套工具函数(ref, watch, onMounted 等),能让你按业务逻辑组织代码。

让我们把上面的理想伪代码,翻译成真实的 Vue Setup 写法的代码:

js 复制代码
export default {
  setup(props) {
    // 🔴 用户模块
    const user = ref(null)          // 状态
    const loading = ref(false)      // 状态
    
    async function fetchUser() {    // 方法
      /* ... */ 
    }
    
    watch(() => props.userId, fetchUser)        // 监听
    onMounted(fetchUser)            // 生命周期

    // 🟢 搜索模块
    const keyword = ref('')         // 状态
    
    async function search() {       // 方法
      /* ... */ 
    }
    
    watch(keyword, search)          // 监听

    return { user, loading, keyword, search }
  }
}

我们想要的:

  1. 内聚性:修改"用户功能",只需关注第一个代码块,无需跳转。
  2. 可读性:新人接手,看代码块注释就知道有哪些功能模块。
  3. 可维护性:删除功能,直接删掉一个块,不会残留碎片。

统统达成。

💡 额外收益 : 当代码按业务逻辑聚合后,你会发现: "提取复用"变成了一个自然的水到渠成的动作

今天写在 setup 里的"用户模块"代码块, 明天只需要包一层函数、定义好输入输出, 就能变成一个可复用的 useUser() 组合函数。

具体怎么提取、有哪些最佳实践,我们下篇实战文章细聊

为什么 mixin 不行?

读到这儿,可能有 Options API 的老用户会问:

如果把用户逻辑写在一个 mixin 里,不也能聚合吗?为什么非要 Setup?

这是一个非常好的问题。确实,Mixin 也能把相关代码组织在一起。

但从 实际使用 的维度看,Mixin 存在致命缺陷。

js 复制代码
// UserMixin.js
export default {
  data() { return { user: null } },
  methods: { fetchUser() {} }
}

// Component.vue
export default {
  mixins: [UserMixin],
  // 问题:this.user 从哪来?IDE 无法提示,阅读时需要跳转文件
  mounted() {
    this.fetchUser() // 这个方法是哪来的?需要去 Mixin 里找
  }
}

Mixin 的三个使用缺陷:

  1. 来源不透明 :在组件里看不到 user 的定义,除非把组件用到的所有 mixin 都看一遍。
  2. 命名冲突 :如果两个 mixin 都定义了 user,后引入的会覆盖先引入的,且没有任何警告。
  3. 数据流向模糊:组件依赖了哪些 mixin 属性?无法肉眼识别,只能靠运行时验证。

想要让对象的某个属性(mixins)使用其它对象后(xxxMixin),能提示动态添加出的属性,这在 ts 里是地狱难度。

这也是 mixin 极难推广的原因。当然从上面的逻辑也可以看到,好的 mixin 几乎可以无痛转为 hook

总结:为什么强烈建议用 Setup?

回到最初的问题:为什么写 Vue 强烈建议用 Setup?

维度 Options API Setup
🧩 代码组织 按"选项类型"分散 ✅ 按"业务逻辑"聚合
🔍 可读性 需要脑内拼合逻辑 ✅ 同一功能物理相邻
🔧 可维护性 修改需跨多处跳转 ✅ 就近修改,无遗漏
🧪 可复用性 Mixin 隐式+冲突 ✅ 函数显式+类型友好
🚀 可演进性 提取复用成本高 ✅ 聚合后自然可提取

核心结论

Setup 的真正价值,不是"多了一套新语法", 而是把代码组织权还给了开发者

它允许你按照人类思考业务的方式写代码, 而不是按照框架分类的方式填表格。

Options API 并没有错,它在小型组件、初学者场景下依然清晰。 但当你的组件越来越复杂,强制按类型组织代码就会成为一种技术债务

Setup 不是为了让代码变复杂, 而是为了当你在面对一个 1000 行的组件时,不至于无从下手。

下一步:别把 Setup 写成 Options

理解了 Setup 的设计动机,不代表能写好 Composition API。

我观察到一个普遍现象: 很多开发者虽然用了 Setup,但只是把 Options 的代码搬运进了 setup 函数:

  • dataref
  • methodsfunction
  • mountedonMounted

结果写出了 "换皮版"的组合式代码

  • ❌ 既没享受到"按逻辑聚合"的组织红利
  • ❌ 又失去了 Options"结构清晰"的入门友好

有了"自由",不等于会"用好"。

在下一篇实战文章中,我将从一个真实的详情页案例出发,带你:

3 步升级,真正吃透 Composition API

步骤 升级内容 解决什么问题
升级 1 在 setup 内按功能模块组织代码 告别"大仓库",让同一功能的代码物理相邻
升级 2 拆分 setup,抽成 useXxx 组合函数 逻辑复用变得自然,输入输出显式可控
升级 3 从生命周期驱动转向数据驱动 watch 替代 onMounted,让代码更声明式

👉 立即阅读下篇别再写换皮 Options 了!Vue3 Setup 的真正用法是这3步升级

相关推荐
sorryhc2 小时前
我让 AI 帮我写了一个 Code Agent!
前端·openai·ai编程
工边页字2 小时前
面试官:请详细介绍下AI中的token,越详细越好!
前端·人工智能·后端
anyup2 小时前
月销 8000+,uView Pro 让 uni-app 跨端开发提速 10 倍
前端·uni-app·开源
前端Hardy3 小时前
别再忽略 Promise 拒绝了!你的 Node.js 服务正在“静默自杀”
前端·javascript·面试
前端Hardy3 小时前
别再被setTimeout闭包坑了!90% 的人都写错过这个经典循环
前端·javascript·vue.js
小林coding3 小时前
专为程序员打造的简历模版来啦!覆盖前端、后端、测开、大模型等专业简历
前端·后端
前端Hardy3 小时前
你的 Vue 组件正在偷偷吃掉内存!5 个常见的内存泄漏陷阱与修复方案
前端·javascript·面试
RaidenLiu3 小时前
Flutter Platform Channel 底层架构解析 —— 从 BinaryMessenger 到跨平台消息通信机制
前端·flutter·前端框架
bluceli3 小时前
CSS容器查询:响应式设计的新范式
前端·css