2025年5月05日·阅读约1分钟

面向大型组件库的 Vue 3:Composition API 与 Options API 对比

Vue 3 Composition API 与 Options API:在大型后台组件库中,它们如何影响复用、测试与新成员上手速度。

面向大型组件库的 Vue 3:Composition API 与 Options API 对比

在大型后台组件库中这个选择为何重要

大型管理后台的组件库不是一个带几个按钮的营销站点。它包含数十(甚至上百)个会在各个界面重复出现的构建块:支持排序和批量操作的数据表、筛选面板、带验证规则的表单、抽屉和模态框、确认流程,以及像日期选择器和权限守卫这样的工具组件。

因为这些模式无处不在,团队经常复制并微调代码以赶期限。一个表格有自定义筛选栏,另一个有稍微不同的实现,很快你就会有五个“几乎相同”的版本。到那时,Composition API 与 Options API 的选择就不再只是个人偏好,而会影响整个库的健康状况。

通常第一个坏掉的是一致性。UI 仍能工作,但行为会漂移:某处按 Esc 能关闭模态,而另一处不能;同一个字段在一个页面上是 blur 校验,而在另一个页面上是 submit 时校验。接着,速度下降,因为每次改动都需要在近似重复的实现中找不同。最后是信心下降:大家避免重构,因为无法预判会影响到什么。

在共享库里,三个实际决策最重要:

  • 代码复用:如何在不产生纠缠依赖的前提下打包共享逻辑。
  • 测试:在不依赖脆弱 UI 测试的情况下验证行为的难易程度。
  • 入门:新贡献者多快能读懂一个组件并做出安全修改。

举个简单例子:你的后台有 20 个列表页,产品团队要求新增“已保存筛选”。如果表格、筛选和 URL 同步逻辑分散且不一致,你要么发布很慢,要么发布有缺陷。你选择的 API 风格决定了这些逻辑是否会集中在一个可复用的地方、它如何清晰地与每个页面连接,以及新手能否轻松扩展它。

如果你正在构建 Vue 3 管理应用(包括在像 AppMaster 这样的平臺里使用 Vue 3 作为 Web UI 层的团队),尽早做出这个选择可以在未来节省数月的维护成本。

Options 和 Composition 在日常代码中的不同

最快感受差别的方法是打开一个大型后台组件,问自己:“我在哪里改变这个特性的行为?”在组件库中,这个问题每天都会出现。

使用 Options API 时,代码按类型分组:data 放状态,methods 放动作,computed 放派生值,watch 放副作用。当组件小的时候,这种结构很容易浏览。但在大型表格或表单组件中,一个功能(比如批量操作或字段验证)的逻辑经常分散在多个区块。你可以保持整洁,但需要纪律和一致的命名来避免“在文件里到处跳”的工作流。

使用 Composition API 时,代码通常按功能分组。你把相关的状态、派生值、副作用和辅助函数写在一起,并能把重复逻辑抽成 composables。在后台风格的库里,这通常更符合人们的思路:"关于筛选的一切都在这里", "关于行选择的一切都在这里"。它也能减少不同组件间的重复,例如在多个表格间复用 usePagination

一个日常区别是依赖如何显现。

  • Options API 感觉更隐式:一个方法可能依赖 this.userthis.filtersthis.loading,你只有读到方法内部才知道。
  • Composition API 倾向更明确:当一个函数闭包中使用了 filtersloading,你能在附近看到这些变量的定义,并在需要时把它们作为参数传给辅助函数。

权衡是:如果把所有东西都塞进一个没有结构的 setup(),Composition API 会变得嘈杂。

一个实践经验的启发规则:

  • 当组件主要是展示型且逻辑轻量时,选择 Options API
  • 当组件包含多个特性且这些特性在库中有共享规则时,选择 Composition API
  • 若选用 Composition API,约定一个简单的布局,把代码按功能分组(而不是“先放所有 refs”)。
  • 若选用 Options API,强制命名并用简短注释和一致的方法名把相关逻辑放在一起。

两者都能工作。关键是选一种让下一次改动看起来明显而非“聪明”的组织方式。

代码复用:什么能干净扩展,什么会变得混乱

