彻底理解Vue中的计算属性(computed)、方法(methods)和侦听器(watch)
前言:在学习Vue这个框架的时候,我们都会接触到里面的计算属性,方法,侦听器,但是对于初学者来说,很容易把这些概念混淆,所以准备写一篇文章来彻底讲清楚这三个属性。
对于初学者来说,可能还不清楚计算属性、方法、侦听器这些属性该写在哪里,我这里就简单写一个vue2的模板,当然代码不是千篇一律的,每个人的代码格式都可能不同,但是大概的规范是要遵循的,我这里只是列举的一个比较常见的写法。如果你是Vue大佬,那么请忽略这一点。
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script type='text/javascript'>
const options = {
//指定你这个Vue实例需要挂载到哪里,或者说哪个容器
el:'#app',
//数据
data() {
return {
count: 123
}
},
//方法
methods: {
},
//计算属性
computed: {
},
//侦听器
watch: {
}
}
const vm = new Vue(options);
</script>
在学习计算属性(computed)之前,我们先来理解理解计算属性这四个字,computed这个英文单词是计算的意思,那么我们可以思考下,计算什么呢?这里给出一个简单的例子来帮助大家理解
我们知道,可以在Vue的插值中写表达式比如这样:
<body>
<div id='app'>{{books.length>0?'isOk':'notOk'}}</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script type='text/javascript'>
const options = {
el:'#app',
data() {
return {
books: ['西游记','水浒传','三国演义','红楼梦']
}
},
methods: {
},
computed: {
},
watch: {
}
}
const vm = new Vue(options);
</script>
</body>
此时页面上展示的效果肯定是isOk,为什么呢?因为books.length>0?‘isOk’:'notOk’计算出来的值就是isOk,在Vue的插值语法中,写的就是表达式。到这里我们可以想想看,books.length>0?‘isOk’:'notOk’这个表达式像不像是在计算某个值,这里就相当于是这个表达式计算出了isOk这个值,然后渲染到页面上去。我们可以这样理解计算这个词。
但是有一个问题,就是如果我们的页面上有很多地方都需要用到这个计算过程呢?难道我们每个地方都去写一遍这个吗,那不是多写了好多代码吗,而且我们并不知道这个表达式的复杂程度。就比如这样,我们可能在很多地方都用到这个计算出来的值,那么我们每个地方都去写这个表达式那就太麻烦了,当然你现在可能会觉得不麻烦,就这?写这点东西算个啥呀,感觉还好啊。其实不是这样的,因为在未来的前端开发中,你所接触到的代码量不可能只是我这一点,你会编写很多的代码,到时候就比较复杂了。
<div id='app'>
<div>{{books.length>0?'isOk':'notOk'}}</div>
<p>{{books.length>0?'isOk':'notOk'}}</p>
<a href="###">{{books.length>0?'isOk':'notOk'}}</a>
</div>
此时,Vue就给了我们一个computed属性,我们就叫他计算属性,
可以理解成这个属性就是用来计算的。那我们怎么写呢?computed里面写什么呢?别急,我来给你演示下
<body>
<div id='app'>
<div>{{books.length>0?'isOk':'notOk'}}</div>
<p>{{books.length>0?'isOk':'notOk'}}</p>
<a href="###">{{books.length>0?'isOk':'notOk'}}</a>
<div>{{myFirstComputed}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script type='text/javascript'>
const options = {
el:'#app',
data() {
return {
books: ['西游记','水浒传','三国演义','红楼梦']
}
},
methods: {
},
computed: {
myFirstComputed() {
return this.books.length>0?'isOk':'notOk'
}
},
watch: {
}
}
const vm = new Vue(options);
</script>
</body>
像这样,直接将函数编写在computed里面就可以了,然后再{{}}中编写就好了。
这里解释一下,我们再computed里面编写的函数都会被挂载在Vue实例上面去,所以我们直接使用就可以了。我们在控制台打印一下vm,返回的数据中就有我们刚才写的函数
有了这个computed,我们就可以少些很多代码了,相当与我们把很多代码直接封装到一个地方,需要用的地方直接调这个属性名就可以了。
讲到这里,我需要大家都记住一句话,Vue官方文档里面的话“对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性。”后面会用到这句话。
2. ### 方法(methods)
依葫芦画瓢,方法和计算属性相同点,就是将函数写在methods中,需要用的地方直接调用就是了。
<body>
<div id='app'>
<div>{{books.length>0?'isOk':'notOk'}}</div>
<p>{{books.length>0?'isOk':'notOk'}}</p>
<a href="###">{{books.length>0?'isOk':'notOk'}}</a>
<div>{{myFirstComputed}}</div>
<div>{{myFirstMethod()}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script type='text/javascript'>
const options = {
el:'#app',
data() {
return {
books: ['西游记','水浒传','三国演义','红楼梦']
}
},
methods: {
myFirstMethod() {
return this.books.length>0?'isOk':'notOk'
}
},
computed: {
myFirstComputed() {
return this.books.length>0?'isOk':'notOk'
}
},
watch: {
}
}
const vm = new Vue(options);
console.log(vm);
</script>
</body>
打印vm,多了一个函数,这里和计算属性有点区别,大家都可以看出来,多添加的这个属性是一个函数,所以我们调用的时候就应该写成函数的形式。
综上所述,我们发现计算属性能实现的效果,我们用方法也能实现,也就是说,两种方式的最终结果确实是完全相同的。那他们的区别在哪里呢?
计算属性和方法的区别:让我们先来想想之前说的那句话,“对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性。”为什么对于任何包含响应式数据的复杂逻辑,都应该使用计算属性,原因时计算属性是基于它们的响应依赖关系缓存的,计算属性只在相关响应式依赖发生改变时它们才会重新求值。也就是说我们的数据books没有改变的话,就不会重新求值,会调用之前计算属性返回的结果。这里就有一个缓存,这个缓存就是之前计算属性返回的值。但是方法不是这样的,方法没有这个机制,方法是无论哪里调用了,就会执行这个函数。所以,计算属性的优点其实就是减少了资源的消耗。比如说你的计算属性里面一大堆逻辑需要处理,如果你用计算属性就可以不去每次执行(当然是响应式数据没有改变的情况下),但是如果你用方法,就无意间做了很多没必要的事,明明可以解决的内存资源,何必要浪费呢?
侦听器其实就是侦听某个属性值,只要这个值发生变化就会调用这个侦听函数,侦听函数名就是这个属性名。
<body>
<div id='app'>
<div>{{books.length>0?'isOk':'notOk'}}</div>
<p>{{books.length>0?'isOk':'notOk'}}</p>
<a href="###">{{books.length>0?'isOk':'notOk'}}</a>
<div>{{myFirstComputed}}</div>
<div>{{myFirstMethod()}}</div>
<input type="text" v-model="firstname">
{{fullname}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script type='text/javascript'>
const options = {
el:'#app',
data() {
return {
books: ['西游记','水浒传','三国演义','红楼梦'],
firstname:'李',
lastname:'四',
fullname:''
}
},
methods: {
myFirstMethod() {
return this.books.length>0?'isOk':'notOk'
}
},
computed: {
myFirstComputed() {
return this.books.length>0?'isOk':'notOk'
}
},
watch: {
firstname() {
this.fullname = this.firstname + this.lastname;
}
}
}
const vm = new Vue(options);
console.log(vm);
</script>
在这里的话,只要firstname改变,就会调用firstname这个监听器。
那么监听器和计算属性有什么区别呢?
四个字来总结:各有千秋
就上面侦听器那个例子,我们可以用计算属性来实现,直接写一个计算属性,return一个this.firstname + this.lastname就可以了。但是为什么要写个监听器呢,还得多添加一个属性值fullname。确实是这样的,这里就展现出了计算属性的优点。但是,如果我是想异步改变值呢?比如说我想在改变了firstname值之后五秒钟后才改变fullname,你计算属性怎么写?
改变firstname延时5秒改变fullname的值正确写法
watch: {
firstname() {
setTimeout(()=>{
this.fullname = this.firstname + this.lastname;
},5000)
}
}
计算属性怎么写呢?
computed: {
myFirstComputed() {
setTimeout(()=>{
return this.books.length>0?'isOk':'notOk'
},5000);
}
},
这样?这是不可以的。
所以,侦听器和计算属性各有千秋。