优先在 onmounted 中发起异步数据加载,因 setup 顶层无法访问 dom 且 await 会阻塞初始化;setup 适合声明 ref、computed 等响应式定义;props 依赖的数据加载应配合 watch 或 onbeforemount。

在 Vue 3 的 Composition API 中,setup 不是生命周期钩子,而是一个初始化函数,它在组件实例创建后、挂载前执行,且只运行一次。数据加载的入口选择,关键看你要加载的数据是否依赖 DOM、是否需响应式、是否需等待 props 或 reactive 状态就绪。
优先在 onMounted 中发起请求
绝大多数真实场景下,推荐把异步数据加载(如 API 调用)放在 onMounted 钩子中,而不是 setup 顶层:
- setup 顶层代码执行时,组件尚未挂载,无法访问 ref 绑定的 DOM 元素(如 template 中的 ref="el"),也拿不到正确的 $el
- onMounted 确保 DOM 已就绪,适合需要操作 DOM 或依赖渲染结果的逻辑(比如初始化图表、第三方插件、滚动定位)
- 避免在 setup 中直接 await:会阻塞组件初始化流程,导致整个 setup 同步执行被延迟,可能引发响应式失效或警告
- onMounted 内部可安全使用 ref、computed、watch,并能配合 try/catch 处理错误
setup 顶层适合同步初始化和声明式定义
setup 本身适合做不带副作用的声明性工作,比如:
- 定义 ref、reactive、computed、watch —— 这些本身不触发网络请求
- 从 props 解构默认值或做简单转换(不涉及异步)
- 初始化非响应式变量(如计数器、缓存对象),只要不依赖挂载后环境
- 调用 provide/inject 或 useXXX 自定义 Hook(前提是这些 Hook 内部已封装好时机控制)
需要根据 props 初始化状态?用 watch 或 onBeforeMount
如果数据加载依赖 props(例如 id 变化要重新拉取详情),不要在 setup 里直接请求,也不要仅靠 onMounted —— 因为 onMounted 只触发一次:
- 用 watch 监听关键 prop,变化时调用加载函数(最常用、最灵活)
- 若首次加载 + 后续响应变化都要覆盖,可组合 onMounted + watch
- onBeforeMount 在挂载前执行,比 onMounted 更早,但依然能访问 setup 中定义的响应式状态;适合做“准备性”异步(如预加载配置),但不保证 DOM 存在
避免常见陷阱
以下做法容易出问题:
- 在 setup 顶层写 await api.get():会导致 setup 返回 Promise,Vue 会报错或降级为不可响应行为
- 在 setup 里调用 this.$nextTick():setup 中没有 this,且 nextTick 应在 onMounted 或 watch 回调中按需使用
- 把 loading 状态设在 setup 顶层变量而非 ref:会导致模板无法响应更新
- 在 onUnmounted 中忘记取消 pending 请求:用 AbortController 或 axios CancelToken 显式清理