在后台风格的应用中,复用不是锦上添花。你会在数十个页面重复相同行为,细小的不一致就会变成 bug 和支持工单。

大多数复用需求集中在几个反复出现的类别:与后端匹配的排序/筛选/分页、表单验证和错误映射、权限校验与 UI 限制、查询同步(URL 参数、已保存视图、默认筛选)以及带有表格选择规则的批量操作。

Options API 的复用:强大,但容易隐藏复杂性

在 Options API 中,复用经常从 mixins、extends 或插件开始。

Mixins 快速但扩展性差,因为它们会隐藏方法或计算属性来自哪里。两个 mixin 可能在同名方法上悄悄冲突,此时你在调试时看不到组件文件中发生了什么。

extends 比 mixins 看起来干净一些,但它仍会带来继承谜题,你不得不读多个文件才能理解组件实际做了什么。插件适合应用级关注点(全局指令、共享服务),但不适合包含随屏幕变化的业务规则。

问题通常在于复用变得隐式。新贡献者无法在不搜索整个代码库的情况下回答“这个数据来自哪里?”

Composition API 的复用:composables 保持显式

Composition API 的复用通常围绕 composables:返回 refs、computed 值和处理函数的小函数。大赢点是复用会在组件顶部附近显现,你可以传入参数而不是依赖隐藏的组件上下文。

例如,usePagination 可以接受默认值并以一致的形状发出变化,而 usePermissions 可以接受当前角色和功能名。到那时,选择更多变成库是偏好显式连接还是隐式继承的问题。

为了保持复用可预测,把每个可复用单元当作一个小 API:给它清晰的名字,定义输入和输出,并保持单一职责。如果一个 composable 开始同时处理分页、缓存、权限和通知,就把它拆分。这样以后替换单个部分不会破坏其他东西。

构建可复用的表单和表格而不头疼

在后台风格的应用中,表单和表格是组件库要么物超所值要么变成迷宫的地方。两种 API 都能工作,不同之处在于你如何打包像脏状态、错误映射和提交流程这样的共享行为,而又不让每个组件显得“特殊”。

对于共享表单逻辑,Options API 通常会把你推向 mixins 或共享 helper。mixins 刚开始方便,但后来常常难以回答基本问题:"这个字段的错误从何而来?" 或 "为什么提交被禁用?"

Composition API 使这类复用更可见,因为你可以把逻辑移到 composables(例如 useDirtyStateuseFormErrorsuseSubmitFlow),并确切地看到一个表单组件引入了哪些内容。在大型库中,这种清晰度通常比节省几行代码更重要。

保持组件 API 稳定的实用做法是把公共表面当作契约:props、emits 和 slots 应该很少改变,即便你重写内部实现。这个契约在两种风格中看起来一样,但 Composition API 常让重构更安全,因为你可以一次替换一个 composable,而不去动模板 API。

一些随着库增长仍然保持清晰的模式:

  • 构建只做一件事的基础组件(BaseInput、BaseSelect、BaseTable),然后把它们组合成功能组件。
  • 对布局灵活性使用 slots(操作区、空状态、单元格渲染),而不是为每个边缘情况增加 props。
  • 及早规范事件(例如 update:modelValuesubmitrowClick),以免应用依赖内部细节。
  • 把验证和格式化放在输入附近,但把业务规则放在外面(composables 或父容器)。

过度抽象是常见陷阱。一个处理所有字段类型、所有验证规则和所有布局选项的“超级表单”往往比普通 Vue 更难用。一个简单的规则是:如果一个基础组件需要超过少数几个 props 才能覆盖所有团队需求,它可能应该拆成两个组件。

有时重复是正确的选择。如果只有一个页面需要带有多行分组的奇怪表头,复制一小段并把它保留为本地实现更好。巧妙的抽象通常带来长长的维护尾巴,特别是当新贡献者加入并试图理解“普通”组件与框架内框架之间的差别时。

如果你在为大型表单和表格库在 Composition 与 Options 之间做选择,优先优化数据流的可读性。复用很好,但当它隐藏了从用户操作到发出事件的路径时,就不值得。

测试影响:什么更容易验证

别再重复造轮子
添加认证和常见集成,而无需为每个页面重写相同的胶水代码。
创建应用

