watch 默认 lazy,组件初始化时不触发,仅在数据真正变化时执行回调;添加 immediate: true 后,会在实例创建完成、数据初始化后立即执行一次,newVal 为当前值、oldVal 为 undefined。

watch 默认是 lazy 的,意思是它只在监听的数据**真正发生变化之后**才执行回调,组件初始化时不会触发。这符合大多数场景的直觉:你关心的是“变化”,而不是“初始状态”。
lazy 是默认行为,代表“按需响应”
比如监听一个输入框的值:
data() { return { search: '' } },
watch: { search(newVal, oldVal) { console.log('搜索词变了', newVal) } }
页面刚加载完,search 是空字符串,但这个 watch 不会打印任何内容——因为没变过。只有用户开始输入、search 值从 '' 变成 'a' 时,回调才第一次运行。
这种设计避免了冗余执行,尤其适合含 API 请求、DOM 操作或复杂计算的逻辑。
immediate 改变时机:首次创建就执行一次
加 immediate: true 后,watch 回调会在组件实例创建完成、数据已初始化的那一刻,**立即用当前值执行一次**,之后再按常规响应后续变化。
常见写法(选项式 API):
watch: {
userId: {
handler(newId) { this.fetchUser(newId) },
immediate: true
}
}
效果相当于:组件一挂载,就用 data 中初始的 userId 发起一次请求,无需额外在 created 或 mounted 里手动调一次 fetchUser。
注意:这次“立即执行”的 oldVal 一定是 undefined,因为没有上一次的值;newVal 就是当前初始化后的值。
lazy 和 immediate 的本质区别不在“是否异步”,而在“触发条件”
两者都遵循 Vue 的响应式更新队列(默认异步),区别仅在于:
- lazy(默认):触发条件 = 数据从 A → B 的变更发生时
- immediate:触发条件 = 组件 setup / created 完成 + 数据已有值 → 立即触发一次,之后仍按 lazy 规则响应变更
换句话说,immediate 不是让 watch “变同步”,而是给它增加了一次“零延迟的初始化快照”。


















