計(jì)算屬性概念
模板內(nèi)的表達(dá)式非常便利,但是設(shè)計(jì)它們的初衷是用于簡(jiǎn)單運(yùn)算的。在模板中放入太多的邏輯會(huì)讓模板過(guò)重且難以維護(hù)。例如:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
在這個(gè)地方,模板不再是簡(jiǎn)單的聲明式邏輯。你必須看一段時(shí)間才能意識(shí)到,這里是想要顯示變量 message 的翻轉(zhuǎn)字符串。當(dāng)你想要在模板中多次引用此處的翻轉(zhuǎn)字符串時(shí),就會(huì)更加難以處理。
所以,對(duì)于任何復(fù)雜邏輯,你都應(yīng)當(dāng)使用計(jì)算屬性
簡(jiǎn)單的理解為:
1.計(jì)算屬性其實(shí)就是 Vue 實(shí)例的一個(gè)屬性
2.計(jì)算屬性一般依賴傳統(tǒng)的 Vue 實(shí)例屬性
3.計(jì)算屬性一般是通過(guò)運(yùn)算得到的屬性
計(jì)算屬性的get和set
簡(jiǎn)單的寫(xiě)法
computed: {
reversedMessage() {
return this.message.split('').reverse().join('')
}
}
完整版寫(xiě)法
computed: {
reversedMessage: {
get() { //獲取值
return this.message.split('').reverse().join('')
},
set(val) { // set方法:設(shè)置值
this.message = val.split('').reverse().join('')
}
}
}
計(jì)算屬性VS方法
HTML
<p>Reversed message: "{{ reversedMessage() }}"</p>
JS
// 在組件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
計(jì)算屬性是基于它們的依賴進(jìn)行緩存的,只有在它相關(guān)的依賴發(fā)生改變時(shí)才會(huì)重新求值,即計(jì)算屬性會(huì)對(duì)計(jì)算出來(lái)的結(jié)果進(jìn)行緩存,這就意味著只要 message 還沒(méi)有發(fā)生改變,多次訪問(wèn) reversedMessage 計(jì)算屬性會(huì)立即返回之前的計(jì)算結(jié)果,而不必再次執(zhí)行函數(shù)
方法是每次重新渲染時(shí),調(diào)用方法將總會(huì)再次執(zhí)行函數(shù),開(kāi)銷(xiāo)比較大
我們?yōu)槭裁葱枰彺妫?/p>
假設(shè)我們有一個(gè)性能開(kāi)銷(xiāo)比較大的計(jì)算屬性 A,它需要遍歷一個(gè)巨大的數(shù)組并做大量的計(jì)算。然后我們可能有其他的計(jì)算屬性依賴于 A 。如果沒(méi)有緩存,我們將不可避免的多次執(zhí)行 A 的 getter!如果你不希望有緩存,請(qǐng)用方法來(lái)替代
計(jì)算屬性 vs 偵聽(tīng)屬性
watch
放在 data 中的對(duì)象,一旦發(fā)生改變就會(huì)執(zhí)行相應(yīng)的操作,當(dāng)需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開(kāi)銷(xiāo)較大的操作時(shí),這個(gè)方式是最有用的
<div>
<p>FullName: {{fullName}}</p>
<p>FirstName: <input type="text" v-model="firstName"></p>
</div>
new Vue({
el: '#root',
data: {
firstName: 'Joy',
lastName: 'lqy',
fullName: ''
},
watch: {
firstName(newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
}
}
})
watch 中的對(duì)象在 data 中已經(jīng)定義了,當(dāng)我們輸入firstName后, watch監(jiān)聽(tīng)每次修改變化的新值,然后計(jì)算輸出fullName。也就是上面的代碼中,fullName 一開(kāi)始被渲染出來(lái)的時(shí)候是空值,如下所示:

watch的高級(jí)用法
handler方法和immediate屬性
如上所述,一開(kāi)始被渲染出來(lái)的時(shí)候,fullName是空值,如果想要一開(kāi)始就讓最初綁定的值執(zhí)行該怎么辦尼?別急,我們只需要給firstName綁定一個(gè)handler方法,之前我們寫(xiě)的watch方法其實(shí)默認(rèn)寫(xiě)的就是這個(gè)handler,Vue.js會(huì)去處理這個(gè)邏輯,最終編譯出來(lái)其實(shí)就只這個(gè)handler,設(shè)置immediate:true代表如果在 wacth 里聲明了 firstName 之后,就會(huì)立即先去執(zhí)行里面的handler方法,如果為 false就跟我們以前的效果一樣,不會(huì)在綁定的時(shí)候就執(zhí)行
修改后的代碼如下:
watch: {
firstName: {
handler(newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
},
// 代表在wacth里聲明了firstName這個(gè)方法之后立即先去執(zhí)行handler方法
immediate: true
}
}
結(jié)果如下:

deep屬性
deep,默認(rèn)值為false,代表是否深度監(jiān)聽(tīng),
總的來(lái)說(shuō),計(jì)算屬性傾向于格式化/處理當(dāng)前的數(shù)據(jù),而 watch 傾向于執(zhí)行數(shù)據(jù)變化需要進(jìn)行的操作
注銷(xiāo)watch
為什么要注銷(xiāo) watch?因?yàn)槲覀兊慕M件是經(jīng)常要被銷(xiāo)毀的,比如我們跳一個(gè)路由,從一個(gè)頁(yè)面跳到另外一個(gè)頁(yè)面,那么原來(lái)的頁(yè)面的watch 其實(shí)就沒(méi)用了,這時(shí)候我們應(yīng)該注銷(xiāo)掉原來(lái)頁(yè)面的 watch 的,不然的話可能會(huì)導(dǎo)致內(nèi)置溢出。好在我們平時(shí) watch 都是寫(xiě)在組件的選項(xiàng)中的,他會(huì)隨著組件的銷(xiāo)毀而銷(xiāo)毀。
const app = new Vue({
template: '<div id="root">{{text}}</div>',
data: {
text: 0
},
watch: {
text(newVal, oldVal){
console.log(`${newVal} : ${oldVal}`);
}
}
});
但是,如果我們使用下面這樣的方式寫(xiě) watch,那么就要手動(dòng)注銷(xiāo)了,這種注銷(xiāo)其實(shí)也很簡(jiǎn)單
const unWatch = app.$watch('text', (newVal, oldVal) => {
console.log(`${newVal} : ${oldVal}`);
})
unWatch(); // 手動(dòng)注銷(xiāo)watch
app.$watch調(diào)用后會(huì)返回一個(gè)值,就是unWatch方法,你要注銷(xiāo) watch 只要調(diào)用unWatch方法就可以了。