侦听器(Watch)
# 侦听器(Watch)
# 1. 什么是侦听器
侦听器(watchers)允许开发者监视 Vue 实例中数据的变化,并在数据变化时执行特定的操作。通过使用侦听器,开发者可以在数据变化时触发相应的回调函数,从而实现数据驱动的响应式编程。
提示
在 Vue 实例中,watch
选项是一个对象,其中的每个属性都是一个侦听器,用于监视 Vue 实例中对应数据的变化。watch
选项中的属性名必须与 data
中的属性名相对应,否则侦听器不会起作用。
# 2. 定义侦听器的语法
在 Vue 实例中,定义侦听器有两种方式:方法定义和对象定义。
# 2.1 方法定义侦听器
方法定义的侦听器是直接在 watch
选项中定义方法,用于监视对应的数据变化。方法中可以访问到数据变化前后的值。
语法格式
new Vue({
el: '#app', // 绑定 Vue 实例的根元素
data: {
username: '' // 定义一个 username 数据
},
watch: {
// 方法定义的侦听器
username(newVal, oldVal) {
// newVal 是 username 的新值,oldVal 是 username 的旧值
console.log('username changed from', oldVal, 'to', newVal);
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
在定义的侦听器中,newVal
和 oldVal
是固定的写法,用于 Vue 侦听器(watchers)中表示数据变化前后的值。
newVal
:变化后的新值,即数据变化后的当前值,始终是第一个参数。oldVal
:变化前的旧值,即数据变化前的之前值,始终是第二个参数。
# 2.2 对象定义侦听器
对象定义的侦听器可以更灵活地设置侦听器的选项,如 immediate
和 deep
。handler
是侦听器的处理函数。
语法格式
new Vue({
el: '#app', // 绑定 Vue 实例的根元素
data: {
username: '' // 定义一个 username 数据
},
watch: {
// 对象定义的侦听器
username: {
handler(newVal, oldVal) {
// newVal 是 username 的新值,oldVal 是 username 的旧值
console.log('username changed from', oldVal, 'to', newVal);
},
immediate: false, // 是否立即触发回调
deep: false // 是否深度监听
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2.3 监测用户名是否可用
以下是一个使用 watch
侦听器监测用户名变化,并使用 axios
发起 AJAX 请求,检测用户名是否可用的示例:
# 1. 方法定义侦听器示例
new Vue({
el: '#app', // 绑定 Vue 实例的根元素
data: {
username: '' // 定义一个 username 数据
},
watch: {
// 监听 username 值的变化
async username(newVal) {
if (newVal === '') return; // 如果新值为空字符串,则不做任何处理
try {
// 使用 axios 发起请求,判断用户名是否可用
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal);
console.log(res); // 打印服务器返回的结果
} catch (error) {
console.error('Error fetching username availability:', error); // 捕捉并打印请求错误
}
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2. 对象定义侦听器示例
new Vue({
el: '#app', // 绑定 Vue 实例的根元素
data: {
username: '' // 定义一个 username 数据
},
watch: {
// 监听 username 值的变化
username: {
async handler(newVal) {
if (newVal === '') return; // 如果新值为空字符串,则不做任何处理
try {
// 使用 axios 发起请求,判断用户名是否可用
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal);
console.log(res); // 打印服务器返回的结果
} catch (error) {
console.error('Error fetching username availability:', error); // 捕捉并打印请求错误
}
},
immediate: false, // 是否立即触发回调
deep: false // 是否深度监听
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
总结
- 方法定义侦听器:直接在
watch
选项中定义方法,用于监视数据变化,适用于简单的监听需求。 - 对象定义侦听器:在
watch
选项中定义一个对象,包含handler
方法和其他选项(如immediate
和deep
),适用于需要更多控制的监听需求。
# 3. immediate
选项
默认情况下,组件在初次加载完毕后不会调用 watch
侦听器。如果希望在组件初次渲染完成后立即调用侦听器,可以使用 immediate
选项:
new Vue({
el: '#app', // 绑定 Vue 实例的根元素
data: {
username: '' // 定义一个 username 数据
},
watch: {
username: {
// handler 是固定写法,表示当 username 的值变化时,自动调用 handler 处理函数
handler: async function (newVal) {
if (newVal === '') return; // 如果新值为空字符串,则不做任何处理
try {
// 使用 axios 发起请求,判断用户名是否可用
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal);
console.log(res); // 打印服务器返回的结果
} catch (error) {
console.error('Error fetching username availability:', error); // 捕捉并打印请求错误
}
},
immediate: true // 表示页面初次渲染好之后,就立即触发当前的 watch 侦听器
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在上面的代码中,immediate: true
表示在组件初次渲染后立即调用侦听器。
笔记
当需要使用 deep
或 immediate
选项时,必须使用对象语法,并在对象中定义 handler
属性来指定回调函数。
# 4. deep
选项
如果需要侦听一个对象内部属性的变化,默认情况下是无法侦听到的。这时需要使用 deep
选项:
new Vue({
el: '#app', // 绑定 Vue 实例的根元素
data: {
user: {
name: '', // 用户名
age: 0 // 用户年龄
}
},
watch: {
// 监听 user 对象的变化
user: {
handler: function (newVal, oldVal) {
console.log('User data changed:', newVal); // 打印新的用户数据
},
deep: true // 深度监听,对象内部任何一个属性发生变化都会被监听到
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在上面的代码中,deep: true
选项用于深度监听 user
对象内部属性的变化。
# 5. 监听对象单个属性的变化
如果只需要监听对象中某个特定属性的变化,可以直接监听该属性:
new Vue({
el: '#app', // 绑定 Vue 实例的根元素
data: {
user: {
name: '', // 用户名
age: 0 // 用户年龄
}
},
watch: {
// 监听 user.name 的变化
'user.name': function (newVal, oldVal) {
// newVal 是 user.name 的新值,oldVal 是 user.name 的旧值
console.log('User name changed from', oldVal, 'to', newVal); // 打印用户名的变化
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在上面的代码中,直接监听 user.name
的变化。这里的 user.name
使用了字符串路径的方式,告诉 Vue 侦听的是 user
对象的 name
属性,而不是直接的属性名。
# 6. 监听路由变化
# 1. 监听路由路径 ($route.path
)
监听路由路径的变化,可以用于更新导航菜单的激活状态或触发某些特定操作。
export default {
data() {
return {
activeIndex: '' // 保存当前激活的菜单项
};
},
watch: {
// 监听路由路径的变化
'$route.path'(newPath, oldPath) {
console.log('Route path changed from', oldPath, 'to', newPath);
// 更新导航菜单的激活项
this.activeIndex = newPath;
// 你还可以在这里添加逻辑,例如根据新路径加载数据
}
},
created() {
// 初始化时根据当前路由路径设置激活项
this.activeIndex = this.$route.path;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2. 监听路由参数 ($route.params
)
动态路由参数变化时触发回调,比如 /user/:id
,可以用于在参数变化时更新页面数据。
export default {
watch: {
// 监听路由参数 id 的变化
'$route.params.id'(newId, oldId) {
console.log('Route param id changed from', oldId, 'to', newId);
// 根据新 ID 更新页面内容,例如加载新的用户信息
this.fetchUserData(newId);
}
},
methods: {
fetchUserData(id) {
// 执行获取用户数据的逻辑
console.log('Fetching user data for ID:', id);
// 在这里进行数据请求或其他处理
}
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 3. 监听查询参数 ($route.query
)
查询参数变化时触发回调,可以用于处理 URL 中的查询字符串,例如搜索功能。
export default {
watch: {
// 监听查询参数的变化
'$route.query'(newQuery, oldQuery) {
console.log('Route query changed from', oldQuery, 'to', newQuery);
// 根据新的查询参数执行搜索或筛选操作
this.searchResults(newQuery.q);
}
},
methods: {
searchResults(query) {
// 执行搜索的逻辑
console.log('Searching for:', query);
// 在这里发起请求或更新界面
}
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 4. 监听路由元信息 ($route.meta
)
元信息的变化可以用于权限控制或页面设置。例如,根据路由的元数据决定是否显示某个页面的内容。
export default {
watch: {
// 监听路由元信息的变化
'$route.meta.requiresAuth'(newVal, oldVal) {
console.log('Route meta requiresAuth changed from', oldVal, 'to', newVal);
// 根据新的 requiresAuth 值执行逻辑
if (newVal && !this.isAuthenticated()) {
// 如果需要认证且用户未登录,则跳转到登录页面
this.$router.push('/login');
}
}
},
methods: {
isAuthenticated() {
// 判断用户是否已登录
return !!localStorage.getItem('token');
}
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 5. 监听完整的路由对象 ($route
)
如果需要对整个路由对象进行监听,可以监听 $route
。这适用于在路径、参数或查询字符串任意一个发生变化时,执行统一的逻辑。
export default {
watch: {
// 监听整个路由对象的变化
'$route'(to, from) {
console.log('Route changed from', from.fullPath, 'to', to.fullPath);
// 根据新的路由执行统一的逻辑处理
this.handleRouteChange(to, from);
}
},
methods: {
handleRouteChange(to, from) {
// 处理路由变化的逻辑
console.log('Handling route change from', from.fullPath, 'to', to.fullPath);
// 例如,执行数据清理或更新页面标题
this.updatePageTitle(to.meta.title);
},
updatePageTitle(title) {
document.title = title || '默认标题';
}
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 6. 监听路由嵌套路径 ($route.matched
)
在多级嵌套路由中,监听 matched
可以用来处理特定层级的路由变化。
export default {
watch: {
// 监听路由匹配的组件链的变化
'$route.matched'(newMatched, oldMatched) {
console.log('Route matched changed:', newMatched, 'from', oldMatched);
// 你可以在这里处理嵌套路由的特定逻辑
this.handleNestedRoutes(newMatched);
}
},
methods: {
handleNestedRoutes(matched) {
// 处理嵌套路由的逻辑
console.log('Handling nested routes:', matched);
// 例如,加载相应的子组件或更新某些状态
}
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 7. 计算属性 vs 侦听器
计算属性和侦听器都是响应式编程的重要工具,但它们的适用场景有所不同:
- 计算属性:适用于依赖其他数据计算得来的属性,具有缓存功能,只有当依赖的数据发生变化时才会重新计算。
- 侦听器:适用于监视数据的变化并执行异步或开销较大的操作,如 AJAX 请求、复杂的 DOM 操作等。
特性 | 计算属性 | 侦听器 |
---|---|---|
缓存 | 是 | 否 |
适用场景 | 计算属性值 | 异步操作、复杂操作 |
简单逻辑 | 是 | 否 |
复杂操作 | 否 | 是 |
new Vue({
el: '#app', // 绑定 Vue 实例的根元素
data: {
firstName: 'John', // 定义 firstName 数据
lastName: 'Doe' // 定义 lastName 数据
},
computed: {
// 定义一个计算属性 fullName,依赖于 firstName 和 lastName
fullName() {
return this.firstName + ' ' + this.lastName; // 返回拼接后的全名
}
},
watch: {
// 监听 firstName 的变化
firstName(newVal, oldVal) {
// newVal 是 firstName 的新值,oldVal 是 firstName 的旧值
console.log('First name changed from', oldVal, 'to', newVal); // 打印 firstName 的变化
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在上面的示例中:
fullName
是一个计算属性,用于实时计算用户的全名,并且只有当firstName
或lastName
发生变化时才会重新计算。watch
侦听器监视firstName
的变化,并在变化时执行特定的操作。
在 Vue 中,侦听器是通过 watch
选项定义的对象方法,用于监视数据的变化,并在变化时执行相应的回调函数。通过合理使用侦听器,可以更好地实现数据驱动的响应式编程。
总结
watch
中的属性名必须与 data
中的属性名相同。
watch
中的同名属性可以是一个方法,也可以是一个对象。
- 如果是方法,则为简单的侦听器。
- 如果是对象,则可以包含
handler
方法和其他选项(如immediate
、deep
)。