前端-一篇彻底搞懂Vue(computed,watch,methods)

一. 理解Vue中的computed用法

computed是计算属性的; 它会根据所依赖的数据动态显示新的计算结果, 该计算结果会被缓存起来。computed的值在getter执行后是会被缓存的。如果所依赖的数据发生改变时候, 就会重新调用getter来计算最新的结果。

下面我们根据官网中的demo来理解下computed的使用及何时使用computed。

computed设计的初衷是为了使模板中的逻辑运算更简单, 比如在Vue模板中有很多复杂的数据计算的话, 我们可以把该计算逻辑放入到computed中去计算。

下面我们看下官网中的一个demo如下:

<!DOCTYPE html>
<html>
<head>
  <title>vue</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    {
    
    {
    
     msg.split('').reverse().join('') }}
  </div>
  <script type="text/javascript">
    new Vue({
    
    
      el: '#app',
      data: {
    
    
        msg: 'hello'
      }
    });
  </script>
</body>
</html>

如上代码, 我们的data属性中的msg默认值为 ‘hello’; 然后我们在vue模板中会对该数据值进行反转操作后输出数据, 因此在页面上就会显示 ‘olleh’; 这样的数据。这是一个简单的运算, 但是如果页面中的运算比这个还更复杂的话, 这个时候我们可以使用computed来进行计算属性值, computed的目的就是能使模板中的运算逻辑更简单。因此我们现在需要把上面的代码改写成下面如下代码:

<!DOCTYPE html>
<html>
<head>
  <title>vue</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <p>原来的数据: {
    
    {
    
     msg }}</p>
    <p>反转后的数据为: {
    
    {
    
     reversedMsg }}</p>
  </div>
  <script type="text/javascript">
    var vm = new Vue({
    
    
      el: '#app',
      data: {
    
    
        msg: 'hello'
      },
      computed: {
    
    
        reversedMsg() {
    
    
          // this 指向 vm 实例
          return this.msg.split('').reverse().join('')
        }
      }
    });
  </script>
</body>
</html>

如上代码, 我们在computed中声明了一个计算属性 reversedMsg。我们提供的 reversedMsg 函数, 将用作属性 vm.reversedMsg 的getter函数; 我们可以在上面实例化后代码中, 打印如下信息:
console.log(vm);

打印信息如下所示, 我们可以看到 vm.reversedMsg = ‘olleh’;

在这里插入图片描述
我们也可以打开控制台, 当我们修改 vm.msg 的值后, vm.reversedMsg 的值也会发生改变,如下控制台打印的信息可知:
在这里插入图片描述
如上打印的信息我们可以看得到, 我们的 vm.reversedMsg 的值依赖于 vm.msg 的值,当vm.msg的值发生改变时, vm.reversedMsg 的值也会得到更新。

computed 应用场景

  1. 适用于一些重复使用数据或复杂及费时的运算。我们可以把它放入computed中进行计算, 然后会在computed中缓存起来, 下次就可以直接获取了。

  2. 如果我们需要的数据依赖于其他的数据的话, 我们可以把该数据设计为computed中。

二:computed 和 methods的区别?

如上demo代码, 如果我们通过在表达式中调用方法也可以达到同样的效果, 现在我们把代码改成方法, 如下代码:

<!DOCTYPE html>
<html>
<head>
  <title>vue</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <p>原来的数据: {
    
    {
    
     msg }}</p>
    <p>反转后的数据为: {
    
    {
    
     reversedMsg() }}</p>
  </div>
  <script type="text/javascript">
    var vm = new Vue({
    
    
      el: '#app',
      data: {
    
    
        msg: 'hello'
      },
      /*
      computed: {
        reversedMsg() {
          // this 指向 vm 实例
          return this.msg.split('').reverse().join('')
        }
      }
      */
      methods: {
    
    
        reversedMsg() {
    
    
          // this 指向 vm 实例
          return this.msg.split('').reverse().join('')
        }
      }
    });
    console.log(vm);
  </script>
</body>
</html>

如上代码, 我们反转后的数据在模板中调用的是方法 reversedMsg(); 该方法在methods中也定义了。那么也可以实现同样的效果, 那么他们之间到底有什么区别呢?

区别是:

  1. computed 是基于响应性依赖来进行缓存的。只有在响应式依赖发生改变时它们才会重新求值, 也就是说, 当msg属性值没有发生改变时, 多次访问 reversedMsg 计算属性会立即返回之前缓存的计算结果, 而不会再次执行computed中的函数。但是methods方法中是每次调用, 都会执行函数的, methods它不是响应式的。
  2. computed中的成员可以只定义一个函数作为只读属性, 也可以定义成 get/set变成可读写属性, 但是methods中的成员没有这样的。

