计算属性computed和侦听器watch
一、计算属性computed
计算属性,顾名思义,就是计算出一个属性,并且计算属性的求值结果会被缓存起来,只在它的响应式依赖发生改变时该属性才会重新求值。
计算属性一般用于简化模板中的表达式,如下,只要 firstName
和lastName
还没有发生改变,多次访问 fullName
计算属性会立即返回之前的计算结果,而不必再次执行函数。
<div id="app">
<!--这种方式 若出现次数多,或内容冗长,则很繁琐 -->
<p>{{ firstName + lastName }}</p>
<!--使用计算属性简化模板表达式 -->
<p>{{ fullName }}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
firstName: "梅梅",
lastName: "李",
},
computed: {
fullName: function () {
return this.firstName + this.lastName
}
}
})
</script>
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。复杂的表达式会让模板变得不是那么声明式。我们应该尽量描述理应出现什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
此外应该把复杂的计算属性分割为极可能多简单的属性。
computed:{
basePrice(){
return this.manufactureCost / ( 1 - this.profitMargin)
},
discount(){
return this.basePrice * (this.discountPercent || 0)
},
finalPrice(){
return this.basePrice - this.discount
}
}
也可以进行一些更复杂的操作:
<div id="app">
<h2>总价格:{{ totalPrice }}</h2>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
// 给一个对象数组book,数组每一个元素都是一个对象
books: [
{ id: 110, name: "编程之美", price: 119 },
{ id: 111, name: "代码大全", price: 115 },
{ id: 112, name: "深入理解计算机原理", price: 99 },
{ id: 113, name: "图解HTTP", price: 85 },
]
},
computed: {
totalPrice: function () {
let result = 0
for (let i = 0; i < this.books.length; i++) {
result += this.books[i].price
}
return result
}
}
})
</script>
computed的完整形式,其中包括一个setter方法和一个getter方法。一般只需要实现getter方法。setter一般是空的,不希望别人给这个计算属性设置值,就会直接删去setter方法。如:
<script>
var app = new Vue({
el: '#app',
data: {
firstName: '旺旺',
lastName: '仙贝'
},
computed: {
fullName: {
// 未给fullName外加赋值时,输出为get方法的输出
// 但如果外界再赋值,则会输出 console.log('---', newValue)
set: function (newValue) {
console.log('---', newValue)
},
get: function () {
return 'abc'
// 之后使用fullName属性时就会调用get方法,返回abc
}
}
}
})
</script>
二、侦听属性watch
侦听属性watch可以监听data中指定数据的变化,然后触发这个watch中对应的function处理函数。当我们需要深度监听对象中的属性时,可以打开deep:true
选项, 这样便会对对象中的每一项属性进行监听。
通过watch除了可以监听一些DOM元素外,还可以监听DOM之外的元素,比如路由对象。
watch:{
'$route': function ( newVal, oldVal ){
console.log(`之前的路由:${oldVal},现在的路由${newVal}`);
if (newVal.path === '/register'){
console.log('这是注册组件');
}else{
console.log('这是登录组件')
}
}
}
当需要在数据变化时执行异步或开销较大的操作时,watch是最有用的。使用 watch
选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
以下是官网例子:
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
// 请参考:https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
三、computed、watch、methods的对比
computed
- computed形式上是function()函数,主要当做属性来使用;监听data里的数据变化,会根据data数据变化而自动重新计算;
- computed中属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算;
- 最后一定要有个返回值,而且不带参数。
watch
- 监听一个对象,键是需要观察的表达式,值是对应回调函数。
- 主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑;可以看作是computed和methods的结合体。
methods
- 方法表示一个具体的操作,主要书写业务逻辑;methods的返回值和参数都是可有可无的。
- methods无法监听data数据,并且methods中的方法诶有缓存,每次刷新都会去执行。
总结:
如果一个值依赖多个属性(多对一),用computed
更加方便;
如果一个值变化后会引起一系列操作,或一个值变化会引起一系列值的变化(一对多),用watch
更方便一些。
watch的回调里面会传入监听属性的新旧值,通过这两个值可以做一些特定的操作;computed通常就是简单的计算。