在组件库中,测试通常分为三类:纯逻辑(格式化、验证、过滤)、渲染(在给定状态下显示什么)和交互(点击、输入、键盘、发出事件)。你选择的 API 风格会改变你能在不挂载完整组件的情况下测试第一类的频率。

Options API 的测试通常看起来像“挂载组件,操控实例状态,断言 DOM”。这可行,但它会促使更大的测试,因为逻辑混在 methodscomputedwatch 和生命周期钩子中。当出错时,你还要花时间判断是 watcher 的时序、生命周期副作用还是逻辑本身的问题。

Options API 在以下场景下常显得直接:

  • 依赖生命周期顺序的用户流程(在 mount 时 fetch、在路由变化时重置)
  • 由 watcher 驱动的行为(自动保存、查询同步)
  • 从组件方法发出的事件(save()reset()applyFilter()

Composition API 改变了平衡。如果你把逻辑移到 composables,就可以像普通函数一样对其做单元测试,使用小的输入和清晰的输出。这减少了你需要的“挂载并点击”类测试,并让失败更局部。它也让依赖更容易控制:你可以把依赖(比如 fetch 函数、日期格式化器或权限检查器)作为参数传入 composable,而不是去 mock 一个全局。

一个具体的例子:一个带排序、分页和选中行的可复用 AdminTable。在 Composition API 下,选择逻辑可以放在 useRowSelection() 中,并在不渲染表格的情况下测试(切换、清除、全选、在页面间保留)。然后你只需少量组件测试来确认模板正确地绑定了按钮、复选框和发出的事件。

为保持测试小而可读(无论哪种风格),在逻辑与 UI 之间建立清晰的分界:

  • 把业务规则放在纯函数或 composables 中,而不是放在 watchers。
  • 把副作用(fetch、存储、定时器)放在可注入的依赖之后。
  • 每个组件保持少量聚焦的集成测试,而不是一个巨大的“所有功能”测试。
  • 在库中一致命名状态和事件(减少测试设置)。
  • 避免隐藏耦合(比如方法 A 依赖 watcher B 运行)。

如果你的目标是通过风格决策提高测试稳定性,那就倾向于减少依赖生命周期的行为,把逻辑单元化,这样你可以在不依赖 DOM 的情况下验证它们。

新贡献者入门:人们多快能成为产能状态

选择你想要的交付方式
部署到 AppMaster Cloud 或你自己的云,或导出源码自托管。
部署应用

在大型后台组件库中,入门更多是教人们在哪里找到东西、如何遵循相同约定,以及如何有把握地修改。大多数减速来自三个差距:导航(逻辑在哪里?)、约定(我们这里如何做?)和信心(如何在不破坏五个页面的情况下修改?)。

使用 Options API,新人通常在第一天更容易上手,因为结构熟悉:props、data、computed、methods、watchers。代价是实际行为常常分散。像“服务器端筛选”这样的单个特性可能被拆到 watcher、computed 和两个 methods 中,再加上 mixin。人们可以阅读每个区块,但需要时间把故事拼起来。

使用 Composition API,入门的优势是相关逻辑可以放在一起:状态、副作用和辅助函数在一处。代价是需要学习 composable 的使用模式。新贡献者需要理解像 useTableState() 这样的模式,以及响应式值如何在多个 composables 之间流动。如果没有清晰边界,可能会觉得是在没有地图的文件间跳来跳去。

一些约定能消除大多数问题,无论你选哪种风格:

  • 使用可预测的目录结构:components/composables/types/tests/
  • 选定命名模式并坚持(例如:useXXTableXForm)。
  • 添加简短的 docblock:组件做什么、关键 props、主要事件。
  • 定义一个“应急规则”(何时可以添加新 composable 或 helper)。
  • 保留一个小的“金牌”组件示例,展示首选模式。

举例:如果你团队使用 Vue 3 生成一个管理面板然后定制它(例如用 AppMaster 生成 Web 应用并由开发者扩展),当有一个明确的地方去调整表格行为(排序、筛选、分页)和一个明确的地方去调整 UI 连接(slots、列渲染器、行操作)时,入门情况会大幅改善。这种清晰性比你选择的 API 更重要。

逐步实施:如何选择风格并安全引入

对于大型管理 UI 库,最安全的办法是从一个边界明确的特性开始,把它当作试点,而不是整体重写。

挑一个复用高且行为清晰的模块,比如表格筛选或表单验证。修改代码前,写下它目前的行为:输入(props、query params、用户操作)、输出(事件、emits、URL 变化)和边界情况(空状态、重置、服务器错误)。

接着,设定边界。决定哪些必须保留在组件内部(渲染、DOM 事件、无障碍细节),哪些可以移到共享代码(解析筛选、去抖、构建 API 参数、默认状态)。库常出错的地方在于把 UI 决策移动到共享代码里,这会降低复用性。

一个实际的推广计划:

  • 选一个能清楚展示该模式且被多个页面使用的组件。
  • 抽取一个共享单元(composable 或纯 helper),保持 API 小且明确。
  • 先为该单元添加一个基于真实后台场景的聚焦测试。
  • 用新单元端到端重构所选组件。
  • 把相同模式应用到另一个组件以确认其可扩展性。

保持共享 API 普通且显而易见。例如,一个 useTableFilters() composable 可以接受初始筛选并暴露 filtersapply()reset()toRequestParams()。避免魔法行为(例如自动读取全局状态),除非那是你们应用的既定规则。

试点后,发布一份简短的内部指南并附上一个示例供贡献者复制。比起长篇文档,一个具体规则更有效,比如:“所有表格筛选逻辑都放在 composable;组件只绑定 UI 控件并调用 apply()。”

在扩展前,使用简单的完成定义:

  • 新代码在两个不同组件中读起来一致。
  • 测试在不挂载完整 UI 的情况下覆盖共享逻辑。
  • 新贡献者可以在不接触无关文件的情况下更改筛选规则。

如果你团队也用像 AppMaster 这样的无代码工具生成管理门户,同样的试点思路也适用:挑一个工作流(比如审批),定义行为,然后在大规模推广前标准化模式。

大型库中常见的错误与陷阱

把模式变成构建模块
用可复用的模式构建 Vue 3 管理界面,在各个屏幕间保持逻辑一致。
试用 AppMaster

大型组件库最大的问题很少是语法层面的。它们来自一系列局部决定的堆积,使得复用、测试和维护变得更难。

一个常见陷阱是随机混用模式。如果库一半使用 Options API,另一半使用 Composition API 且没有规则,每个新组件就会变成风格争论。你还会看到为相同问题出现重复而形状不同的解决方案(表单、表格、权限)。如果允许两种风格并存,写一条明确策略:新组件使用一种风格、遗留代码仅在需要时触碰、共享逻辑放在统一位置。

另一个陷阱是“上帝 composable”。它通常起始于有用的 useAdminPage()useTable(),然后慢慢吸收路由、fetch、缓存、选择、对话框、通知和权限。它变得难以测试,因为一次调用触发许多副作用;也难以复用,因为每个页面只需要其中 30%,却得承担 100% 的复杂性成本。

Watchers 也是痛点来源。它们容易添加来修复不同步,但随之而来的时序 bug(尤其在异步数据和去抖输入时)很难复现。当有人报告“它有时会清除我的选择”时,你可能要花数小时复现并定位问题。

通常预警信号包括:

  • 组件只能在某个特定 props/事件顺序下工作。
  • composable 在不显式说明的情况下读写全局状态。
  • 多个 watcher 更新同一片状态。
  • 重构持续以小幅方式破坏消费者页面。
  • 贡献者回避修改“那个文件”,因为风险太大。

最后一个陷阱是在重构时破坏公共 API。表格、筛选和字段组件会迅速传播。重命名一个 prop、改动发出的事件或调整 slot 行为都可能静默地破坏几十个页面。

更安全的做法是把组件 API 当作契约:弃用而不是删除,保留兼容层一段时间,并添加简单的用法测试,按消费者的真实使用方式挂载组件。如果你用 AppMaster 生成 Vue 3 管理界面,这点更重要,因为一致的组件契约能让屏幕复用更容易、变化更可预测。

提交到模式前的快速检查

交付完整的管理应用
在 AppMaster 中建模你的数据并生成完整后端与 Vue 3 Web 应用。
开始构建

在你最终选择 Composition、Options 或混合之前,对库中真实组件做几项快速检查。目标简单:让人容易找到逻辑、安全复用并能测试管理员真正依赖的部分。

1) 某人能快速找到逻辑吗?

