前端面试题之watch和computed和它们之间的异步问题

103 阅读2分钟

computed 和 watch 的区别是什么?

computed

  • 它是计算属性。主要用于值的计算并一般会返回一个值。所以它更多⽤于计算值的场景
  • 它具有缓存性。当访问它来获取值时,它的 getter 函数所计算出来的值会进行缓存
  • 只有当它依赖的属性值发生了改变,那下⼀次再访问时才会重新调⽤ getter 函数来计算
  • 它适⽤于计算⽐较消耗性能的计算场景
  • 必须要有一个返回值

watch

  • 它更多的是起到 “观察” 的作⽤,类似于对数据进行变化的监听并执行回调。
  • 主要⽤于观察 props 或 本组件 data 的值,当这些值发生变化时,执⾏处理操作
  • 可以监听数据发生变化,可以监听的数据有(props、data、computed、$route) watch 侦听器如果监听的是一个对象,需要开启深度监听
  • 不一定要返回某个值、
watch:{
  num:{
    // 监听数据发生变化的处理函数
    handler(newNum) {
      console.log(newNum)
    },
    // 是否开启深度监听
    deep: true
  }
}

如果想实现首次监听配置 immediate 为 true

建议

  • 当目的是进⾏数值计算,且依赖于其他数据,那么推荐使用 computed
  • 当需要在某个数据发生变化的, 同时做⼀些稍复杂的逻辑操作,那么推荐使⽤ watch

computed 和 watch哪个可以异步

计算属性能(computed)完成的功能监听属性都能完成

比如计算两个数的和

<body>
    <div id="app">
        <p>总和{{adden}}</p>
        输入的值1<input type="text" v-model:value="a" >
        输入的值2<input type="text" v-model:value="b">
    </div>
    <script src="../vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                a:"",
                b:"",
                adden:"",
            },
            //监听属性实现
            watch:{
               a(val){
                   this.adden=this.b*1+val*1
               },
               b(val){
                   this.adden=this.a*1+val*1
               }
            },
            // 计算属性实现
            // computed: {
            //  adden(){
            //      console.log(this);
            //     return this.a*1+this.b*1
                   
            //  }
            // },
        })
    </script>
</body>

计算属性(computed)不能进行异步操作

因为计算属性(computed)是通过return返回值传递参数 异步操作的时候return是没有意义的

以定时器(setTimeout)举例

很明显58行打印的并不是setTimeout中return的值,而是对应的setTimtout()的 ID

详情可以看

setTimeout() - 简书 (jianshu.com) www.jianshu.com/p/9b389c99d… 异步函数的返回值都不是用return返回的

所以vue中的计算属性(computed)不能使用异步函数

如果想让两个数的和隔一秒在出现 计算函数是不行的

<body>
   <div id="app">
       <p>总和{{adden}}</p>
       输入的值1<input type="text" v-model:value="a" >
       输入的值2<input type="text" v-model:value="b">
   </div>
   <script src="../vue.js"></script>
   <script>
       var vm = new Vue({
           el: '#app',
           data: {
               a:"",
               b:"",
               // adden:"",
           },
           //监听属性
           // watch:{
           //    a(val){
           //        this.adden=this.b*1+val*1
           //    },
           //    b(val){
           //        this.adden=this.a*1+val*1
           //    }
           // },
<**  这是错误的 **>
           computed: {
            adden(){
                console.log(this);
               return setTimeout(()=>{
                    console.log(this.a);
                    return this.a*1 + this.b*1
                },1000)
                  
            }
           },
       })
   </script>
</body>

此时adden返回的是setTimeout的Id

监听属性(watch)

当被监听的属性变化时会自动调用预定义函数

watch可以进行异步操作

    <body>
    <div id="app">
        <p>总和{{adden}}</p>
        输入的值1<input type="text" v-model:value="a" >
        输入的值2<input type="text" v-model:value="b">
    </div>
    <script src="../vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                a:"",
                b:"",
                adden:0,
            },
            //监听属性
            watch:{
               a(val){
                   setTimeout(()=>{
                       this.adden=this.b*1+val*1
                   },1000)
                   
               },
               b(val){
                   this.adden=this.a*1+val*1
               }
            },
            // computed: {
            //  adden(){
            //      console.log(this);
            //     return setTimeout(()=>{
            //          console.log(this.a);
            //          return this.a*1 + this.b*1
            //      },1000)
                   
            //  }
            // },
        })
    </script>
</body>

以定时器为例

所有不被vue管理的函数(定时器的回调,ajax的回调函数等)最好写成箭头函数这样this的指向才是vm或者组件实例对象,被vue管理的函数最好不要使用箭头函数,this会指向为被定义(箭头函数绑定的是父级作用域的上下文)

原文链接:blog.csdn.net/qq_64209130…