1. 前言
本节介绍侦听器 watch
的使用方法。包括什么是侦听器,侦听器的特点,以及如何对不同类型的数据进行监听。其中重点掌握对不同类型的数据如何使用侦听器,了解它之后,才能在之后的日常开发中熟练运用。
2. 官方定义
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。 — 官方定义
侦听器 watch
是 Vue
提供的一种用来观察和响应 Vue
实例上的数据变化的属性。当被侦听的数据发生变化时,会触发对应的侦听函数。
3. 使用侦听器
前面我们介绍了什么是侦听器 watch
,那么如何定义一个侦听器呢?
侦听器 watch
实际是 vue
实例上的一个对象属性。当我们需要对 vue
实例上某个属性进行侦听时,我们以需要被侦听的属性名作为 watch
对象的键,以一个函数 function
作为该键的值。函数 function
接收两个参数:侦听数据变化之后的值newValue
;侦听数据变化之前的值oldValue
:
var vm = new Vue({el: '#app',data() {return {count: 0}},watch: {count: function(newVal, oldVal) {// 具体处理逻辑},}})var vm = new Vue({ el: '#app', data() { return { count: 0 } }, watch: { count: function(newVal, oldVal) { // 具体处理逻辑 }, } })var vm = new Vue({ el: '#app', data() { return { count: 0 } }, watch: { count: function(newVal, oldVal) { // 具体处理逻辑 }, } })
代码解释:
第 5 行,我们在 data
中定义了 Number
类型的数据 count
。
第 8-12 行,我们在侦听属性 watch
中定义了侦听器 count
。
在介绍完如何定义一个侦听器之后,让我们用几个实例来学习一下如何在项目中使用侦听器。
3.1 对字符串、布尔值、数字、数组类型的监听
假设当前项目中有以下需求,用户的购买商品页面:
- 当用户每次修改要购买商品名称的时候,都需要清空购买数量。
- 用户添加的数量在必须大于 0,并且当首次出现数量大于 10 的时候弹出数量超额提示,下次超过不再提示。
- 购物车数量发生变化时提示用户。
在我们写具体的代码逻辑之前,让我们先来分析一下如何使用侦听器watch
来实现需求功能:
- 对于需求 1, 我们需要对商品名称进行侦听,当它改变的时候将数量清零。
- 对于需求 2, 我们需要对商品数量进行侦听,当商品数量首次超过 10 的时候,弹出对应提示;当商品数量小于 0 的时候,将数量改为 0。
- 对于需求 3, 对购物车数据进行侦听,每次操作购物车数据时提示用户。
具体实现如下:
实例演示
<!DOCTYPE html><html lang="en" style="background-color: #ccc;"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body style="background-color: #ccc;"><div id="app"><div>商品名称:<input v-model="name"/></div><button v-on:click="cut">减一个</button>购买数量{{count}}<button v-on:click="add">加一个</button><button v-on:click="addCart">加入购物车</button><div v-for="(item, index) in list" :key="index">{{item.name}} x{{item.count}}</div></div></body><script src="https://unpkg.com/vue/dist/vue.js"></script><script type="text/javascript">var vm = new Vue({el: '#app',data: {name: '',count:0,isMax: false,list: []},methods: {cut() {this.count = this.count - 1this.isMax = false},add() {this.count = this.count + 1},addCart() {this.list.push({name: this.name,count: this.count})}},watch: {count: function(newVal, oldVal) {if(newVal > 10) {this.isMax = true}if(newVal < 0) {this.count = 0}},name: function() {this.count = 0this.isMax = false},isMax: function(newVal) {if (newVal) {console.log('注意:您购买的数量较大,请确认是否操作有误')}},list() {console.log('购物车数据发生改变')}}})</script></html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body style="background-color: #ccc;"> <div id="app"> <div>商品名称:<input v-model="name"/></div> <button v-on:click="cut">减一个</button> 购买数量{{count}} <button v-on:click="add">加一个</button> <button v-on:click="addCart">加入购物车</button> <div v-for="(item, index) in list" :key="index"> {{item.name}} x{{item.count}} </div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { name: '', count:0, isMax: false, list: [] }, methods: { cut() { this.count = this.count - 1 this.isMax = false }, add() { this.count = this.count + 1 }, addCart() { this.list.push({ name: this.name, count: this.count }) } }, watch: { count: function(newVal, oldVal) { if(newVal > 10) { this.isMax = true } if(newVal < 0) { this.count = 0 } }, name: function() { this.count = 0 this.isMax = false }, isMax: function(newVal) { if (newVal) { console.log('注意:您购买的数量较大,请确认是否操作有误') } }, list() { console.log('购物车数据发生改变') } } }) </script> </html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body style="background-color: #ccc;"> <div id="app"> <div>商品名称:<input v-model="name"/></div> <button v-on:click="cut">减一个</button> 购买数量{{count}} <button v-on:click="add">加一个</button> <button v-on:click="addCart">加入购物车</button> <div v-for="(item, index) in list" :key="index"> {{item.name}} x{{item.count}} </div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { name: '', count:0, isMax: false, list: [] }, methods: { cut() { this.count = this.count - 1 this.isMax = false }, add() { this.count = this.count + 1 }, addCart() { this.list.push({ name: this.name, count: this.count }) } }, watch: { count: function(newVal, oldVal) { if(newVal > 10) { this.isMax = true } if(newVal < 0) { this.count = 0 } }, name: function() { this.count = 0 this.isMax = false }, isMax: function(newVal) { if (newVal) { console.log('注意:您购买的数量较大,请确认是否操作有误') } }, list() { console.log('购物车数据发生改变') } } }) </script> </html>
代码解释:
在 html 代码中, 我们给出一个商品名称输入框以及添加数量和减少数量的两个按钮。
代码第 4-9 行,我们定义了四个数据,分别是:
name — 商品的名称
count — 商品的数量
isMax — 是否首次超过数量 10
list — 购物车列表
代码第 10-24 行,我们定义了三个方法,分别是:
cut — 将数量 count – 1
add — 将数量 count + 1
addCart — 修改购物车数据
代码第 25-49 行,我们定义了四个侦听器,分别是:
name — 对数据 name
侦听,触发时将 count
变成 0;并 将 isMax 变成 false
count — 对数据 count
侦听, 检测到 count
大于 10 时,将 isMax 变成 true,检测到 count
小于 0 的时候将 count
修改为 0。
isMax — 对数据 isMax 侦听,触发时,判断变化后的值是否为 true,当值为 true 时弹出提示消息
list — 对数据 list 侦听,每次改变时弹出提示消息。
3.2 示例二:侦听对象某个属性的变化
上面在示例一中学习了对字符串、布尔值、数字、数组类型的侦听,那么如何对对象的某个属性进行侦听呢?
我们对上述代码稍作修改:实例演示
<!DOCTYPE html><html lang="en" style="background-color: #ccc;"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><div id="app"><div>商品名称:<input v-model="product.name"/></div></div></body><script src="https://unpkg.com/vue/dist/vue.js"></script><script>var vm = new Vue({el: '#app',data: {product: {name: ''}},watch: {product: function(newValue){console.log(newValue)}}})</script></html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '' } }, watch: { product: function(newValue){ console.log(newValue) } } }) </script> </html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '' } }, watch: { product: function(newValue){ console.log(newValue) } } }) </script> </html>
代码解释:
第 11-13 行,我们定义来侦听器 product
。我们的理想效果是:当在输入框中输入商品名称的时候,在控制台中会打印出最新的 product
值。
从效果图中我们可以看出,实际的执行效果和我们想象中并不一致。当在输入框中输入商品名称的时候,侦听器 product
并没有触发。这是为什么呢?我们先带着这个疑问看下下面这段的代码:实例演示
<!DOCTYPE html><html lang="en" style="background-color: #ccc;"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><div id="app"><div>商品名称:<input v-model="product.name"/></div></div></body><script src="https://unpkg.com/vue/dist/vue.js"></script><script>var vm = new Vue({el: '#app',data: {product: {name: ''}},watch: {product: function(newValue){console.log(newValue)}},mounted() {this.product = {name: 123}}})</script></html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '' } }, watch: { product: function(newValue){ console.log(newValue) } }, mounted() { this.product = {name: 123} } }) </script> </html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '' } }, watch: { product: function(newValue){ console.log(newValue) } }, mounted() { this.product = {name: 123} } }) </script> </html>
代码解释:
第 11-13 行,我们定义了侦听器 product
,并在生命周期 mounted
函数中修改 product
的值。运行代码,我们惊奇地发现侦听器会在一开始的时候触发,输入框中输入的时候同样不会触发。
大部分同学看到这里应该已经猜到之前为什么修改name
不会触发侦听器。因为product
指向的是一个引用地址,在第一个例子中,我们只修改了product
的name
属性,而没有修改product
的引用地址。而在第二个例子中,我们给 product
重新赋值,因此product
的引用地址也发生了改变,所以可以成功触发侦听器。
通过这个结论,如果只想监听 product
的 name
属性的变化,可以对代码进行如下修改:实例演示
<!DOCTYPE html><html lang="en" style="background-color: #ccc;"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><div id="app"><div>商品名称:<input v-model="product.name"/></div></div></body><script src="https://unpkg.com/vue/dist/vue.js"></script><script>var vm = new Vue({el: '#app',data: {product: {name: ''}},watch: {'product.name': function(newValue){console.log(newValue)}}})</script></html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '' } }, watch: { 'product.name': function(newValue){ console.log(newValue) } } }) </script> </html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '' } }, watch: { 'product.name': function(newValue){ console.log(newValue) } } }) </script> </html>
代码解释:
第 10-12 行,我们定义了侦听器 product.name
,对 product
的 name
属性进行监听。当 name
值发生变化的时候触发侦听器。
4. 侦听器的高级用法
在之前的例子中,我们都是给侦听器赋值一个函数,实际上它还可以用对象的形式来定义。
4.1 handler方法
我们给 name
绑定了一个 handler
方法,之前我们写的 watch
方法其实默认写的就是这个handler
。当 name
发生改变时, handler
方法就会执行。实例演示
<!DOCTYPE html><html lang="en" style="background-color: #ccc;"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body style="background-color: #ccc;"><div id="app"><div>商品名称:<input v-model="name"/></div></div></body><script src="https://unpkg.com/vue/dist/vue.js"></script><script>var vm = new Vue({el: '#app',data: {name: '方便面'},watch: {name: {handler(newVal, oldVal) {console.log(newVal)}}}})</script></html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body style="background-color: #ccc;"> <div id="app"> <div>商品名称:<input v-model="name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { name: '方便面' }, watch: { name: { handler(newVal, oldVal) { console.log(newVal) } } } }) </script> </html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body style="background-color: #ccc;"> <div id="app"> <div>商品名称:<input v-model="name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { name: '方便面' }, watch: { name: { handler(newVal, oldVal) { console.log(newVal) } } } }) </script> </html>
代码解释:
第 7-11 行,我们定义了侦听器 name
。它是一个对象,当 name
发生变化的时候,会调用 handler
的方法。。
4.2 immediate属性
有时候你可能需要在侦听器最初绑定的时候就触发一次,这个时候我们就需要用到immediate
属性。
示例:实例演示
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><div id="app"><div>商品名称:<input v-model="name"/></div></div></body><script src="https://unpkg.com/vue/dist/vue.js"></script><script>var vm = new Vue({el: '#app',data: {name: '方便面'},watch: {name: {handler(newVal, oldVal) {console.log(newVal)},// 代表在wacth里声明了name这个方法之后立即先去执行一次handler方法immediate: true}}})</script></html><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { name: '方便面' }, watch: { name: { handler(newVal, oldVal) { console.log(newVal) }, // 代表在wacth里声明了name这个方法之后立即先去执行一次handler方法 immediate: true } } }) </script> </html><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="name"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { name: '方便面' }, watch: { name: { handler(newVal, oldVal) { console.log(newVal) }, // 代表在wacth里声明了name这个方法之后立即先去执行一次handler方法 immediate: true } } }) </script> </html>
代码解释:
第 13 行,我们给侦听器 name
一个 immediate
属性,它的值为 true。这个时候,当页面第一次渲染的时候就会触发侦听器的 handler
函数。
4.3 deep属性
在之前的 3.2 中我们学习了如何对对象的某个属性做侦听:通过指明对象的某个属性来。假如现在有以下代码:实例演示
<!DOCTYPE html><html lang="en" style="background-color: #ccc;"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><div id="app"><div>商品名称:<input v-model="product.name"/></div><div>商品数量:<input v-model="product.count"/></div><div>商品标题:<input v-model="product.title"/></div></div></body><script src="https://unpkg.com/vue/dist/vue.js"></script><script>var vm = new Vue({el: '#app',data: {product: {name: '',count: '',title: ''}},watch: {'product.name': function(newValue){console.log(newValue)},'product.count': function(newValue){console.log(newValue)},'product.title': function(newValue){console.log(newValue)}}})</script></html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> <div>商品数量:<input v-model="product.count"/></div> <div>商品标题:<input v-model="product.title"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '', count: '', title: '' } }, watch: { 'product.name': function(newValue){ console.log(newValue) }, 'product.count': function(newValue){ console.log(newValue) }, 'product.title': function(newValue){ console.log(newValue) } } }) </script> </html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> <div>商品数量:<input v-model="product.count"/></div> <div>商品标题:<input v-model="product.title"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '', count: '', title: '' } }, watch: { 'product.name': function(newValue){ console.log(newValue) }, 'product.count': function(newValue){ console.log(newValue) }, 'product.title': function(newValue){ console.log(newValue) } } }) </script> </html>
代码解释:
在 watch
属性中,我们写了三个侦听器,都是针对 product
的某个属性进行侦听的。那有没有什么更简单的方法可以实现当 product
里面任何属性发生变化的时候就执行侦听呢?
这里就需要用到 deep
属性。deep
属性代表是否深度监听,默认值是 false。当设置为 true 时,会对对象里面的每个属性进行侦听。
示例:实例演示
<!DOCTYPE html><html lang="en" style="background-color: #ccc;"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><div id="app"><div>商品名称:<input v-model="product.name"/></div><div>商品数量:<input v-model="product.count"/></div><div>商品标题:<input v-model="product.title"/></div></div></body><script src="https://unpkg.com/vue/dist/vue.js"></script><script>var vm = new Vue({el: '#app',data: {product: {name: '',count: '',title: ''}},watch: {product: {handler: function(newVal) {console.log(newVal)},deep: true}}})</script></html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> <div>商品数量:<input v-model="product.count"/></div> <div>商品标题:<input v-model="product.title"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '', count: '', title: '' } }, watch: { product: { handler: function(newVal) { console.log(newVal) }, deep: true } } }) </script> </html><!DOCTYPE html> <html lang="en" style="background-color: #ccc;"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <div>商品名称:<input v-model="product.name"/></div> <div>商品数量:<input v-model="product.count"/></div> <div>商品标题:<input v-model="product.title"/></div> </div> </body> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { product: { name: '', count: '', title: '' } }, watch: { product: { handler: function(newVal) { console.log(newVal) }, deep: true } } }) </script> </html>
代码解释:
第 17 行,我们加了一个属性 deep
,deep
的意思就是深入观察,监听器会一层层地往下遍历,给对象的所有属性都加上这个监听器,修改对象里面任何一个属性都会触发这个监听器里的 handler。
暂无评论内容