打开一个典型的后台重型组件(筛选 + 表格 + 权限 + 批量操作)。假装你是新来者。

好的信号是贡献者能在 2 分钟内回答“筛选逻辑在哪里?”或“什么决定按钮是否禁用?”。在 Options API 中,这通常意味着逻辑在 computedmethods 和 watcher 中清楚分布。使用 Composition API 时,这意味着 setup() 被组织成小的命名块(或 composables),而不是一个巨大的函数。

2) 共享工具是像函数一样工作,还是带魔法?

无论你选哪种模式,共享代码都应该有清晰的输入输出,且副作用最小。如果 helper 去读取全局状态、修改传入对象或在不明显的情况下触发网络请求,复用就很危险。

快速检查:

  • 你能从 composable 或 helper 的签名猜到它返回什么吗?
  • 你能在两个组件中使用它而无需额外隐藏设置吗?
  • 你能在测试中重置它的状态而不借助 hack 吗?

3) 你的测试是否聚焦管理员行为?

后台应用常在可预测的方式失败:筛选应用错误、权限泄露、表单校验不一致、编辑后表格状态异常。

别去测试内部实现细节(比如 watcher vs refs),而是围绕行为写测试:"给定角色 X,操作 Y 隐藏"、"保存失败显示错误并保留用户输入"、"筛选改变会更新查询并触发空状态消息"。这样即便后来在风格间重构,测试仍然稳定。

