1 简介

  所谓监听就是对内置对象的状态或者属性变化进行监听并且做出反应的响应,监听属性,意思就是可以监视其他数据的变化

 

2 使用

  使用watch配置项,在配置项里面写上要监视的属性,每次属性值的变化都会触发handler函数回调,也可以监视计算属性的变化

 

2.1 语法格式

  其中,属性名表示监视的data里面的属性或者计算属性里面的属性

  handler是监视回调的方法,它有两个参数:新的值和旧的值

  配置项:可以配置也可以不配置

watch:{
        属性名:{
          handler(newValue,oldValue){
            console.log('新值:'+newValue + "---旧值:"+oldValue)
          },
          配置项:值,
          配置项:值
        }
      }    

 

2.2 简写格式

  当只有handler回调函数而没有其他配置项的时候,可以简写成下面格式

watch:{
        属性名(newValue,oldValue){
            console.log('新值:'+newValue + "---旧值:"+oldValue)
          }
      }

 

2.3 示例1-没有其他配置项

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州'
      },
      watch:{
        name(newValue,oldValue){
            console.log('新值:'+newValue + "---旧值:"+oldValue)
          }
      }
     })
    
     </script>
    </body>

如下图,初始化的时候,监视方法没有调用

 

  如下图,在修改第一个name输入框的时候,监视方法调用了

 

2.4 示例2 添加配置项 immediate

  immediate,默认值是false,设置为true时,在被监视的值初始化的时候调用一次监视函数

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州'
      },
      watch:{
        name:{
            immediate:true,
          handler(newValue,oldValue){
            console.log('新值:'+newValue + "---旧值:"+oldValue)
          }
        }
      }
     })
    
     </script>
    </body>

如下图,在初始化的时候,监视方法就调用了。且发现旧值是undifined,因为只没有改变过,所以没有旧值,就是undifined了

 

3 深度监视

3.1 示例1 监视的属性是个对象

  监视person这个属性,它是个对象

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
     <h1>{{person}}</h1>
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州',
       person:{
        sex:'',
        age:20
       }
      },
      watch:{
        person:{
          immediate:true,
          handler(newValue,oldValue){
            console.log('新值:'+newValue + "---旧值:"+oldValue)
          }
        }
      }
     })
    
     </script>
    </body>

如下图,我们去修改person.age和person.sex的时候,发现监视方法没有被调用。只有使用一个新的对象替换原对象的时候,监视方法才调用了

因为,被监视的属性是对象的时候,它监视的是整个对象。可以理解为被监视的这个属性持有的值实际上是对象的地址,所以监视的时候实际上是在监视这个地址有没有发生变化

 

3.2 监视对象中某个属性

  监视person的age属性

  注意:watch里面'person.age'需要加上引号。虽然我们平时写这些属性都可以不加引号,实际上是vue给我们自动加上了,但是在这里需要我们自己手动写上引号

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
     <h1>{{person}}</h1>
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州',
       person:{
        sex:'',
        age:20
       }
      },
      watch:{
        'person.age':{
          immediate:true,
          handler(newValue,oldValue){
            console.log('新值:'+newValue + "---旧值:"+oldValue)
          }
        }
      }
     })
    
     </script>
    </body>

如下图,修改person.age时监视方法发生变化

 

3.3 监视对象的所有属性 配置项deep

  deep:是否进行深度监视,默认false,配置为true,深度监视对象里面每个属性(包括多个层级)

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
     <h1>{{person}}</h1>
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州',
       person:{
        sex:'',
        age:20,
        house:{
            'size':'120',
            'floor':12
        }
       }
      },
      watch:{
        person:{
          immediate:true,
          deep:true,
          handler(newValue,oldValue){
            console.log('新值:'+newValue + "---旧值:"+oldValue)
          }
        }
      }
     })
    
     </script>
    </body>

  如下图,修改person.age和person.house.floor,监视方法都会被调用

 

 

 

 

 

4 监视属性和计算属性的区别

  1)computed能实现的,watch都能实现,computed不能实现的,watch也能实现

  2)计算属性不能执行异步任务。计算属性一般不会用来向服务器请求或者执行异步任务(如setTimeOut),因为耗时可能会比较长,我们的计算属性要实时更新。所以这个异步任务就可以用监听属性来做

 

4.1 示例

  我们要实现下面这个标题的值是两个输入框的值的拼接

  分别使用监视属性和计算属性实现

 

4.1.1 监视属性实现

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
     <h1>{{nameAndAddress}}</h1>
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州',
       nameAndAddress:''
       }
      ,
      watch:{
        name:{
          immediate:true,
          handler(newValue,oldValue){
            this.nameAndAddress = this.name + this.address
          }
        },
        address:{
          immediate:true,
          handler(newValue,oldValue){
            this.nameAndAddress = this.name + this.address
          }
        }
      } 
     })
    
     </script>
    </body>

 

4.1.2 计算属性实现

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
     <h1>{{nameAndAddress}}</h1>
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州'
      },
      computed:{
        nameAndAddress(){
            return this.name+this.address
        }
      }
    })
      
    
     </script>
    </body>

 

4.2 示例2

  在上面的基础上,要求两个输入框的值变化时,标题的值要延迟2秒后再变化

 

4.2.1 监视属性

  注意setTimeout里面写的是箭头函数,这样子它就没有自己的this,this往外层找,就能找到vm对象 

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
     <h1>{{nameAndAddress}}</h1>
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州',
       nameAndAddress:''
       }
      ,
      watch:{
        name:{
          immediate:true,
          handler(newValue,oldValue){
            console.log('111')
            setTimeout(()=>{
                 this.nameAndAddress = this.name + this.address
            },2000)
            
          }
        },
        address:{
          immediate:true,
          handler(newValue,oldValue){
            console.log('111')
            setTimeout (()=>{
                 this.nameAndAddress = this.name + this.address
            },2000)
            
          }
        }
      }
     })
    
     </script>
    </body>

 

4.2.2 计算属性

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
     <h1>{{nameAndAddress}}</h1>
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州'
      },
      computed:{
        nameAndAddress(){
            setTimeout(()=>{
                this.name+this.address
            },2000)
            
        }
      }
    })
      
     </script>
    </body>

如下图,我们发现标题压根没有值。

 

我们要知道,计算属性的值依赖的是getter函数的返回值。

如下图这么编码,计算属性nameAndAddress是没有返回值的,也就是它的getter函数式没有返回值的,所以该计算属性就没有值

nameAndAddress(){
            setTimeout(()=>{
                this.name+this.address
            },2000)
            
        }

它只能下面这么编码,但是这样子又不能实现等2秒的效果

nameAndAddress(){
            setTimeout(()=>{
                 this.name+this.address
            },2000)
            return this.name+this.address
        }

 

4.3 注意细节

  在vue示例里面,所有被vue管理的函数,全部写成普通函数,这样子this就可以获取到vm对象或者组件对象

  所有不被vue管理的函数,最好写成箭头函数,这样子this就可以获取到vm对象或者组件对象

<body>

    <div id="root">
     <input v-model="name"></input> <br/><br/>
     <input v-model="address" ></input> <br/>
    </div>
   
    <script type="text/javascript" >
   
     const vm = new Vue({
      el:'#root',
      data:{
       name:'历史',
       address:'杭州'
      },
      watch:{
        name:{
          handler(newValue,oldValue){
            console.log('新值:'+newValue + "---旧值:"+oldValue)
          }
        }
      }
     })
   
     </script>
    </body>