在上篇中介绍了模板语法、Data Property 和方法。本节的计算属性和侦听器非常有用。
一、计算属性
1.任何包含响应式数据的复杂逻辑,都应该使用计算属性。模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div id="computed-basics"> 3 <p>Has published books:</p> 4 <span>{{ publishedBooksMessage }}</span> 5 </div> 6 </template> 7 <script> 8 export default { 9 data() { 10 return { 11 author: { 12 name: "John Doe", 13 books: [ 14 "Vue 2 - Advanced Guide", 15 "Vue 3 - Basic Guide", 16 "Vue 4 - The Mystery", 17 ], 18 }, 19 }; 20 }, 21 computed: { 22 // 计算属性的 getter 23 publishedBooksMessage() { 24 // `this` 指向 vm 实例 25 return this.author.books.length > 0 ? "Yes" : "No"; 26 }, 27 }, 28 }; 29 </script>
这里声明了一个计算属性 publishedBooksMessage
。尝试更改应用程序 data
中 books
数组的值,你将看到 publishedBooksMessage
如何相应地更改。你可以像普通属性一样将数据绑定到模板中的计算属性。Vue 知道 vm.publishedBookMessage
依赖于 vm.author.books
,因此当 vm.author.books
发生改变时,所有依赖 vm.publishedBookMessage
的绑定也会更新。
以上计算属性的原理是什么?
1. data 属性初始化 getter setter
2. computed 计算属性初始化,提供的函数将用作属性 reversedMessage 的 getter
3. 当首次获取 reversedMessage 计算属性的值时,Dep 开始依赖收集
4. 在执行 message getter 方法时,如果 Dep 处于依赖收集状态,则判定 message 为reversedMessage 的依赖,并建立依赖关系
5. 当 message 发生变化时,根据依赖关系,触发 reverseMessage 的重新计算
2.计算属性对比方法
在以上例子中,调用方法达到同样的效果。但是不同的是计算属性是基于它们的反应依赖关系缓存的。计算属性只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 author.books
还没有发生改变,多次访问 publishedBookMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。使用缓存是为了提高性能,如果不希望有缓存,直接用 method
来替代就可以了。
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:
1 computed: { 2 fullName: { 3 // getter 4 get() { 5 return this.firstName + ' ' + this.lastName 6 }, 7 // setter 8 set(newValue) { 9 const names = newValue.split(' ') 10 this.firstName = names[0] 11 this.lastName = names[names.length - 1] 12 } 13 } 14 }
二、侦听器
当需要在数据变化时执行异步或开销较大的操作时,使用侦听器。请看示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div> 3 <p> 4 Ask a yes/no question: 5 <input v-model="question" /> 6 </p> 7 <p>{{ answer }}</p> 8 </div> 9 </template> 10 <script> 11 export default { 12 data() { 13 return { 14 question: "", 15 answer: "Questions usually contain a question mark. ;-)", 16 }; 17 }, 18 watch: { 19 // whenever question changes, this function will run 20 question(newQuestion, oldQuestion) { 21 if (newQuestion.indexOf("?") > -1) { 22 this.getAnswer(); 23 } 24 }, 25 }, 26 methods: { 27 getAnswer() { 28 this.answer = "Thinking..."; 29 axios 30 .get("https://yesno.wtf/api") 31 .then((response) => { 32 this.answer = response.data.answer; 33 }) 34 .catch((error) => { 35 this.answer = "Error! Could not reach the API. " + error; 36 }); 37 }, 38 }, 39 }; 40 </script>
使用 watch
选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
计算属性和侦听器的对比:
依赖 | 是否有缓存 | 开销 | 觖发方式 | 使用场景 | |
methods | 外部调用 | 无 | 大 | 事件或者函数的回调,被动调用 | 每次确实需要重新加载,不需要缓存时,需要参数时 |
compled | 依赖追踪属性值的改变,响应式依赖 | 有 | 小,快 | 在html渲染开始,自动触发 |
改变一个变量的显示,数据量大,需要缓存的时候。命令式且重复的的数据,复杂的渲染数据计算,多处需要引用的变量计算。比如表单校验 |
watch | 依赖追踪属性值的改变 | 无 | 大,差 | on这种触发 | 异步操作,Ajax,操作时 |
三、Class与Style绑定
表达式结果的类型除了字符串之外,还可以是对象或数组。
1.对象绑定语法
:class="{ active: isActive }",也可以
:class="{ active: isActive, 'text-danger': hasError }" 然后结合以下代码实现:
1 data() { 2 return { 3 isActive: true, 4 hasError: false 5 } 6 }
此外,还可以通过computed
属性实现。
2.数组的语法
:class="[activeClass, errorClass]",如果变量值为
'active',
'text-danger'
渲染的结果为:
<div class="active text-danger"></div>
1 <my-component class="baz boo"></my-component>或者<my-component :class="{ active: isActive }"></my-component>
4.绑定内联样式
style样式绑定跟class基本类型。:style
的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名。
在 :style
中使用需要 (浏览器引擎前缀) vendor prefixes 的 CSS property 时,如 transform
,Vue 将自动侦测并添加相应的前缀。
这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex
。
1 <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>