4) 你有统一的异步状态规范吗?

大型库里会出现许多小的异步流程:加载选项、校验字段、获取表格行、失败重试。如果每个组件都自己发明 loading/error 处理,入门和调试都会变慢。

选定一种清晰的异步状态形状(loading、error、重试、取消)。Composition API 常鼓励用可复用的 useAsyncX() composable,而 Options API 可以标准化 data() 状态加共享方法。无论哪种,只要整个库一致即可。

5) 组件公共 API 是否稳定且自说明?

把组件当作产品。它们的 props、events 和 slots 是契约。如果契约经常变化,每个后台页面都会变得脆弱。

寻找那种解释意图的注释(而非机械说明):props 的含义、保证发出的事件、哪些被视为内部。如果你用 AppMaster 生成内部管理工具,这种心态同样适用:稳定的构建块能让未来页面更快交付。

示例场景与给团队的下一步建议

想象你在重建一个后台的“Users”页面:一个筛选栏(状态、角色、创建日期)、一个可选中行的表格、批量操作(禁用、删除、导出)以及基于角色的访问控制(只有管理员能批量删除,经理能编辑角色)。

无论 Composition API 还是 Options API,UI 最终可以一样,但代码往往会组织不同。

在 Options API 中,你常得到一个大型组件,data 放筛选和选择,computed 放派生状态,methods 放获取、批量操作和权限检查。复用通常以 mixins 或共享 helper 的形式出现。结构熟悉,但相关逻辑可能分散(fetch 在 methods、查询同步在 watchers、权限在 computed)。

在 Composition API 中,你通常把页面拆成几个聚焦的 composables:一个负责查询和筛选,一个负责表格选择和批量操作,一个负责权限。页面组件成为这些部分的组装体,每个关注点的逻辑都聚合在一起。代价是你需要清晰的命名和文件夹约定,否则贡献者会觉得 setup 里有魔法。

复用通常在后台库中自然出现于以下方面:同步到 URL 的筛选、服务端表格模式(分页、排序、全选、批量操作保护)、权限检查与 UI 限制(按钮、列、行操作)以及跨页面一致的空/加载状态。

对大多数团队有效的下一步计划:

  1. 为新代码选定默认风格,只有书面理由才允许例外。
  2. 定义约定:composables 存放位置、命名方式、可导入范围与必须返回的内容。
  3. 添加一个小的参考页面(例如本示例的 Users 页面)作为首选模式的金标准。
  4. 先为可复用部分写测试(筛选、权限、批量操作),而不是视觉布局测试。
  5. 如果某些后台页面比深度定制更看重速度,考虑用无代码工具(比如 AppMaster)生成这些页面,然后把手写库用于真正独特的部分。