我们可以再看下如下demo:

<!DOCTYPE html>
<html>
<head>
  <title>vue</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <div>第一次调用computed属性: {
    
    {
    
     reversedMsg }}</div>
    <div>第二次调用computed属性: {
    
    {
    
     reversedMsg }}</div>
    <div>第三次调用computed属性: {
    
    {
    
     reversedMsg }}</div>
    <!-- 下面是methods调用 -->
    <div>第一次调用methods方法: {
    
    {
    
     reversedMsg1() }}</div>
    <div>第二次调用methods方法: {
    
    {
    
     reversedMsg1() }}</div>
    <div>第三次调用methods方法: {
    
    {
    
     reversedMsg1() }}</div>
  </div>
  <script type="text/javascript">
    var vm = new Vue({
    
    
      el: '#app',
      data: {
    
    
        msg: 'hello'
      },
      computed: {
    
    
        reversedMsg() {
    
    
          console.log(1111);
          // this 指向 vm 实例
          return this.msg.split('').reverse().join('')
        }
      },
      methods: {
    
    
        reversedMsg1() {
    
    
          console.log(2222);
          // this 指向 vm 实例
          return this.msg.split('').reverse().join('')
        }
      }
    });
    console.log(vm);
  </script>
</body>
</html>

执行后的结果如下所示:
在这里插入图片描述
如上代码我们可以看到, 在computed中有属性reversedMsg, 然后在该方法中会打印 1111; 信息出来, 在methods中的方法reversedMsg1也会打印 2222 信息出来, 但是在computed中, 我们除了第一次之后,再次获取reversedMsg值后拿得是缓存里面的数据, 因此就不会再执行该reversedMsg函数了。但是在methods中, 并没有缓存, 每次执行reversedMsg1()方法后,都会打印信息。
从上面截图信息我们就可以验证的。

那么我们现在再来理解下缓存的作用是什么呢? computed为什么需要缓存呢? 我们都知道我们的http也有缓存, 对于一些静态资源, 我们nginx服务器会缓存我们的静态资源,如果静态资源没有发生任何改变的话, 会直接从缓存里面去读取,这样就不会重新去请求服务器数据, 也就是避免了一些无畏的请求, 提高了访问速度, 优化了用户体验。

对于我们computed的也是一样的。如上面代码, 我们调用了computed中的reversedMsg方法一共有三次,如果我们也有上百次调用或上千次调用的话, 如果依赖的数据没有改变, 那么每次调用都要去计算一遍, 那么肯定会造成很大的浪费。因此computed就是来优化这件事的。

三:Vue中的watch的用法

watch它是一个对data的数据监听回调, 当依赖的data的数据变化时, 会执行回调。在回调中会传入newVal和oldVal两个参数。
Vue实列将会在实例化时调用$watch(), 他会遍历watch对象的每一个属性。

watch的使用场景是:当在data中的某个数据发生变化时, 我们需要做一些操作, 或者当需要在数据变化时执行异步或开销较大的操作时. 我们就可以使用watch来进行监听。
watch普通监听和深度监听

如下普通监听数据的基本测试代码如下:

<!DOCTYPE html>
<html>
<head>
  <title>vue</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <p>空智个人信息情况: {
    
    {
    
     basicMsg }}</p>
    <p>空智今年的年龄: <input type="text" v-model="age" /></p>
  </div>
  <script type="text/javascript">
    var vm = new Vue({
    
    
      el: '#app',
      data: {
    
    
        basicMsg: '',
        age: 31,
        single: '单身'
      },
      watch: {
    
    
        age(newVal, oldVal) {
    
    
          this.basicMsg = '今年' + newVal + '岁' + ' ' + this.single;
        }
      }
    });
  </script>
</body>
</html>

显示效果如下:
在这里插入图片描述
如上代码, 当我们在input输入框中输入年龄后, 比如32, 那么watch就能对 ‘age’ 这个属性进行监听,当值发生改变的时候, 就会把最新的计算结果赋值给 ‘basicMsg’ 属性值, 因此最后在页面上就会显示 ‘basicMsg’ 属性值了。

理解handler方法及immediate属性

如上watch有一个特点是: 第一次初始化页面的时候, 是不会去执行age这个属性监听的, 只有当age值发生改变的时候才会执行监听计算. 因此我们上面第一次初始化页面的时候, ‘basicMsg’ 属性值默认为空字符串。那么我们现在想要第一次初始化页面的时候也希望它能够执行 ‘age’ 进行监听, 最后能把结果返回给 ‘basicMsg’ 值来。因此我们需要修改下我们的 watch的方法,需要引入handler方法和immediate属性, 代码如下所示:

<!DOCTYPE html>
<html>
<head>
  <title>vue</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <p>空智个人信息情况: {
    
    {
    
     basicMsg }}</p>
    <p>空智今年的年龄: <input type="text" v-model="age" /></p>
  </div>
  <script type="text/javascript">
    var vm = new Vue({
    
    
      el: '#app',
      data: {
    
    
        basicMsg: '',
        age: 31,
        single: '单身'
      },
      watch: {
    
    
        age: {
    
    
          handler(newVal, oldVal) {
    
    
            this.basicMsg = '今年' + newVal + '岁' + ' ' + this.single;
          },
          immediate: true
        }
      }
    });
  </script>
</body>
</html>

如上代码, 我们给我们的age属性绑定了一个handler方法。其实我们之前的watch当中的方法默认就是这个handler方法。但是在这里我们使用了immediate: true; 属性,含义是: 如果在watch里面声明了age的话, 就会立即执行里面的handler方法。如果 immediate 值为false的话,那么效果就和之前的一样, 就不会立即执行handler这个方法的。因此设置了 immediate:true的话,第一次页面加载的时候也会执行该handler函数的。即第一次 basicMsg 有值。

因此第一次页面初始化效果如下:

在这里插入图片描述
理解deep属性

watch里面有一个属性为deep,含义是:是否深度监听某个对象的值, 该值默认为false。

如下测试代码:

<!DOCTYPE html>
<html>
<head>
  <title>vue</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <p>空智个人信息情况: {
    
    {
    
     basicMsg }}</p>
    <p>空智今年的年龄: <input type="text" v-model="obj.age" /></p>
  </div>
  <script type="text/javascript">
    var vm = new Vue({
    
    
      el: '#app',
      data: {
    
    
        obj: {
    
    
          basicMsg: '',
          age: 31,
          single: '单身'
        }
      },
      watch: {
    
    
        'obj': {
    
    
          handler(newVal, oldVal) {
    
    
            this.basicMsg = '今年' + newVal.age + '岁' + ' ' + this.obj.single;
          },
          immediate: true,
          deep: true // 需要添加deep为true即可对obj进行深度监听
        }
      }
    });
  </script>
</body>
</html>

如上测试代码, 如果我们不把 deep: true添加的话,当我们在输入框中输入值的时候,改变obj.age值后,obj对象中的handler函数是不会被执行到的。受JS的限制, Vue不能检测到对象属性的添加或删除的。它只能监听到obj这个对象的变化,比如说对obj赋值操作会被监听到。比如在mounted事件钩子函数中对我们的obj进行重新赋值操作, 如下代码:

mounted() {
    
    
  this.obj = {
    
    
    age: 22,
    basicMsg: '',
    single: '单身'
  };
}

最后我们的页面会被渲染到 age 为 22; 因此这样我们的handler函数才会被执行到。如果我们需要监听对象中的某个属性值的话, 我们可以使用 deep设置为true即可生效。deep实现机制是: 监听器会一层层的往下遍历, 给对象的所有属性都加上这个监听器。当然性能开销会非常大的。

当然我们可以直接对对象中的某个属性进行监听的,比如就对 ‘obj.age’ 来进行监听, 如下代码也是可以生效的。

<!DOCTYPE html>
<html>
<head>
  <title>vue</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <p>空智个人信息情况: {
    
    {
    
     basicMsg }}</p>
    <p>空智今年的年龄: <input type="text" v-model="obj.age" /></p>
  </div>
  <script type="text/javascript">
    var vm = new Vue({
    
    
      el: '#app',
      data: {
    
    
        obj: {
    
    
          basicMsg: '',
          age: 31,
          single: '单身'
        }
      },
      watch: {
    
    
        'obj.age': {
    
    
          handler(newVal, oldVal) {
    
    
            this.basicMsg = '今年' + newVal + '岁' + ' ' + this.obj.single;
          },
          immediate: true,
          // deep: true // 需要添加deep为true即可对obj进行深度监听
        }
      }
    });
  </script>
</body>
</html>

watch 和 computed的区别是:

相同点:他们两者都是观察页面数据变化的。

不同点:computed只有当依赖的数据变化时才会计算, 当数据没有变化时, 它会读取缓存数据。
watch每次都需要执行函数。watch更适用于数据变化时的异步操作。

四:computed的基本原理

computed上面我们也已经说过, 它设计的初衷是: 为了使模板中的逻辑运算更简单。它有两大优势:

  1. 使模板中的逻辑更清晰, 方便代码管理。
  2. 计算之后的值会被缓存起来, 依赖的data值改变后会重新计算。

其他

参考: https://www.cnblogs.com/tugenhua0707/p/11760466.html

猜你喜欢

转载自blog.csdn.net/zhanggqianglovec/article/details/124439339