計算屬性computed裏面的getter和setter

參考一:

在 Vue 中,computed 的屬性可以被視爲是 data 一樣,可以讀取和設值,因此在 computed 中可以分成 getter(讀取) 和 setter(設值),一般情況下是沒有 setter 的,computed 預設只有 getter ,也就是隻能讀取,不能改變設值。

vue.js計算屬性默認只有 getter,因爲是默認值所以我們也常常省略不寫,如下代碼:

<div id="demo">{{ fullName }}</div>

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

其實computed裏的代碼完整的寫法應該是:

 computed: {
    fullName: {
      get(){
         return this.firstName + ' ' + this.lastName
      }
    }
  }

計算屬性getter的觸發時間

<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,都會觸發computed以及updated (),也就是說會執行: console.log('computed getter...')和console.log('updated') (用來驗證是不是執行了,沒有其他意思)

需要注意的是,不是說我們更改了getter裏使用的變量,就會觸發computed的更新,前提是computed裏的值必須要在模板裏使用才行。怎麼理解呢?

如下代碼,我們把template裏的fullName 註釋掉:

<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')
  }
})

就算我們更改了firstName以及lastName都不會觸發computed 中的 getter 中的console.log('computed getter...'),而只會觸發console.log('updated')

計算屬性settter

<template>
    <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>
</template>

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')
  }
})

在template 中,我們可以看到,input 是直接綁 v-model="fullName",如果我們這裏直接修改了fullName的值,那麼就會觸發setter,同時也會觸發getter以及updated函數。其執行順序是setter -> getter -> updated,如下:

console.log('computed setter...')
console.log('computed getter...')
console.log('updated')

這裏需要注意的是,並不是觸發了setter也就會觸發getter,他們兩個是相互獨立的。我們這裏修改了fullName會觸發getter是因爲setter函數裏有改變firstName 和 lastName 值的代碼。也就是說我們如果註釋掉上邊的setter中修改firstName 和lastName的代碼後就不會執行getter,如下:

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
        }

會執行,且順序如下

console.log('computed setter...')
console.log('updated')

參考二:

computed 計算屬性

定義:當其依賴的屬性的值發生變化的時,計算屬性會重新計算。反之則使用緩存中的屬性值。

一個完整的計算屬性如下:

computed: {
 example: {
   get () {
     return 'example'
   },
   set (newValue) {
     console.log(newValue)
   }
 }
複製代碼

基礎用法

1.計算屬性的getter函數

當其依賴的屬性的值發生變化的時,這個計算屬性的值也會自動更新。多用於"data,computed"的屬性。

<template>
  <div>
    <h4>測試</h4>
    <div>
      <input type="text" v-model="message" />
      <div>{{changeMessage}}</div>
    </div>
  </div>
</template>

<script>
   export default {
    data () {
       return {
         message: 'hello'
       }
     },
    computed: {
       changeMessage: {
        // 計算屬性:依賴message變化而變化  依賴沒變化就不會重新渲染;
        get () {
           return this.message + 'world'
        },
        set () {
        }
      }
     }
  }
</script>
複製代碼

2.計算屬性的setter函數

當賦值給計算屬性的時候,將調用setter函數。多用於在模板組件中需要修改計算屬性自身的值的時候。

<template>
  <div>
    <h4>測試</h4>
    <div>
      {{didi}}
      {{family}}
    </div>
    <div>
      {{didiFamily}}
    </div>
  </div>

</template>

<script>
   export default {
    data () {
       return {
        didi: 'didi',
        family: 'family'
       }
     },
    computed: {
      didiFamily:{
        //getter
        get:function(){
          return this.didi + ' ' + this.family
        },
        //setter
        set:function(newValue){
          // 這裏由於該計算屬性被賦值,將被調用
          console.log(newValue)
          this.didi = 123
          this.family = 456
        }
      }
    },
    mounted () {
      // 賦值,調用setter函數
      this.didiFamily = 'John Doe'
    }
  }
</script>
複製代碼

3.計算屬性的緩存

Vue實例中被觀察的數據屬性發生了改變時纔會重新執行getter,但是我們有時候計算屬性依賴實時的非觀察數據屬性,比如下面例子中的Data.now

<template>
  <div>
    <h4>測試</h4>
    <div>
      <input type="text" v-model="message" />
      <div>{{now}}</div>
    </div>
  </div>

</template>

<script>
   export default {
    data () {
       return {
         message: 'hello'
       }
     },
    computed: {
      now:{
        cache: false,
        get:function(){
          return Date.now() + this.message
        }
      }
    },
    mounted () {
      setInterval(() => {
        // 當緩存開關爲false的時候,定時器每次打印的時間都是不一樣的
        console.log(this.now)
      }, 500)
    }
  }
</script>
複製代碼

1.計算屬性getter不執行的場景

當包含計算屬性的節點被移除並且模板中其他地方沒有再引用該屬性的時候,那麼對應的計算屬性的getter函數方法不會執行

代碼實例如下

<template>
  <div>
    <h4>測試</h4>
    <div>
      <button @click="toggleShow">Toggle Show Total Price</button>
      <p v-if="showTotal">Total Price = {{totalPrice}}</p>
    </div>
  </div>

</template>

<script>
   export default {
    data () {
       return {
        showTotal: true,
        basePrice: 100
       }
     },
    computed: {
      totalPrice () {
        return this.basePrice + 1
      }
    },
    methods: {
      toggleShow () {
        this.showTotal = !this.showTotal
      }
    }
  }
</script>
複製代碼

2.在v-for中使用計算屬性,起到類似"過濾器的作用"

<template>
  <div>
    <h4>測試</h4>
    <div>
      <ul>
      	<li v-for="n in evenNumbers">{{n}}</li>
      </ul>
    </div>
  </div>

</template>

<script>
   export default {
    data () {
       return {
        numbers: [ 1, 2, 3, 4, 5 ]
       }
     },
    computed: {
      evenNumbers () {
        return this.numbers.filter(function (number) {
          return number % 2 === 0
        })
      }
    }
  }
</script>

複製代碼

3.watch與computed的set函數的比較

vuex 接收 的computed ,用set監測不到變化,必須要用watch纔可以生效;(原理:實質沒有改變computd的值,只是改變了get的return值 => 組件外的訪問)

v-model 改變的computed,用watch監測不到變化,必須要用computed對象中的set函數方法才能監測得到(原理:相當於每次改變了computed自身的值 => 組件內的訪問)

參考三:

一、getter 和 setter

首先要明確一點,每一個計算屬性都包含一個 getter 函數和 setter 函數。

舉個栗子:來實現一個 "顯示姓名" 的 小demo 。

<div id="app">
    {{ fullName }}                      // 3、渲染 fullName
</div>

var app = new Vue({
  el: '#app',
  data: {                               // 1、在data中定義 firstName 和 lastName
    firstName: 'Barry',
    lastName: 'Dong'
  },
  computed: {                          // 2、在計算屬性中定義 fullName
    fullName: function () {
      return this.firstName + this.lastName
    }
  }
})

現在你可以在頁面中看到 "BarryDong" 。

等等,getter 和 setter 呢?你不是說每一個計算屬性都包含一個 getter 和 setter 嗎?上面這個 demo 怎麼沒有呢?

這是因爲,計算屬性會默認使用 getter 函數。也就是說,即使你沒有明確寫出 getter 函數,計算屬性也會默認使用它。

上面的 demo 中的計算屬性,可以改寫成這樣:

computed: {
    fullName: {
      get: function () {             // 這裏就明確寫出了 getter 函數
        return this.firstName + this.lastName
      }
    }
}

運行結果與上次結果一致。

那麼 setter 函數呢?

setter 函數與 getter 函數類似,也是寫在計算屬性中。而不同之處在於,getter 函數是默認用法,setter 函數不是默認用法。如果你要使用 setter 函數,那麼你必須要手動寫出 setter 函數。

比如,上面的 demo ,需求變了:我要能顯示名字的同時,還能改變名字。(這裏我們要求,姓與名之間要用中文逗號分隔,不然不好識別哪個是姓,哪個是名)

<div id="app">
    {{ fullName }} <br>
</div>

var app = new Vue({
  el: '#app',
  data: {
    firstName: 'Barry',
    lastName: 'Dong'
  },
  computed: {
    fullName: {
      get: function () {
        return this.firstName + this.lastName
      },
      set: function (newName) {        // 我們加上了 setter 函數,可以傳入新的名字
        var name = newName.split(',')  // 把傳入的名字根據逗號,拆分成數組
        this.firstName = name[0]    // 數組的第一個元素爲 firstName
        this.lastName = name[1]     // 數組的第二個元素爲 lastName
      }
    }
  }
})

現在我們打開控制檯,在控制檯中改變名字:

訪問 Vue 實例的屬性的屬性,直接使用點運算符即可

那麼現在可以看到,頁面中渲染出了 "曾小賢" 。

總結:

  • 每一個計算屬性都包含一個 getter 函數和 setter 函數;
  • 計算屬性會默認使用 getter 函數;
  • 你也可以提供 setter 函數,當修改計算屬性的值時,就會觸發 setter 函數,執行一些自定義的操作。

二、methods 與 computed

首先要知道,methods 裏的方法,與 computed 裏的方法,可以起到同樣的作用。

舉個栗子:做一個 "現在距1970年的時間戳" demo 。

<div id="app">
    方法拿到的時間戳:{{ now() }} <br>
    計算屬性拿到的時間戳:{{ thisTime }}
</div>

var app = new Vue({
  el: '#app',
  data: {
    
  },
  methods: {               // 你看下面 computed 中的方法,是不是類似的?
    now: function () {
      return Date.now()
    }
  },
  computed: {
    thisTime: function () {
      return Date.now()
    }
  }
})

可以看到,兩種方法都拿到了時間戳。

那麼細心的同學會發現,上面代碼中:

<div id="app">
    方法拿到的時間戳:{{ now() }} <br>    // now 後面有括號
    計算屬性拿到的時間戳:{{ thisTime }}  // thisTime 後面沒有括號
</div>

這是爲啥呢?

因爲,now 是寫在 methods 中的,所以要調用。而 thisTime 是寫在 計算屬性 中的,既然是屬性,那自然就不用加括號了。

請牢記這一點。


三、計算屬性的緩存

這裏要結合 methods 來對比學習。

methods:只要頁面重新渲染,methods 中的方法就會重新執行;不渲染,就不執行。

如:

<div id="app">
    {{ text }}
</div>

var app = new Vue({
  el: '#app',
  data: {
    text: '星星點燈',          // text 在頁面中渲染
    message: '照亮我的前程'    // message 未在頁面中渲染
  },
  computed: {
    ...
  },
  methods: {
    ...
  }
})

如果通過 app.text 改變了 text 的值,那麼頁面就會重新渲染出 text 的值,methods 中的方法也會重新執行。而如果改變的是 message ,因爲 message 未在頁面中渲染,那麼頁面就不會重新渲染 message 的值,methods 中的方法不會重新執行。

computed:不管頁面是否渲染,只要計算屬性依賴的數據未發生改變,那麼計算屬性就不會發生變化,因爲計算屬性是基於它的緩存的。只有當計算屬性依賴的數據發生變化時,計算屬性纔會重新取值。

如:

<div id="app">
    {{ abc }}
</div>

var app = new Vue({
  el: '#app',
  data: {
    text: '星星點燈',
    message: '照亮我的前程',
    dongdong: '哼哼哈嘿'
  },
  computed: {
    abc: function () {
      return this.text + this.message    // 計算屬性依賴了 Vue 實例中的 text 和 message
    }
  },
  methods: {
    
  }
})

如果我將 Vue 實例中的 text 屬性更改:

那麼計算屬性就會重新取值,重新渲染:

頁面重新渲染

而如果我更改實例中的 dongdong 屬性,因爲計算屬性沒有依賴它,所以計算屬性不會變:

頁面仍然顯示:

 

那我們要在什麼時候使用計算屬性呢?

那就取決於你是否需要使用緩存了!如果你要遍歷一個很大的數組,或者要進行大量的運算,那麼你就可以使用計算屬性。

 

補充一點:

計算屬性不僅可以依賴當前 Vue 實例的數據,還可以依賴其他 Vue 實例的數據。

<div id="app">
    {{ text }} <br>
    {{ reverseText }}
</div>

var app2 = new Vue({
  el: '#app2',
  data: {
    text: '你自己卻不知道'
  }
})

var app = new Vue({
  el: '#app',
  data: {
    text: '我想說其實你很好'
  },
  computed: {
    reverseText: function() {
      return this.text.split('').reverse().join('') + ',' + app2.text    // 依賴 app2 中的 text
    }
  }
})

如果 app2 中的 text 改變,那麼計算屬性也會隨之改變。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章