前言
一些初學(xué)者可能對(duì)計(jì)算屬性和偵聽(tīng)屬性的使用場(chǎng)景感到困惑不解呵萨,本文主要介紹兩者的用法箱吕、使用場(chǎng)景及其兩者的區(qū)別脑豹。 本文的代碼請(qǐng)猛戳github博客坐慰,紙上得來(lái)終覺(jué)淺,大家動(dòng)手多敲敲代碼鲤孵!
計(jì)算屬性
1.介紹
計(jì)算屬性是自動(dòng)監(jiān)聽(tīng)依賴值的變化壶栋,從而動(dòng)態(tài)返回內(nèi)容,監(jiān)聽(tīng)是一個(gè)過(guò)程普监,在監(jiān)聽(tīng)的值變化時(shí)贵试,可以觸發(fā)一個(gè)回調(diào),并做一些事情凯正。它有以下幾個(gè)特點(diǎn):
- 數(shù)據(jù)可以進(jìn)行邏輯處理毙玻,減少模板中計(jì)算邏輯。
- 對(duì)計(jì)算屬性中的數(shù)據(jù)進(jìn)行監(jiān)視
- 依賴固定的數(shù)據(jù)類型(響應(yīng)式數(shù)據(jù))
計(jì)算屬性由兩部分組成:get和set廊散,分別用來(lái)獲取計(jì)算屬性和設(shè)置計(jì)算屬性桑滩。默認(rèn)只有g(shù)et,如果需要set允睹,要自己添加运准。另外set設(shè)置屬性,并不是直接修改計(jì)算屬性缭受,而是修改它的依賴胁澳。
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
//this.fullName = newValue 這種寫法會(huì)報(bào)錯(cuò)
var names = newValue.split(' ')
this.firstName = names[0]//對(duì)它的依賴進(jìn)行賦值
this.lastName = names[names.length - 1]
}
}
}
現(xiàn)在再運(yùn)行 vm.fullName = 'John Doe' 時(shí),setter 會(huì)被調(diào)用米者,vm.firstName 和 vm.lastName 也會(huì)相應(yīng)地被更新韭畸。
2.計(jì)算屬性 vs 普通屬性
可以像綁定普通屬性一樣在模板中綁定計(jì)算屬性,在定義上有區(qū)別:計(jì)算屬性的屬性值必須是一個(gè)函數(shù)蔓搞。
data:{ //普通屬性
msg:'浪里行舟',
},
computed:{ //計(jì)算屬性
msg2:function(){ //該函數(shù)必須有返回值胰丁,用來(lái)獲取屬性,稱為get函數(shù)
return '浪里行舟';
},
reverseMsg:function(){
//可以包含邏輯處理操作喂分,同時(shí)reverseMsg依賴于msg,一旦msg發(fā)生變化隘马,reverseMsg也會(huì)跟著變化
return this.msg.split(' ').reverse().join(' ');
}
}
3.計(jì)算屬性 vs 方法
兩者最主要的區(qū)別:computed 是可以緩存的,methods 不能緩存妻顶;只要相關(guān)依賴沒(méi)有改變,多次訪問(wèn)計(jì)算屬性得到的值是之前緩存的計(jì)算結(jié)果蜒车,不會(huì)多次執(zhí)行讳嘱。網(wǎng)上有種說(shuō)法就是方法可以傳參,而計(jì)算屬性不能酿愧,其實(shí)并不準(zhǔn)確沥潭,計(jì)算屬性可以通過(guò)閉包來(lái)實(shí)現(xiàn)傳參:
:data="closure(item, itemName, blablaParams)"
computed: {
closure () {
return function (a, b, c) {
/** do something */
return data
}
}
}
偵聽(tīng)屬性
Vue 提供了一種更通用的方式來(lái)觀察和響應(yīng) Vue 實(shí)例上的數(shù)據(jù)變動(dòng):偵聽(tīng)屬性watch。watch中可以執(zhí)行任何邏輯嬉挡,如函數(shù)節(jié)流钝鸽,Ajax異步獲取數(shù)據(jù)汇恤,甚至操作 DOM(不建議)。
1.常規(guī)用法
<template>
<div class="attr">
<h1>watch屬性</h1>
<h2>{{ $data }}</h2>
<button @click="() => (a += 1)">修改a的值</button>
</div>
</template>
<script>
export default {
data() {
return {
a: 1,
b: { c: 2, d: 3 },
e: {
f: {
g: 4
}
},
h: []
};
},
watch: {
a: function(val, oldVal) {
this.b.c += 1;
},
"b.c": function(val, oldVal) {
this.b.d += 1;
},
"b.d": function(val, oldVal) {
this.e.f.g += 1;
},
e: {
handler: function(val, oldVal) {
this.h.push("浪里行舟");
},
deep: true //用于監(jiān)聽(tīng)e對(duì)象內(nèi)部值的變化
}
}
};
</script>
復(fù)制代碼
2.使用 watch 的深度遍歷和立即調(diào)用功能
使用 watch 來(lái)監(jiān)聽(tīng)數(shù)據(jù)變化的時(shí)候除了常用到 handler 回調(diào)拔恰,其實(shí)其還有兩個(gè)參數(shù)因谎,便是:
- deep 設(shè)置為 true 用于監(jiān)聽(tīng)對(duì)象內(nèi)部值的變化
- immediate 設(shè)置為 true 將立即以表達(dá)式的當(dāng)前值觸發(fā)回調(diào)
<template>
<button @click="obj.a = 2">修改</button>
</template>
<script>
export default {
data() {
return {
obj: {
a: 1,
}
}
},
watch: {
obj: {
handler: function(newVal, oldVal) {
console.log(newVal);
},
deep: true,
immediate: true
}
}
}
</script>
以上代碼我們修改了 obj 對(duì)象中 a 屬性的值,我們可以觸發(fā)其 watch 中的 handler 回調(diào)輸出新的對(duì)象颜懊,而如果不加 deep: true财岔,我們只能監(jiān)聽(tīng) obj 的改變,并不會(huì)觸發(fā)回調(diào)河爹。同時(shí)我們也添加了 immediate: true 配置匠璧,其會(huì)立即以 obj 的當(dāng)前值觸發(fā)回調(diào)。 我們?cè)倏匆粋€(gè)實(shí)際工作中常遇到的場(chǎng)景:組件創(chuàng)建的時(shí)候我們獲取一次列表的數(shù)據(jù)咸这,同時(shí)監(jiān)聽(tīng)input框夷恍,每當(dāng)發(fā)生變化的時(shí)候重新獲取一次篩選后的列表。
created(){
this.fetchPostList()
},
watch: {
searchInputValue(){
this.fetchPostList()
}
}
有沒(méi)有辦法優(yōu)化一下呢媳维?
watch: {
searchInputValue:{
handler: 'fetchPostList',
immediate: true
}
}
首先酿雪,在watchers中,可以直接使用函數(shù)的字面量名稱侨艾;其次执虹,聲明immediate:true表示創(chuàng)建組件時(shí)立馬執(zhí)行一次。
兩者之間對(duì)比
從上面流程圖中唠梨,我們可以看出它們之間的區(qū)別:
- watch:監(jiān)測(cè)的是屬性值袋励, 只要屬性值發(fā)生變化,其都會(huì)觸發(fā)執(zhí)行回調(diào)函數(shù)來(lái)執(zhí)行一系列操作当叭。
- computed:監(jiān)測(cè)的是依賴值茬故,依賴值不變的情況下其會(huì)直接讀取緩存進(jìn)行復(fù)用,變化的情況下才會(huì)重新計(jì)算蚁鳖。
除此之外磺芭,有點(diǎn)很重要的區(qū)別是:計(jì)算屬性不能執(zhí)行異步任務(wù),計(jì)算屬性必須同步執(zhí)行醉箕。也就是說(shuō)計(jì)算屬性不能向服務(wù)器請(qǐng)求或者執(zhí)行異步任務(wù)钾腺。如果遇到異步任務(wù),就交給偵聽(tīng)屬性讥裤。watch也可以檢測(cè)computed屬性放棒。
接下去我們看個(gè)用watch來(lái)實(shí)現(xiàn)防抖的例子:直到用戶停止輸入超過(guò)1秒后,才更新視圖己英。
<template>
<div>
{{ fullName }}
<div>firstName: <input v-model="firstName" /></div>
<div>lastName: <input v-model="lastName" /></div>
</div>
</template>
<script>
import { setTimeout } from "timers";
export default {
data: function() {
return {
firstName: "浪里行舟",
lastName: "前端工匠",
fullName: "浪里行舟 前端工匠"
};
},
watch: {
firstName: function(val) {
clearTimeout(this.firstTimeout);
this.firstTimeOut = setTimeout(() => {
this.fullName = val + " " + this.lastName;
}, 1000);
},
lastName: function(val) {
clearTimeout(this.lastTimeout);
this.lastTimeOut = setTimeout(() => {
this.fullName = this.firstName + " " + val;
}, 1000);
}
}
};
復(fù)制代碼
總結(jié)
計(jì)算屬性適合用在模板渲染中间螟,某個(gè)值是依賴了其它的響應(yīng)式對(duì)象甚至是計(jì)算屬性計(jì)算而來(lái);而偵聽(tīng)屬性適用于觀測(cè)某個(gè)值的變化去完成一段復(fù)雜的業(yè)務(wù)邏輯。
- computed能做的厢破,watch都能做荣瑟,反之則不行
- 能用computed的盡量用computed
歡迎關(guān)注公眾號(hào):前端工匠,你的成長(zhǎng)我們一起見(jiàn)證摩泪!
求點(diǎn)贊笆焰,求關(guān)注~