如果你已经使用 AppMaster,它能帮助你在生成部分与手写部分之间保持相同的心智模型:稳定的组件契约,以及以小且明确单元打包的共享逻辑。对于评估内部门户无代码方案的团队,AppMaster (appmaster.io) 能生成完整应用(后端、Web、移动端)同时仍允许你在 Vue 3 Web UI 层标准化关键部分。

如果这周只做一件事,把 Users 页面当作你的模板并在代码审查中强制执行。一个清晰的示例比长篇风格指南更能推动一致性。

常见问题

Which API should we pick for a large Vue 3 admin component library?

默认在你的库中使用 Composition API,如果你会频繁复用像筛选、分页、批量操作和权限控制这样的行为。它更便于把共享逻辑抽象成 composables,并让依赖关系更明确。当组件主要是展示型且逻辑很少时,使用 Options API 也很合适。

What’s the biggest day-to-day difference between Options API and Composition API?

Options API 会按类型分组代码(datamethodscomputedwatch),所以某个功能的逻辑常会分散在多个地方。Composition API 更常按功能分组,让“过滤”或“选择”相关的代码能放在一起。最佳选择是能让下一次改动容易定位且能安全应用的那种方式。

Why do mixins become a problem in big libraries?

在 Options API 中,复用常从 mixins 或 extends 开始,这会隐藏方法和计算属性的来源并可能导致命名冲突。Composition API 倾向于用 composables,具有明确的输入输出,组件中能直接看到组合方式。对于共享库来说,显式的复用通常更易维护。

How do we keep composables from turning into a “god composable”?

把每个 composable 当作一个小型 API:单一职责、明确参数与返回值。如果一个 composable 同时处理分页、缓存、权限和通知,就应该拆分。小型 composables 更容易测试、更易复用,也不容易引入意外副作用。

What’s a practical way to build reusable tables and forms without over-abstracting?

保持公共契约稳定:props、emits 和 slots 应尽量少改动。把输入格式化和基础校验放在字段组件附近,但把业务规则放在 composables 或容器组件中。这样即便重写内部实现,也不会迫使每个使用页面做改动。

Which API leads to easier testing in an admin UI library?

Composition API 通常更方便在不挂载整个组件的情况下单元测试逻辑,因为你可以直接测试 composables 和纯函数。Options API 则更常需要挂载组件进行测试,watchers 和生命周期的时序可能会带来噪音。不论哪种风格,把业务规则与 UI 连接分离才是保证测试小而稳定的关键。

How should we handle loading and error state consistently across many components?

为异步状态(比如加载、错误、重试、取消)标准化一种形状,例如统一使用 loadingerror,并定义清晰的重试或取消策略。不要让每个组件都发明自己的异步状态约定,否则调试会很慢。无论用哪种 API,重要的是在整个库中保持一致性。

What helps new contributors ramp up fastest in a large component library?

Options API 对新手入门通常更友好,因为结构熟悉(props、data、computed、methods、watchers)。但真实逻辑往往分散,需要花时间把各块拼起来。Composition API 当贡献者熟悉 composables 和文件夹约定后,可以更快上手,因为相关行为被分组并且复用可见。最有用的做法是提供一个“golden”示例组件并在代码审查中强制执行相同模式。

How do we switch styles safely without a risky rewrite?

选择一个边界清晰且高复用的特性(比如表格筛选或表单错误映射)作为试点。抽取一个小而明确的共享单元,先为该单元写专注的测试,然后将选中的组件端到端重构为使用该单元。该模式在至少两个组件上验证通过后,再逐步推广。

What are the warning signs our component library is becoming hard to maintain?

注意以下迹象:出现大量近似重复的实现;watchers 相互斗争;组件仅在特定 props/事件顺序下可用;或贡献者回避修改某些“危险”的文件。另一个警告是频繁破坏 props、事件或 slot 行为。遇到这些问题时,需要更明确的契约和更显式的复用策略。

容易上手
创造一些 惊人的东西

使用免费计划试用 AppMaster。
准备就绪后,您可以选择合适的订阅。

开始吧