组合式 API 解决了什么问题

选项式 API 把代码按「数据类型」切块:数据放一处、方法放一处、监听与生命周期各放一处。当组件只做一件事时它非常清晰,但一个管理页往往同时处理筛选、分页、权限与表单,相关逻辑被拆散到四五个选项中,读代码要在文件里来回跳。组合式 API 按「业务关注点」组织,同一件事的状态、计算与副作用写在一起,还可以整体抽成可复用的函数。

组合式 API 的价值不在写法新潮,而在于它让「一段可复用的逻辑」终于有了天然的载体——一个普通函数。
核心概念
  • setup组件的入口,在其中声明状态、计算与副作用。
  • ref包装基础类型,模板中自动解包,脚本中通过 .value 读写。
  • reactive代理对象,属性直接读写,注意解构会丢失响应性。
  • 组合函数以 use 开头的普通函数,是逻辑复用的基本单位。
Vue 3 组合式 API 配图:折角 V 形图形与下方展开的组件树
组件树自上而下传递属性,事件自下而上回传,数据流始终单向。

setup 与响应式核心:ref 与 reactive

ref 能包装任意类型,是最不容易出错的选择;reactive 只接受对象,适合表示一组内聚的状态。两者的替换代价差别很大:ref 在模板里自动解包,在脚本里要写 .value;reactive 直接访问属性,但一旦被解构或整体重新赋值,响应性就断了。团队内部最好统一约定,避免两种风格混用带来的心智负担。

import { ref, reactive, computed } from "vue";

const keyword = ref("");
const list = ref([]);
const pager = reactive({ page: 1, size: 20 });

const filtered = computed(() =>
  list.value.filter((it) => it.name.includes(keyword.value)));

async function load() {
  list.value = await fetch("/api/list").then((r) => r.json());
}

这段代码里 computed 是关键:它根据关键词派生结果,关键词一变自动重算。把派生值写成计算属性而不是存储状态,就不会出现「列表更新了但筛选结果没变」这类不一致。

计算属性与侦听器

计算属性与侦听器的分工很明确:前者根据已有状态推导新值,应当是纯计算、无副作用;后者响应变化去执行动作,例如发请求、写缓存、上报埋点。选错会导致两种典型问题——用监听器维护派生状态,会出现短暂的不一致窗口;用计算属性发请求,则会在渲染过程中触发副作用,造成难以追踪的重复请求。

场景该用哪个原因
根据筛选条件出结果计算属性纯派生,可缓存
关键词变化后请求接口侦听器需要副作用与防抖
格式化展示文本计算属性同一输入结果稳定

侦听器还提供「立即执行」与深度监听两个开关。深度监听会遍历整个对象,对大型数据结构有明显开销,应尽量改为监听具体字段,或者在数据源头保证引用的更新粒度足够小。

组件通信与插槽

Vue 的通信路径很清楚:父传子用属性,子传父用事件,跨层级用依赖注入,全局共享用状态库。插槽则解决了「结构由父级决定、内容由子级渲染」的场景——一个卡片组件只需要暴露标题和工具栏两个插槽,就能适配完全不同的业务内容,而不必为每种样式新增一个属性开关。

通信方式选择
  • 属性与事件默认选择,显式、易调试,适合一到两层的传递。
  • 依赖注入主题、国际化这类深层共享值,避免层层透传。
  • 状态库跨页面共享的业务状态,配合模块化拆分。
  • 插槽不涉及数据的结构定制,用默认插槽与具名插槽即可。

响应式原理与性能取舍

Vue 3 用代理对象拦截读写:读取时收集依赖,写入时通知相关的副作用重新执行。这套机制决定了性能优化的方向——减少被追踪的属性数量、避免在同一个副作用里依赖过多无关状态。实践中,把大对象拆成若干小 ref,往往比给一个大对象做深度监听更高效。

模板渲染同样是可优化的路径:长列表配上稳定的 key 与按需渲染、频繁切换的区块用 v-show 而非 v-if、列表项内避免使用高开销的高阶组件包装层。这些改动的收益通常比调整响应式写法更直接,也更值得先做。