1、计算属性(computed)
(1)计算属性
模板的设计初衷是为了处理简单的逻辑(声明式逻辑),在模板中加入过多的逻辑会使模板难以阅读和维护
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <p>原始字符串:{{ message }}</p> <!-- 比如说,在模板中对 message 进行复杂的字符串反转操作 --> <p>反转字符串:{{ message.split('').reverse().join('') }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { message: 'hello' } }) </script> </body> </html>
这时,我们就应该考虑使用计算属性,以代替复杂的模板逻辑
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <p>原始字符串:{{ message }}</p> <!-- 在模板中直接使用计算属性 --> <p>反转字符串:{{ reversedMessage }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { message: 'hello' }, // computed 是 Vue 实例的计算属性 // 它可以是一个对象,其中每一项的键是计算属性的名称,值是计算属性对应的函数 computed: { // 这里,我们声明计算属性 reversedMessage,用于储存反转之后的字符串 // 所提供的函数默认作为计算属性的 getter 函数(事实上还可以提供 setter 函数) // 这里的计算属性 reversedMessage 依赖于 message // 也就是说,当 message 发生变化时,reversedMessage 会自动更新 reversedMessage: function () { return this.message.split('').reverse().join('') } } }) </script> </body> </html>
(2)computed & methods
不知道大家还记不记得,我们在之前的文章中有一个例子,使用 methods 实现反转字符串的效果
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <p>原始字符串:{{ message }}</p> <!-- 在模板中显示方法的返回结果 --> <p>反转字符串:{{ reversedMessage() }}</p> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { message: "Hello Vue" }, methods: { // 定义方法 reversedMessage(),返回反转之后的字符串 reversedMessage: function () { return this.message.split('').reverse().join('') } } }) </script> </body> </html>
可以看到,使用 computed 和 methods 实现的功能大体相同,但是它们的不同之处在于:
- computed 是 属性调用,而 methods 是方法调用
- computed 是 基于缓存,而 methods 不是
对于计算属性而言,计算结果将会被缓存,如果某个依赖在该实例范畴之外,那么计算属性是不会被更新的
但是对于方法而言,调用方法总会再次执行代码,我们可以从一个简单的例子看出它们之间的区别:
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <!-- 注意,这里是属性调用 --> <p>第一次调用 computed:{{ msg_in_computed }}</p> <p>第二次调用 computed:{{ msg_in_computed }}</p> <br/> <!-- 注意,这里是方法调用 --> <p>第一次调用 methods:{{ msg_in_methods() }}</p> <p>第二次调用 methods:{{ msg_in_methods() }}</p> </div> <script> var cnt_for_computed = 0; var cnt_for_methods = 0; var vm = new Vue({ el: '#app', computed: { // 定义计算属性 msg_in_computed,多次调用不会更新数据 // 因为它的依赖 cnt_for_computed 是非响应式属性,不在实例范畴之内 msg_in_computed: function () { cnt_for_computed += 1; return cnt_for_computed } }, methods: { // 定义方法 msg_in_methods(),每次调用总会执行代码 msg_in_methods: function () { cnt_for_methods += 1; return cnt_for_methods } } }) </script> </body> </html> <!-- 输出结果 第一次调用 computed:1 第二次调用 computed:1 第一次调用 methods:1 第二次调用 methods:2 -->
(3)getter & setter
计算属性默认只有 getter 函数
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <p>fullName: {{ fullName }}</p> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: 'Steve', lastName: 'Jobs' }, computed: { // 若为计算属性提供一个函数,则该函数默认作为计算属性的 getter 函数 fullName: function () { return this.firstName + ' ' + this.lastName } } }) // 修改计算属性依赖的值时,会自动调用计算属性的 getter 函数 vm.firstName = 'Stephen' </script> </body> </html> <!-- 输出结果 fullName: Stephen Jobs firstName: Stephen lastName: Jobs -->
但是,在需要的时候我们也可以提供一个 setter 函数
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <p>fullName: {{ fullName }}</p> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: 'Steve', lastName: 'Jobs' }, computed: { fullName: { // getter 函数 get: function () { return this.firstName + ' ' + this.lastName }, // setter 函数 set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } }) // 修改计算属性的值时,会自动调用计算属性的 setter 函数 vm.fullName = 'Tim Cook' </script> </body> </html> <!-- 输出结果 fullName: Tim Cook firstName: Tim lastName: Cook -->
2、侦听属性(watch)
侦听属性是一个对象,键是需要观察的表达式,值是 对应的回调函数 或者是 包含选项的对象
- 对应的回调函数
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <p>计数器: {{ counter }}</p> <button @click="counter++">点我</button> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { counter: 1 }, watch: { // 当 counter 发生变化时,会自动调用其对应的回调函数打印一条信息 counter: function(val, oldVal){ alert('计数器从 ' + val + ' 变为 ' + oldVal); } } }); </script> </body> </html>
- 包含选项的对象
var vm = new Vue({ el: '#app', data: { counter: 1 }, watch: { // 使用对象可以为监听属性添加更多的配置信息 counter: { handler: function (val, oldVal) { alert('计数器从 ' + val + ' 变为 ' + oldVal); }, deep: true, // 当监听对象的属性发生变化时(不管嵌套的深度),触发回调 immediate: true // 在侦听开始之后,立即触发回调 }, } });
文章知识点与官方知识档案匹配,可进一步学习相关知识