1. 前言
本節(jié)介紹計(jì)算屬性的使用方法。包括什么是計(jì)算屬性,計(jì)算屬性的特點(diǎn),還有計(jì)算屬性和方法在實(shí)際使用中的區(qū)別。其中重點(diǎn)掌握計(jì)算屬性和方法的區(qū)別,了解它之后,才能在日常工作中靈活使用計(jì)算屬性和方法。
2. 慕課解釋
計(jì)算屬性,就是當(dāng)其依賴屬性的值發(fā)生變化時(shí),這個(gè)屬性的值會自動(dòng)更新,與之相關(guān)的 DOM 部分也會同步自動(dòng)更新?!?官方定義
計(jì)算屬性實(shí)際上是一個(gè)方法,它可以完成各種復(fù)雜的邏輯,包括運(yùn)算、函數(shù)調(diào)用等,并最終返回一個(gè)值。之前的章節(jié)中介紹了模版插值的語法,我們知道模板內(nèi)的可以使用表達(dá)式進(jìn)行計(jì)算,例如:{{ count * number }}
。但有時(shí)候我們的計(jì)算邏輯并非如此簡單,當(dāng)模板中放入太多的復(fù)雜邏輯會讓模板過于繁瑣且難以維護(hù)。計(jì)算屬性 computed
的使用可以解決此類問題。 computed
在項(xiàng)目中會大量使用,在使用時(shí)需要注意的是 computed
必須有一個(gè)返回值。使用 computed
并不難,難點(diǎn)在于如何編寫其內(nèi)部的復(fù)雜邏輯。
3. 寫一個(gè)計(jì)算屬性
前面介紹了什么是計(jì)算屬性,那么怎么去定義一個(gè)計(jì)算屬性呢?讓我們先來看一段代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>商品數(shù)量:{{count}} 個(gè)</h1>
<h1>商品單價(jià):{{unitPrice}} 元</h1>
<h1>商品總價(jià):{{totalPrice()}} 元</h1>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
count: 10,
unitPrice: 24
},
methods: {
totalPrice() {
return this.count * this.unitPrice
}
}
})
</script>
</html>
代碼解釋:上述例子中,模板語句中我們通過一定的邏輯運(yùn)算得到了商品的總價(jià)。像這樣的算法我們可以使用計(jì)算屬性來實(shí)現(xiàn),接下來我們用計(jì)算屬性來改寫這個(gè)例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>商品數(shù)量:{{count}} 個(gè)</h1>
<h1>商品單價(jià):{{unitPrice}} 元</h1>
<h1>商品總價(jià):{{totalPrice}} 元</h1>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
count: 10,
unitPrice: 24
},
computed: {
totalPrice() {
return this.count * this.unitPrice
}
}
})
</script>
</html>
代碼解釋:
第 14-16 行,我們定義了一個(gè)計(jì)算屬性 totalPrice
,它的返回值是商品的單價(jià)和數(shù)量相乘得到的總價(jià),在 html 模板中,我們直接用插值表達(dá)式 {{ totalPrice }} 來獲得商品的總價(jià),然后渲染到頁面上。
計(jì)算屬性的值會根據(jù)其函數(shù)內(nèi)部依賴的變化而重新計(jì)算。也就是說,在上面的例子中當(dāng) count 和 unitPrice 有任意一方發(fā)生改變時(shí) totalPrice 都會隨之更新,最后更新頁面。我們通過下面的例子來驗(yàn)證這一點(diǎn):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>商品數(shù)量:{{count}} 個(gè)</h1>
<h1>商品單價(jià):{{unitPrice}} 元</h1>
<h1>商品總價(jià):{{totalPrice}} 元</h1>
<button @click="changePrice">修改單價(jià)</button>
<button @click="changeCount">修改數(shù)量</button>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
count: 10,
unitPrice: 24
},
computed: {
totalPrice() {
return this.count * this.unitPrice
}
},
methods: {
changePrice() {
vm.unitPrice = vm.unitPrice + 1
},
changeCount() {
vm.count = vm.count + 1
}
}
})
</script>
</html>
代碼解釋:
第 5-7 行,我們在頁面中加入了修改單價(jià)和修改數(shù)量的兩個(gè)操作按鈕,每次點(diǎn)擊修改單價(jià)就對單價(jià) unitPrice + 1,點(diǎn)擊修改數(shù)量就對數(shù)量 count + 1。當(dāng)我們每次點(diǎn)擊時(shí),可以發(fā)現(xiàn)商品總價(jià)的值都會發(fā)生改變,因此計(jì)算屬性的值會隨著依賴的變化而更新。
4. Getter 和 Setter
前面我們介紹了如何編寫一個(gè)簡單的計(jì)算屬性,接下來我們介紹了一下如何通過 getter 和 setter 來編寫一個(gè)復(fù)雜的計(jì)算屬性。
每一個(gè)計(jì)算屬性都包含一個(gè) getter
和一個(gè) setter
,我們上面的兩個(gè)示例都是計(jì)算屬性的默認(rèn)用法,只是利用了 getter
來讀取。在你需要時(shí),也可以提供一個(gè) setter
函數(shù) , 當(dāng)手動(dòng)修改計(jì)算屬性的值就像修改一個(gè)普通數(shù)據(jù)那樣時(shí),就會觸發(fā) setter
函數(shù),執(zhí)行一些自定義的操作。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>全名: {{fullName}} </h1>
<h1>firstName: {{firstName}} </h1>
<h1>lastName: {{lastName}} </h1>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: '',
lastName: ''
},
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
})
</script>
</html>
代碼解釋:
上述代碼中我們定義了一個(gè) fullName 的計(jì)算屬性,并定義了它的 getter 和 setter。
第 4-6行,setter: 每次修改 fullName 時(shí)將姓賦值給 firstName,將名賦值給 lastName。
第 8-11行,getter: 每次獲取 fullName 時(shí)將 firstName 和 lastName 拼接后返回。
我們打開控制臺,運(yùn)行 vm.fullName = ‘John Doe’ 時(shí) setter 會被調(diào)用,vm.firstName 和 vm.lastName 也會相應(yīng)地被更新。在控制臺輸入 vm.firstName 回車可以看到它的值。
5. 計(jì)算屬性 VS 方法
如果不使用計(jì)算屬性,在 methods 里定義了一個(gè)方法,也可以實(shí)現(xiàn)相同的效果,甚至該方法還可以接受參數(shù),使用起來更靈活。
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>商品數(shù)量:{{count}} 個(gè)</h1>
<h1>商品單價(jià):{{unitPrice}} 元</h1>
<h1>商品總價(jià):{{totalPrice()}} 元</h1>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
count: 10,
unitPrice: 24
},
methods: {
totalPrice() {
return this.count * this.unitPrice
}
}
})
</script>
</html>
代碼解釋:
第 8-10 行,我們定義了一個(gè)方法 totalPrice
,它的返回值是商品的單價(jià)和數(shù)量相乘得到的總價(jià),在 html 模板中,我們直接用插值表達(dá)式 {{ totalPrice() }} 來獲得商品的總價(jià),然后渲染到頁面上。
既然 methods 同樣可以解決模板中復(fù)雜邏輯計(jì)算的問題,那么為什么還需要使用計(jì)算屬性呢?
原因就是:計(jì)算屬性是基于它的依賴緩存的。前面我們介紹過,計(jì)算屬性的改變?nèi)Q于其所依賴數(shù)據(jù)的變化,所以只要依賴數(shù)據(jù)不發(fā)生改變,計(jì)算屬性就不會更新。當(dāng)我們重復(fù)獲取計(jì)算屬性時(shí)它也不會重復(fù)計(jì)算,只會獲取緩存的值。而我們每次調(diào)用 methods 都會重新計(jì)算一遍,這樣將會消耗一部分性能。當(dāng)然,如何你不希望對數(shù)據(jù)進(jìn)行緩存,那么可以用方法來代替。
6. 小結(jié)
本節(jié),我們帶大家學(xué)習(xí)了 computed 在 vue 項(xiàng)目中的運(yùn)用。主要知識點(diǎn)有以下幾點(diǎn):
- 通過計(jì)算屬性 computed 來代替在模板編寫復(fù)雜邏輯的使用方法。
- 如何利用 setter 和 getter 來編寫一個(gè)復(fù)雜的計(jì)算屬性。
- 對比了計(jì)算屬性和方法的區(qū)別:計(jì)算屬性是有緩存的而方法每次都會重新執(zhí)行。