computed和watch詳解

本文目錄:

  • 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ù)劳跃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市浙垫,隨后出現(xiàn)的幾起案子刨仑,更是在濱河造成了極大的恐慌,老刑警劉巖夹姥,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杉武,死亡現(xiàn)場離奇詭異,居然都是意外死亡辙售,警方通過查閱死者的電腦和手機(jī)轻抱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旦部,“玉大人十拣,你說我怎么就攤上這事封拧。” “怎么了夭问?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長曹铃。 經(jīng)常有香客問我缰趋,道長,這世上最難降的妖魔是什么陕见? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任秘血,我火速辦了婚禮,結(jié)果婚禮上评甜,老公的妹妹穿的比我還像新娘灰粮。我一直安慰自己,他們只是感情好忍坷,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布粘舟。 她就那樣靜靜地躺著,像睡著了一般佩研。 火紅的嫁衣襯著肌膚如雪柑肴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天旬薯,我揣著相機(jī)與錄音晰骑,去河邊找鬼。 笑死绊序,一個胖子當(dāng)著我的面吹牛硕舆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骤公,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼抚官,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了淋样?” 一聲冷哼從身側(cè)響起耗式,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎趁猴,沒想到半個月后刊咳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡儡司,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年娱挨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捕犬。...
    茶點(diǎn)故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡跷坝,死狀恐怖酵镜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柴钻,我是刑警寧澤淮韭,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站贴届,受9級特大地震影響靠粪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毫蚓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一占键、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧元潘,春花似錦畔乙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氮帐,卻和暖如春嗅虏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背上沐。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工皮服, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人参咙。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓龄广,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蕴侧。 傳聞我的和親對象是個殘疾皇子择同,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內(nèi)容