watch:
在 Vue.js 中,
watch是用于监听数据变化的核心 API,常用于执行副作用逻辑(如异步请求、复杂计算等)。
一、基本用法
1. 简单监听
data: {
count: ''
},
watch: {
// 监听 data 中的属性
count(newVal, oldVal) {
console.log('count 变化:', newVal, oldVal);
}
} // oldValue 老值(一般不用)
2. 监听对象属性
data: {
obj: {
words: ''
}
},
watch: {
// 监听对象深层属性(需用字符串路径)
'obj.words'(newVal) {
console.log('obj.words 变化:', newVal);
}
}
二、配置选项(对象形式)
通过对象形式配置 handler 和选项:
data: {
obj: {
words: '小黑',
lang: 'italy'
},
result: '', // 翻译结果
},
watch: {
obj: {
handler(newVal, oldVal) {
// 处理逻辑
},
deep: true, // 深度监听
immediate: true // 立即执行,一进入页面handler就立刻执行一次
}
}
1. deep: true(深度监听)
- 作用:监听对象或数组内部嵌套值的变化。
- 场景:监听复杂数据结构(如对象、数组)。
- 示例:
watch: { obj: { handler(newVal) { console.log('user 内部变化:', newVal); }, deep: true } }
2. immediate: true(立即执行)
- 作用:在组件初始化时立即触发一次回调。
- 场景:需要初始数据时(如页面加载时自动请求)。
watch: { keyword: { handler(newVal) { this.search(newVal); }, immediate: true // 初始化时执行一次 } }
三、监听多个数据(数组形式)
同时监听多个数据源,触发同一回调:
watch: {
// 监听多个数据,回调参数为数组
[data1, data2](newValues, oldValues) {
const [newData1, newData2] = newValues;
const [oldData1, oldData2] = oldValues;
// 处理逻辑
}
}
四、this.$watch(动态监听)
在组件实例中动态添加监听器:
export default {
created() {
// 动态监听 dataKey 的变化,并保存取消函数
this.unwatch = this.$watch('dataKey', (newVal) => {
// 处理逻辑
}, { deep: true, immediate: true });
},
beforeDestroy() {
// 手动取消监听
this.unwatch();
}
}
五、适用场景
1、异步操作(API 请求)
场景说明:当数据变化需要触发异步操作(如接口请求、定时任务)时,watch 是最佳选择。
典型场景:
- 搜索框输入联想(用户输入关键词后自动请求搜索结果)
- 表单字段变化后实时校验(如验证用户名是否被占用)
- 分页参数变化后重新加载数据
示例
watch: {
searchKeyword(newVal) {
// 防抖处理(避免频繁请求)
clearTimeout(this.timer);
this.timer = setTimeout(async () => {
const res = await axios.get('/api/search', { params: { q: newVal } });
this.results = res.data;
}, 300);
}
}
注意事项
- 必须配合 防抖(Debounce) 或 节流(Throttle) 避免高频请求。
- 需要处理异步错误(如添加
try/catch)。
2、复杂数据逻辑(依赖多个值变化)
场景说明:当需要根据多个数据的变化组合计算,或执行复杂逻辑时(如联动操作)。
典型场景:
- 表单多字段组合校验(如密码和确认密码是否一致)
- 购物车商品数量和总价联动更新
- 地图组件中经纬度变化后重新定位
示例
watch: {
// 监听多个数据(数组形式)
['username', 'password']([newUsername, newPassword], [oldUsername, oldPassword]) {
if (newUsername !== oldUsername) {
this.checkUsernameAvailability(newUsername);
}
if (newPassword.length < 6) {
this.showError('密码至少6位');
}
}
}
3、深度监听对象/数组(Deep Watch)
场景说明:当需要监听对象内部属性或数组元素变化时(如复杂表单对象、配置项)。
典型场景:
- 监听富文本编辑器的内容变化(如
editor.content) - 监听表格行数据(数组元素)的修改
- 监听嵌套配置对象(如主题色、用户偏好设置)
示例
watch: {
userProfile: {
handler(newVal) {
// 当 userProfile 内部属性变化时触发
this.saveToLocalStorage(newVal);
},
deep: true // 深度监听
}
}
注意事项
- 避免滥用
deep: true,尤其是监听大型对象时可能引发性能问题。 - 优先监听具体属性路径(如
'userProfile.name')减少开销。
4、动态路径监听(Dynamic Path)
场景说明: 当需要监听的属性路径是动态生成时(如根据用户选择动态监听不同字段)。
典型场景:
- 动态表单字段(如根据选择的问卷类型监听不同题目)
- 路由参数变化监听(如
$route.params.id)
示例
watch: {
// 监听动态路由参数
'$route.params.id'(newId) {
this.loadData(newId);
}
}
5、组件间通信(父子组件数据同步)
场景说明:当需要监听父组件传递的 prop 变化并同步到子组件内部状态时。
典型场景:
- 父组件传递
selectedTab,子组件监听并切换标签页 - 父组件传递
initialValue,子组件监听并初始化表单
示例
export default {
props: ['initialValue'],
data() {
return {
internalValue: this.initialValue
};
},
watch: {
initialValue(newVal) {
// 父组件传入的值变化时,同步到子组件内部状态
this.internalValue = newVal;
}
}
};
6、手动控制响应式行为
场景说明:当需要对数据变化做精细控制(如跳过初始监听、延迟执行)。
典型场景:
- 仅在某些条件满足时触发监听(如用户主动提交后才监听表单)
- 延迟执行副作用(如等待动画结束后再更新数据)
示例
watch: {
dataKey: {
handler(newVal) {
if (this.isActive) {
// 仅在 isActive 为 true 时执行
this.doSomething(newVal);
}
},
immediate: true // 初始化时也触发一次
}
}
7、与第三方库集成
场景说明:当需要将 Vue 数据与第三方库(如 D3.js、地图库、图表库)同步时。
典型场景:
- 数据变化后更新图表
- 监听地图中心坐标变化后重新渲染
示例
watch: {
chartData: {
handler(newData) {
// 销毁旧图表,重新渲染新数据
this.d3Chart.destroy();
this.d3Chart = new D3Chart(this.$el, newData);
},
deep: true
}
}
六、与 computed 的区别
| 特性 | watch | computed |
|---|---|---|
| 用途 | 监听数据变化,执行副作用(如请求、DOM 操作) | 基于依赖数据动态计算新值 |
| 缓存 | 无缓存 | 有缓存(依赖不变时直接返回缓存值) |
| 同步/异步 | 支持异步操作 | 必须是同步操作 |
| 响应触发条件 | 数据变化时触发 | 依赖数据变化时触发 |
七、注意
1. 避免滥用 deep
- 深度监听会遍历对象所有属性,可能影响性能。
- 优先监听具体属性路径(如
'obj.prop')。
2. 防抖与节流
- 防抖(Debounce):高频触发时延迟执行(如搜索框输入)。
watch: { keyword: { handler: _.debounce(function(newVal) { this.search(newVal); }, 300), immediate: true } } - 节流(Throttle):限制触发频率(如滚动事件)。
3. 清理副作用
- 在组件销毁前清除定时器、取消请求等:
beforeDestroy() { clearTimeout(this.timer); }
4. 监听引用类型
- 直接修改对象/数组的某个属性不会触发监听,需替换整个对象:
// ❌ 不会触发监听 this.obj.name = 'newName'; // ✅ 触发监听 this.obj = { ...this.obj, name: 'newName' };
5.何时避免使用 watch?
- 纯计算场景:优先使用
computed(如拼接字符串、过滤列表)。 - 高频触发且无需实时性:改用事件驱动(如手动按钮提交代替自动保存)或 需结合防抖/节流。
- 简单数据同步:使用
v-model或.sync修饰符。
八、常见问题
1. 为什么 watch 不触发?
- 数据未被 Vue 响应式系统追踪(如未在
data中声明)。 - 直接修改数组索引或对象属性(需使用
Vue.set)。
2. watch 和 $watch 的区别?
watch是组件选项,$watch是实例方法(动态添加监听)。
| 特性 | watch选项 | $watch方法 |
|---|---|---|
| 定义位置 | 在组件选项中静态声明 | 在代码中动态调用(如 created 钩子) |
| 配置方式 | 通过对象配置 handler、deep 等 | 直接传递回调函数和配置对象 |
| 取消监听 | 自动随组件销毁取消 | 需手动调用返回的 unwatch 函数 |
| 灵活性 | 适合固定监听逻辑 | 适合动态条件触发的监听(如按需监听) |
| 监听动态路径 | 需预定义路径(如 'obj.prop') | 可通过变量动态生成路径 |