先记住这个答案
watch 默认先建立观察,等来源变化后再运行回调。immediate: true 会在创建侦听器时立即运行一次,单一来源的首次 oldValue 为 undefined,随后变化时再按正常观察与调度规则运行。它适合“当前参数先同步一次,之后随参数变化继续同步”的关系,但不等于组件已经挂载,也不保证初始异步任务在页面其它工作之前完成。
- immediate 表达创建时主动执行,不是只执行一次
- 首次单源回调没有旧值,应明确处理 undefined
- 初始化请求不要同时在多个生命周期入口重复启动
当前文档需要先加载,再随 ID 变化加载
页面已经有一个有效 documentId 时,immediate 可以让同一段同步逻辑先处理当前文档,后续 ID 改变再处理新文档。这样不必在 onMounted 中另写一次相同请求,同时又保留一份 watch,减少两条初始化路径重复执行的机会。
不过文档 ID 也可能尚未准备好。回调需要明确空 ID 是否跳过、是否清空旧数据以及什么时候进入加载状态。不能因为设置了 immediate 就默认第一次参数有效,也不要让一个尚未确定的 ID 触发错误请求后再用其它状态掩盖结果。
立即执行不是 DOM 已经可以使用
在 setup 同步流程中创建侦听器,初次立即回调就可能早于组件挂载。模板 ref 此时未必指向真实元素,因此不能在这个回调里无条件测量 DOM 或调用第三方控件实例。需要挂载后资源时,应使用对应生命周期与清楚的更新时机。
Options API 中立即侦听的初次执行也有自己的初始化位置,不能直接把它等同于 mounted。flush 主要描述后续回调的调度时机,不能仅看到 post 就推断首次 immediate 回调必然发生在已挂载 DOM 之后。界面相关代码应通过实际生命周期验证,而不是只背选项名称。
首次旧值与异步竞争需要显式处理
单源首次回调的 oldValue 为 undefined,表示没有先前的观察结果,不应直接读取它的属性。比较逻辑可以区分初始化与后续变化,或者让初始化根本不需要旧值。多来源时则按数组结构处理,避免对一个不存在的旧字段组合做错误解构假设。
immediate 启动的请求也可能在后续参数变化后才返回,需要取消或轮次判断,防止旧结果覆盖当前文档。还要考虑与路由数据层、服务端预取或缓存机制的关系,避免同一资源被多个入口重复请求。立即执行决定触发时机,不提供去重、缓存或等待完成的完整协议。
容易答错的地方
- immediate 只会执行初始化这一回
- 除非另有停止条件,它后续仍会随来源变化运行。只执行一次由 once 或明确停止逻辑控制,不能因为名字带立即就忽略后续生命周期。
- 首次 oldValue 必须等于初始 state
- 单源立即回调没有之前已观察的值,oldValue 为 undefined。初始当前值通过 newValue 提供,两者表达不同时间位置。
面试官还会怎么问?
immediate 和 once 同时开启会怎样?
once 限制的是回调运行一次,立即回调也属于一次运行,因此组合使用后不能再期待下一次来源变化继续回调。应按所用版本验证组合行为,而不是把 once 只理解成后续变化计数。
需要立即请求又需要 DOM 测量,可以放一个 watch 吗?
最好先分清两个过程的触发条件。请求可以由当前参数同步,DOM 测量需要元素存在和恰当的更新时机,混在同一个立即回调里容易造成生命周期假设冲突。
初次请求失败后,来源没变会自动再试吗?
不会仅因请求失败就自动产生新的来源变化。重试应有明确事件或策略,并考虑取消、次数与错误状态,immediate 不是自动重试选项。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。