總結(jié):
??1. 計(jì)算屬性computed
在使用時把将,一定要注意,函數(shù)里面的變量都會被監(jiān)聽忆矛,只要里面的某一個值變動察蹲,便會將整個函數(shù)執(zhí)行一遍。 而 watch
只是監(jiān)聽某一個值催训,若是監(jiān)聽的值里面也有很多變量洽议,也會全部監(jiān)聽
??2. 計(jì)算后的屬性可不在 data 中定義,如果定義會報錯漫拭,因?yàn)閷?yīng)的computed作為計(jì)算屬性定義并返回對應(yīng)的結(jié)果給這個變量,變量不可被重復(fù)定義和賦值亚兄。 而 watch
監(jiān)聽 data
中定義的變量變化
computed特性
1.是計(jì)算值,
2.應(yīng)用:就是簡化tempalte里面{{}}計(jì)算和處理props或$emit的傳值
3.具有緩存性嫂侍,頁面重新渲染值不變化,計(jì)算屬性會立即返回之前的計(jì)算結(jié)果儿捧,而不必再次執(zhí)行函數(shù)
watch特性
1.是觀察的動作荚坞,
2.應(yīng)用:監(jiān)聽props挑宠,$emit或本組件的值執(zhí)行異步操作
3.無緩存性,頁面重新渲染時值不變化也會執(zhí)行
接下來介紹下 各自的方法:
1. 計(jì)算屬性 computed
在一個計(jì)算屬性里可以完成各種復(fù)雜的邏輯颓影,包括運(yùn)算各淀、函數(shù)調(diào)用等,只要最終返回一個結(jié)果就可以诡挂。
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p> // 我們把復(fù)雜處理放在了計(jì)算屬性里面了
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
reversedMessage: function () {
// `this` 指向 vm 實(shí)例
return this.message.split('').reverse().join('')
}
}
});
結(jié)果:
? ? ? ?Original message: "Hello"
??Computed reversed message: "olleH"
計(jì)算屬性還可以依賴多個Vue 實(shí)例的數(shù)據(jù)碎浇,只要其中任一數(shù)據(jù)變化,計(jì)算屬性就會重新執(zhí)行璃俗,視圖也會更新奴璃。
每一個計(jì)算屬性都包含一個getter
和一個setter
,我們上面的兩個示例都是計(jì)算屬性的默認(rèn)用法城豁, 只是利用了getter
來讀取苟穆。
在你需要時,也可以提供一個setter
函數(shù)唱星, 當(dāng)手動修改計(jì)算屬性的值就像修改一個普通數(shù)據(jù)那樣時雳旅,就會觸發(fā)setter
函數(shù),執(zhí)行一些自定義的操作间聊,例如:
<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>
<script>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'zhang',
lastName: 'san'
},
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
}
}
},
updated () {
console.log('updated')
}
})
</script>
我們可以看到攒盈,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 值的代碼。
2. 方法
除了使用計(jì)算屬性外氏豌,我們也可以通過在表達(dá)式中調(diào)用方法來達(dá)到同樣的效果喉酌,如:
<div>{{reverseTitle()}}</div>
methods: {
reverseTitle: function () {
return this.title.split('').reverse().join('')
}
}
我們可以將同一函數(shù)定義為一個方法而不是一個計(jì)算屬性,兩種方式的最終結(jié)果確實(shí)是完全相同的泵喘。只是一個使用reverseTitle()
取值泪电,一個使用reverseTitle
取值。
然而纪铺,不同的是計(jì)算屬性是基于它們的依賴進(jìn)行緩存的相速。計(jì)算屬性只有在它的相關(guān)依賴發(fā)生改變時才會重新求值。
這就意味著只要 title還沒有發(fā)生改變鲜锚,多次訪問 reverseTitle計(jì)算屬性會立即返回之前的計(jì)算結(jié)果突诬,而不必再次執(zhí)行函數(shù)。
3. 偵聽屬性 watch
watch監(jiān)控自身屬性變化:
new Vue({
el: '#app',
data: {
firstName: 'hello',
lastName: 'vue',
fullName: 'hello.ve'
},
watch: {
'firstName': function(newval, oldval) {
// console.log(newval,oldval);
this.fullName = this.firstName + this.lastName;
},
'lastName': function(newval, oldval) {
// console.log(newval,oldval);
this.fullName = this.firstName + this.lastName;
}
}
});
watch監(jiān)控路由對象:
new Vue({
el: '#app',
router: router, //開啟路由對象
watch: {
'$route': function(newroute, oldroute) {
console.log(newroute, oldroute);
//可以在這個函數(shù)中獲取到當(dāng)前的路由規(guī)則字符串是什么
//那么就可以針對一些特定的頁面做一些特定的處理
}
}
})
watch監(jiān)聽對象的單個屬性:
watch如果想要監(jiān)聽對象的單個屬性的變化,必須用computed作為中間件轉(zhuǎn)化,因?yàn)閏omputed可以取到對應(yīng)的屬性值芜繁。
data(){
return{
'first':{
second:0
}
}
},
computed:{
secondChange(){
return this.first.second
}
},
watch:{
secondChange(){
console.log('second屬性值變化了')
}
},
簡單實(shí)現(xiàn) computed
和watch
公共類
function defineReactive(data, key, val, fn) {
let subs = [] // 新增
Object.defineProperty(data, key, {
configurable: true,
enumerable: true,
get: function() {
// 新增
if (data.$target) {
subs.push(data.$target)
}
return val
},
set: function(newVal) {
if (newVal === val) return
fn && fn(newVal)
// 新增
if (subs.length) {
// 用 setTimeout 因?yàn)榇藭r this.data 還沒更新
setTimeout(() => {
subs.forEach(sub => sub())
}, 0)
}
val = newVal
},
})
}
computed實(shí)現(xiàn)
function computed(ctx, obj) {
let keys = Object.keys(obj)
let dataKeys = Object.keys(ctx.data)
dataKeys.forEach(dataKey => {
defineReactive(ctx.data, dataKey, ctx.data[dataKey])
})
let firstComputedObj = keys.reduce((prev, next) => {
ctx.data.$target = function() {
ctx.setData({ [next]: obj[next].call(ctx) })
}
prev[next] = obj[next].call(ctx)
ctx.data.$target = null
return prev
}, {})
ctx.setData(firstComputedObj)
}
watch實(shí)現(xiàn)
function watch(ctx, obj) {
Object.keys(obj).forEach(key => {
defineReactive(ctx.data, key, ctx.data[key], function(value) {
obj[key].call(ctx, value)
})
})
}