本文目錄:
- 1.什么是計(jì)算屬性
- 2.計(jì)算屬性的作用
- 3.computed里面的getter和setter
- 4.watch使用的幾種方法
- 5.詳解watch中的immediate和deep屬性
- 6.表單監(jiān)聽
- 7.總結(jié)
1.什么是計(jì)算屬性
計(jì)算屬性從字面上理解完域,它類似屬性的用法软吐,但是卻是可以計(jì)算的,通俗的角度上方法的調(diào)用是這樣的:obj.fn()吟税,屬性的調(diào)用是這樣的:obj.name, 而計(jì)算屬性從內(nèi)容上可能長得像方法凹耙,但使用的時候,不加括號就可以調(diào)用肠仪,我們來看下面的例子:
<p>{{computedMessage}}</p>
......
computed: {
computedMessage(){
return this.message.split('').reverse().join('')
}
}
2.計(jì)算屬性的作用
1.使用計(jì)算屬性肖抱,可以讓模版中少用一些邏輯計(jì)算,便于維護(hù)
如果沒有計(jì)算屬性异旧,模版中的代碼是這樣的
<p>{{message.split('').reverse().join('')}}</p>
有了計(jì)算屬性意述,模版中的代碼是這樣的
<p>{{computedMessage}}</p>
2.計(jì)算屬性可以緩存,提高性能
計(jì)算屬性長得和方法一樣泽艘,那是不是意味著我們可以直接把邏輯寫在方法里面欲险,然后直接調(diào)用方法就可以了呢?
<div id="app">
<p>計(jì)算屬性運(yùn)行結(jié)果:{{computedMessage}}</p>
<p>方法運(yùn)行結(jié)果:{{fnMessage()}}</p>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
message:'hello nodeing!!!!'
},
methods:{
fnMessage(){
//split將message分割成數(shù)組匹涮,然后反轉(zhuǎn)天试,然后使用join給連接成一個新的字符串
return this.message.split('').reverse().join('')
}
},
computed: {
computedMessage(){
return this.message.split('').reverse().join('')
}
}
});
</script>
從結(jié)果來看,它們的結(jié)果的確是一樣的然低,但是它們也有區(qū)別喜每,那就是計(jì)算屬性是基于它們的依賴進(jìn)行緩存的,而方法是需要每次去計(jì)算的雳攘,上面的代碼中带兜,fnMessage多次被調(diào)用,都會去執(zhí)行內(nèi)部的代碼,而對于計(jì)算屬性來說娇妓,它是基于它們的依賴進(jìn)行緩存送漠,意思就是說計(jì)算屬性會緩存它計(jì)算出來的值,只要它的依賴沒有變化无畔,那么它每次取的值就是緩存起來的結(jié)果,而并不會再次去運(yùn)算吠冤,這樣就節(jié)省了調(diào)用的計(jì)算開銷浑彰。在上面代碼中,計(jì)算屬性是依賴message的拯辙,只要message不變郭变,計(jì)算屬性不會再次計(jì)算,只有message變化了,計(jì)算屬性才會再次計(jì)算
根據(jù)上面的結(jié)論诉濒,那么下面這種代碼結(jié)果是不會變化的
computed: {
computedMessage(){
return Date.now()
}
}
上面代碼中周伦,計(jì)算屬性并不依賴于某一個屬性,所以多次被調(diào)用并不會改變其結(jié)果
計(jì)算屬性是根據(jù)其依賴的變量的變化而自動變化
下面我們來看一個具體的例子循诉,
<div id="app">
<button @click="fn1">計(jì)算屬性</button>
<p v-if="isShow">{{computedMessage}}</p>
<br><br>
<button @click="fn2">方法</button>
<p v-if="isFnShow">{{fnMessage()}}</p>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
isShow: false,
isFnShow: false
},
methods: {
fnMessage() {
let date = new Date();
return date.getMilliseconds() //每次重新渲染横辆,都會重新調(diào)用一下方法
},
fn1(){
this.isShow = !this.isShow
},
fn2() {
this.isFnShow = !this.isFnShow
}
},
computed: {
computedMessage() {
let date = new Date();
return date.getMilliseconds() //計(jì)算一次獲得的值會被緩存起來
}
}
});
</script>
3.computed里面的getter和setter
在 Vue 中,computed 的屬性可以被視為是 data 一樣茄猫,可以讀取和設(shè)值狈蚤,因此在 computed 中可以分成 getter(讀取) 和 setter(設(shè)值)划纽,一般情況下是沒有 setter 的脆侮,computed 預(yù)設(shè)只有 getter ,也就是只能讀取勇劣,不能改變設(shè)值靖避。
vue.js計(jì)算屬性默認(rèn)只有 getter,因?yàn)槭悄J(rèn)值所以我們也常常省略不寫比默,如下代碼:
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
其實(shí)computed里的代碼完整的寫法應(yīng)該是:
computed: {
fullName: {
get(){
return this.firstName + ' ' + this.lastName
}
}
}
計(jì)算屬性getter的觸發(fā)時間:
<template>
<div id="demo">
<p> {{ fullName }} </p>
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
</div>
</template>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'zhang',
lastName: 'san'
},
computed: {
fullName: function () {
console.log('computed getter...')
return this.firstName + ' ' + this.lastName
}
},
updated () {
console.log('updated')
}
})
如果我們改變上邊代碼里的2個輸入框的值firstName或者lastName幻捏,都會觸發(fā)computed以及updated (),也就是說會執(zhí)行: console.log('computed getter...')和console.log('updated') (用來驗(yàn)證是不是執(zhí)行了命咐,沒有其他意思)
需要注意的是篡九,不是說我們更改了getter里使用的變量,就會觸發(fā)computed的更新醋奠,前提是computed里的值必須要在模板里使用才行榛臼。怎么理解呢?
如下代碼窜司,我們把template里使用到fullName 變量p標(biāo)簽注釋掉:
``
就算我們更改了firstName以及l(fā)astName都不會觸發(fā)computed 中的 getter 中的console.log('computed getter...')沛善,而只會觸發(fā)console.log('updated'),這就代表computed里面的屬性必須要動態(tài)渲染到頁面上才會自動觸發(fā)更新塞祈。
計(jì)算屬性settter:
<div id="demo">
<p> {{ fullName }} </p>
<input type="text" v-model="fullName">
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
</div>
......
computed: {
fullName: {
//getter 方法
get() {
console.log('computed getter...')
return this.firstName + ' ' + this.lastName
},
//setter 方法
set(newValue) {
console.log('computed setter...')
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
return this.firstName + ' ' + this.lastName
}
}
},
在template 中金刁,我們可以看到,input 是直接綁 v-model="fullName"议薪,如果我們這里直接修改了fullName的值胀葱,那么就會觸發(fā)setter,同時也會觸發(fā)getter以及updated函數(shù)笙蒙。其執(zhí)行順序是setter -> getter -> updated,如下:
console.log('computed setter...')
console.log('computed getter...')
console.log('updated')
這里需要注意的是庆锦,并不是觸發(fā)了setter也就會觸發(fā)getter捅位,他們兩個是相互獨(dú)立的。我們這里修改了fullName會觸發(fā)getter是因?yàn)閟etter函數(shù)里有改變firstName 和 lastName 值的代碼。也就是說我們?nèi)绻⑨尩羯线叺膕etter中修改firstName 和lastName的代碼后就不會執(zhí)行g(shù)etter艇搀,如下:
set(newValue){
console.log('computed setter...')
// var names = newValue.split(' ')
// this.firstName = names[0]
// this.lastName = names[names.length - 1]
return this.firstName + ' ' + this.lastName
}
代碼依舊會執(zhí)正常運(yùn)行尿扯,且順序如下
console.log('computed setter...')
console.log('updated')
watch屬性可以用來監(jiān)聽每一個屬性的變化,watch這個對象里面都是函數(shù)焰雕,函數(shù)的名稱是data中的屬性名稱衷笋,watch中的函數(shù)不需要調(diào)用,當(dāng)屬性發(fā)生改變那么就會觸發(fā)watch函數(shù)矩屁,每個函數(shù)都會接受兩個值辟宗,一個是新值,一個是舊值吝秕,我們可以在watch當(dāng)中就行新舊值的判斷來減少虛擬dom的渲染泊脐。
4.watch使用的幾種方法
(1)通過watch監(jiān)聽data數(shù)據(jù)的變化,數(shù)據(jù)發(fā)生變化時烁峭,就會打印當(dāng)前的值
data(newVal, oldVal) {
console.log(newVal)
console.log(oldVal)
}
}
(2)通過watch監(jiān)聽docData數(shù)據(jù)的變化容客,數(shù)據(jù)發(fā)生變化時,this.change_number++(使用深度監(jiān)聽)
watch: {
docData: {
handler(newVal) {
this.change_number++
},
deep: true
}
}
(3)通過watch監(jiān)聽data數(shù)據(jù)的變化约郁,數(shù)據(jù)發(fā)生變化時,執(zhí)行changeData方法
watch: {
data: 'changeData' // 值可以為methods的方法名
}缩挑,
methods: {
changeData(curVal,oldVal){
conosle.log(curVal,oldVal)
}
}
5.詳解watch中的immediate和deep屬性
(1)immediate
其值是true或false;確認(rèn)是否以當(dāng)前的初始值執(zhí)行handler的函數(shù)
不使用immediate屬性時的watch監(jiān)聽變量時有一個特點(diǎn)鬓梅,就是當(dāng)值第一次綁定時供置,不會執(zhí)行監(jiān)聽函數(shù),只有值發(fā)生改變時才會執(zhí)行己肮。如果我們需要在最初綁定值的時候也執(zhí)行函數(shù)士袄,則就需要用到immediate屬性。
watch: {
docData: {
handler(newVal) {
this.change_number++
},
immediate: true
}
}
(2)deep
其值是true或false谎僻;確認(rèn)是否深入監(jiān)聽娄柳。(一般監(jiān)聽時是不能監(jiān)聽到對象屬性值的變化的,數(shù)組的值變化可以監(jiān)聽到艘绍。)
當(dāng)需要監(jiān)聽一個對象的改變時赤拒,普通的watch方法只是監(jiān)聽該對象的引用,無法監(jiān)聽到對象內(nèi)部屬性的改變诱鞠,此時就需要deep屬性對對象進(jìn)行深度監(jiān)聽挎挖。
data() {
return {
docData: {
'doc_id': 1,
'tpl_data': 'abc'
}
}
},
watch: {
docData: {
handler(newVal) {
this.change_number++
},
deep: true
}
}
設(shè)置deep:true則可以監(jiān)聽到docData.doc_id的變化航夺,此時會給docData的所有屬性都加上這個監(jiān)聽器蕉朵,當(dāng)對象屬性較多時,每個屬性值的變化都會執(zhí)行handler阳掐。如果只需要監(jiān)聽對象中的一個屬性值始衅,則可以做以下優(yōu)化:使用字符串的形式監(jiān)聽對象屬性:
data() {
return {
docData: {
'doc_id': 1,
'tpl_data': 'abc'
}
}
}冷蚂,
watch: {
'docData.doc_id': {
handler(newVal, oldVal) {
......
}
}
}
這樣只會給對象的某個特定的屬性加監(jiān)聽器。
或者也可以使用計(jì)算屬性 computed 作為中間層汛闸,如下:
computed: {
age() {
return this.obj.age
}
},
watch: {
age(newValue, oldValue) {
console.log(newValue)
}
}
(3)handler
其值是一個回調(diào)函數(shù)蝙茶。即監(jiān)聽到變化時應(yīng)該執(zhí)行的函數(shù)。
6.表單監(jiān)聽
在開發(fā)中把表單動態(tài)綁定的所有數(shù)據(jù)都存放到一個對象诸老,這時候可以利用watch監(jiān)聽一個表單中的所有屬性的變化(即對象中任何一個屬性發(fā)生變化時隆夯,便會觸發(fā)該對象的監(jiān)聽,利用深度監(jiān)聽)别伏,從而去做一些邏輯處理蹄衷,比如:當(dāng)表單中的所有值都不為空的時候去執(zhí)行相應(yīng)的邏輯。
watch: {
obj: {
handler (newValue, oldValue) {
if (obj.a && obj.e && obj.d && obj.y && obj.w) {
// 當(dāng)表單中的所有值都不為空的時候執(zhí)行的函數(shù)
} else {
// 當(dāng)表單中的有值為空的時候執(zhí)行的函數(shù)
}
},
deep: true
}
}
7.總結(jié)
數(shù)組(一維畸肆、多維)的變化不需要通過深度監(jiān)聽宦芦,對象數(shù)組中對象的屬性變化則需要deep深度監(jiān)聽。
注意轴脐,不應(yīng)該使用箭頭函數(shù)來定義 watcher 函數(shù) (例如 searchQuery: newValue => this.updateAutocomplete(newValue))调卑。理由是箭頭函數(shù)綁定了父級作用域的上下文,所以 this 將不會按照期望指向 Vue 實(shí)例大咱,this.updateAutocomplete 將是 undefined恬涧。
watch 與 computed的區(qū)別:
watch:watch用于觀察和監(jiān)聽頁面上的vue實(shí)例,當(dāng)你需要在數(shù)據(jù)變化響應(yīng)時碴巾,執(zhí)行異步操作溯捆,或高性能消耗的操作,那么watch為最佳選擇厦瓢。
computed :可以關(guān)聯(lián)多個實(shí)時計(jì)算的對象提揍,當(dāng)這些對象中的其中一個改變時都會觸發(fā)這個屬性具有緩存能力,所以只有當(dāng)數(shù)據(jù)再次改變時才會重新渲染煮仇,否則就會直接拿取緩存中的數(shù)據(jù)劳跃。