[vue] computed 和 method

rencoo發表於2020-08-03
計算屬性

計算屬性只有在它的相關依賴發生改變時才會重新取值

Method

method每次渲染的時候都會被執行

舉一個栗子

<template>
...
<div>
  <p>計算屬性: {{ getComputedTotal }}</p>
  <p>計算屬性: {{ getComputedTotal }}</p>
  <p>方法: {{ getTotal() }}</p>
  <p>方法: {{ getTotal() }}</p>
</div>
...
</template>

<script>
...
data() {
  return {
      a1,
      b2,
      c3
  }  
},
computed:{
   getComputedTotal() {
       console.log('觸發computed')
       return this.a + this.b + this.c
   } 
},
methods:{
   getTotal() {
       console.log('觸發method')
       return this.a + this.b + this.c 
   }
}
...
</script>

執行結果:

觸發computed

觸發method

觸發method

可見,儘管計算屬性在頁面上'呼叫'了兩次,實際上函式只執行了第一次,而方法每次都會執行

計算屬性的好處

可以減少函式的執行,這對於巨大運算量的函式是一個很好的選擇

什麼場景下不能使用計算屬性

我們需要獲取一個運算結果,但是該結果是由非響應式屬性組成時,比如 Date.now()

在這種場景下,如果我們採用計算屬性,那麼由於不是響應式依賴,我們得到的都是初始的結果,這不是我們所期望的

再舉一個栗子

<template>
...
<div>
    <p>計算屬性時間: {{ getComputedTime }}</p>
    <p>計算屬性時間: {{ getComputedTime }}</p>
    <p>計算屬性時間: {{ getComputedTime }}</p>
    <p v-if="showTime">幾秒後, 計算屬性時間: {{ getComputedTime }}</p>
    <p>方法時間: {{ getTime() }}</p>
    <p>方法時間: {{ getTime() }}</p>
    <p>方法時間: {{ getTime() }}</p>
    <p v-if="showTime">幾秒後, 方法時間: {{ getTime() }}</p>
</div>
...
</template>

<script>
...
data() {
  return {
      showTimefalse,
  }  
},
created () {
    setTimeout(() => {
      this.showTime = true
    }, 5000)
},
computed:{
    getComputedTime () {
      return Date.now();
    },
},
methods:{
    getTime () {
      return Date.now()
    },
}
...
</script>

觀察介面初次渲染結果

計算屬性時間一直不變,每處都是初始值
方法時間,每處都是重新計算的結果(如果看不出可能是相隔時間太短…沒關係,我們可以用下面的方式來觀察)

由於我們設定了一個定時器來控制介面新的顯示內容,因此過大約5s後再觀察介面渲染的結果,我們發現
計算屬性時間仍舊未改變,還是原來的值
方法時間,不僅新顯示的內容重新計算了結果,而且之前的方法時間也都進行了重新計算(這是由於定時器觸發了元件的重渲染, 因此所有的方法時間又都重新計算了一遍)

關於元件的重渲染,可以看一下另外一個栗子,有助於更加深刻的理解

...
<p>方法時間: {{ getTime() }}</p>
<button @click="setFullName">setFullName</button>
...

methods:{
    getTime () {
      console.log('觸發method時間')
      return Date.now()
    },
    setFullName () {
      console.log('觸發name改變')
      this.fullName = 'John Doe'
    }
}

點選setFullName按鈕後,執行結果:

console: 觸發name改變
console: 觸發methond時間

很驚訝,獲取時間的方法也被執行了(儘管我們觸發的是改變名字的事件);按照正常思路,大家肯定覺得,方法改變了哪一塊,就應該重新渲染對應的那一塊檢視,而不應該將未改變的那塊進行重渲染(時間)

但是, 查了官網之後,發現vue2.x並不是這樣處理的,它的處理方式如下

在vue2.x中只要元件使用的眾多狀態中有一個發生變化,那麼整個元件就要重新渲染
在這裡vue做了優化:如果元件只有一個節點傳送了變化,直接重新渲染整個元件的所有節點,會造成很大的效能浪費,因此vue使用了vnode,並對其進行快取,在發生變化時,將上一次快取的vnode和當前新建立的vnode進行對比,只更新發生變化的節點

也就是說會觸發重渲染和重新計算,但是更新只是區域性的更新(patch),不知道這樣大家能不能明白

vue2.x改成這種方式,主要是出於效能的考慮,如果將每個節點都繫結一個watcher確實能達到咋們預期的哪塊改變就重新渲染哪塊,但是這樣的話,再編譯階段給每個節點繫結一個watcher是一種很大的浪費,尤其是在介面上有成千上萬的節點時,因此vue2.x採用了較vue1.x更加粗粒度的監聽方式,也就是元件級別,只要元件級別監聽到資料改變,那麼就重渲染整個元件,但是在重渲染元件時採用了虛擬dom來優化這個過程,將每次計算的結果進行快取,然後對比前後的兩次的計算結果,只對發生變化的節點進行更新

以上就是關於vue的計算屬性和方法之間區別的分享,在寫栗子的過程中我們還捎帶研究了下vue2.x的渲染原理。

每一次的疑惑,都是我們接近真理的最好機會,希望我們能把握這樣的機會,而不是白白地錯過真理,共勉!

相關文章