@TOC
计算属性
为什么需要计算属性?
生活中常常会使用到各种复杂逻辑计算,若全部放在逻辑表达式里,会让模板过于繁重且难以维护,于是我们需要把一些响应式数据的复杂逻辑放在专门的计算模块中。
基本使用
下面是一个简单的例子,其首先在data中声明author,author中包含name和books,然后利用计算属性判断books属性长度是否大于0,若大于0则输出其值。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="computed-basics" class="demo"> <p>Has published books:</p> <span>{{ publishedBooksMessage }}</span> </div> <script> Vue.createApp({ data() { return { author: { name: 'John Doe', books: ['Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery'] } } }, computed: { // a computed getter publishedBooksMessage() { // `this` points to the vm instance if(this.author.books.length > 0) return 'Yes!'+this.author.books; else return 'No' } } }).mount('#computed-basics') </script> </body> </html>
计算属性的关键字为computed,其首先声明publishedBooksMessage
函数,你可以像普通属性一样将数据绑定到模板中的计算属性。Vue 知道 vm.publishedBookMessage 依赖于 vm.author.books,因此当 vm.author.books 发生改变时,所有依赖 vm.publishedBookMessage 的绑定也会更新。
computed和methods区别
可以看到computed可以达到和methods同样的效果。都可以通过在表达式中调用方法。
但是不同的是,computed是基于它的响应依赖关系缓存,computed是属性调用,而methods是函数调用,
通俗的来说就是,计算属性(computed)在系统刚运行的时候调用一次,之后只要值不改变,dom中的值不再去调用函数取值,而是从缓存中获取,速度会很快,,比如上例中,每次author.books发生变化时,都会调用一次计算属性。而方法选项methods仅仅刚加载时调用一次,不管值有没有改变,一旦dom中要取值,则会再次调用方法
下面的例子可以帮助理解不同。
<body> <div id="computed-basics" class="demo"> <h1>{{message}}</h1> <p class="test2-1">{{methodTest()}}</p> <p class="test2-2">{{methodTest()}}</p> <p class="test2-3">{{methodTest()}}</p> <p class="test3-1">{{computedTest}}</p> <p class="test3-2">{{computedTest}}</p> </div> <script> Vue.createApp({ data() { return { message: '我是消息,' } }, methods: { methodTest() { return this.message + '现在我用的是methods' } }, computed: { // a computed getter computedTest() { return this.message + '现在我用的是computed' } } }).mount('#computed-basics') </script> </body> </html>
methods定义的方法是以函数调用的形式来访问的
test2-1,test2-2,test2-3是反复地将methodTest方法运行了三遍,如果我们碰到一个场景,需要1000个methodTest的返回值,那么毫无疑问,这势必造成大量的浪费
更恐怖的是,如果你更改了message的值,那么这1000个methodTest方法每一个又会重新计算。
computed依赖于data中的数据,只有在它的相关依赖数据发生改变时才会重新求值,如上例,在Vue实例化的时候,computed定义computedTest方法会做一次计算,返回一个值
在随后的代码编写中,只要computedTest方法依赖的message数据不发生改变,computedTest方法是不会重新计算的
也就是说test3-1,test3-2是直接拿到了返回值,并不是computedTest方法重新计算的结果,而是直接从缓存拿来的结果
在使用时:对于任何复杂逻辑和一些重复的函数,你都应当使用计算属性computed,可以大量节省资源。
计算属性的 Setter
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter。
其中getter方法用于获取,setter方法用于修改。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app-test" class="demo"> </div> <script> const app = { data() { return { name: 'Google', url: 'http://www.google.com' } }, computed: { site: { // getter get: function () { return this.name + ' ' + this.url }, // setter set: function (newValue) { var names = newValue.split(' ') this.name = names[0] this.url = names[names.length - 1] } } } } vm = Vue.createApp(app).mount('#app-test') document.write('name: ' + vm.name); document.write('<br>'); document.write('url: ' + vm.url); document.write('<br>------ 更新数据 ------<br>'); // 调用 setter, vm.name 和 vm.url 也会被对应更新 vm.site = '百度 https://www.baidu.com'; document.write('name: ' + vm.name); document.write('<br>'); document.write('url: ' + vm.url); </script> </body> </html>
当调用 setter对vm.site进行修改后, vm.name 和 vm.url 也会被对应更新
监听属性
当需要在数据变化时执行异步或开销较大的操作时, Vue 通过 监听(watch )选项提供了一个更通用的方法来响应数据的变化。
基本使用
通过使用 watch 实现计数器,为counter添加监听,每次触发点击事件时,运行function(counter),跳出提示。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id = "app-test"> <p style = "font-size:25px;">计数器: {{ counter }}</p> <button @click = "counter++" style = "font-size:25px;">点我</button> </div> <script> const app = { data() { return { counter: 1 } } } vm = Vue.createApp(app).mount('#app-test') vm.$watch('counter', function(counter) { alert('计数器值变为 :' + counter + '!'); }); </script> </body> </html>
在这里插入图片描述
更多关于watch的可以查阅官方文档
computed和watch的区别
使用 watch 选项允许我们执行异步操作 (访问一个 API),并设置一个执行该操作的条件。这些都是computed属性无法做到的。如下面这个例子
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="watch-example" class="demo"> <p> Ask a yes/no question: <input v-model="question" /> </p> <p>{{ answer }}</p> </div> <style> .demo { font-family: sans-serif; border: 1px solid #eee; border-radius: 2px; padding: 20px 30px; margin-top: 1em; margin-bottom: 40px; user-select: none; overflow-x: auto; } </style> <!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 --> <!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 --> <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script> <script> const watchExampleVM = Vue.createApp({ data() { return { question: '', answer: 'Questions usually contain a question mark. ;-)' } }, watch: { // whenever question changes, this function will run question(newQuestion, oldQuestion) { if (newQuestion.indexOf('?') > -1) { this.getAnswer() } } }, methods: { getAnswer() { this.answer = 'Thinking...' axios .get('https://yesno.wtf/api') .then(response => { this.answer = response.data.answer }) .catch(error => { this.answer = 'Error! Could not reach the API. ' + error }) } } }).mount('#watch-example') </script> </body> </html>
在这里插入图片描述
除上以外,computed和watch的区别主要有:
1、是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
2、computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,就必须添加immediate属性,设置为true(immediate:true)
3、使用场景:computed 和 watch 的使用场景并不一样,computed 的话是通过几个数据的变化,来影响一个数据,而 watch,则是可以一个数据的变化,去影响多个数据。
4、是否调用缓存:computed中的函数所依赖的属性没有发生变化,调用时会从缓存中读取(如上文所说),而watch不调用缓存。