【Vue】Vue.js中watch、computed以及methods的聯繫和區別

computed計算屬性對比於method:

  1. computed響應式,method非響應;computed在調用時只有當其引用的響應式屬性改變時纔會調用函數從新計算值(存在緩存),而methods是一個函數,每次調用就是執行其函數式
  2. computed中的成員能夠只定義一個函數做爲只讀屬性,也能夠自定義get/set做爲讀寫屬性
  3. computed以vue對象的屬性形式存在
在實際開發中,使用computed和mothod每每都能起到做用——返回或處理一個咱們要的值,可是適用場景不一樣;好比:當咱們要去時刻監控一個視圖層對應的數據層的值的變化時,使用computed就比較合理了,由於computed可緩存的,只要數據層所依賴的值不改變,computed就不會改變,而只要變了 ,computed的值就會實時更新到視圖層上,即computed是響應式的。

而在這個例子中,若是使用watch也能夠實現,可是那就是對視圖層對應的數據層的值的依賴數據進行監聽,發生變化時再調用相應的函數更改該值,那麼watch和computed又有什麼區別呢? 異同以下:html

computed計算屬性對比於watch偵聽器:

  1. 相同點:都是vue對監聽器的實現,都起到監聽/依賴一個數據並進行處理的做用
  2. 在應用中,computed主要應用於同步數據,而watch是用來觀測一個值的變化去實現一段開銷較大的複雜業務邏輯或執行異步操做
  3. 能用computed時優先使用computed,避免當咱們須要獲得的值依賴於多個數據時屢次調用watch的尷尬
  4. watch監聽的值接收兩個參數——新值、舊值 ,能夠設置在初始化時調用vue

    例:node

    watch:監聽一個屬性值的變化並執行相應的函數數組

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

    computed:依賴其餘屬性所計算出來的值緩存

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

高級用法:

計算屬性的setter

var vm = new Vue({
  el:'#demo',
  data:{
    firstName:'Foo',
    lastName:'Bar'
  },
  computed:{
    fullName:{
      get(){
          return this.firstName + '·' + this.lastName        
      },
      set(newVal){
        var name = NewVal.split('·')
        this.firstName = name[0];
        this.lastName = name[name.length-1]
      }
    }
  }
})
//運行vm.fullName = 'Kobe Bryant'時,set方法會被調用,vm.firstName和vm.lastName的值會被更新

偵聽器的handler方法和immediate屬性

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      console.log('第一次沒有執行')
      this.fullName = val + '·' + this.lastName
    }
  }
})

若是想firstName在第一次被綁定的時候就執行:dom

watch: {
  firstName: {
    handler(val){
      console.log('第一次執行了')
      this.fullName = val + '·' + this.lastName
    },
    immediate:true//在watch中聲明後當即執行handler
  }
}

偵聽器的deep屬性

var vm = new Vue({
  el:'#demo',
  data:{
        item:{
      a:'',
      b:''
    }
  },
  watch:{
    item:{
            handler(val){
        console.log('item.a changed')
      },
      immediate: true
    }
  }
})
//運行vm.item.a = '123',發現控制檯沒有打印「item.a changed」

改變item.a的值發現控制檯沒有打印字符串,這是由於vue沒法檢測到對象屬性的添加或者刪除。因爲vue會在初始化實例時給實例的屬性執行getter/setter轉化過程,因此屬性必須在data對象上存在才能讓Vue轉換它,才能是響應式的異步

默認狀況下watch只監聽對對象的引用,如當this.item = {a: '123',b:'123'}執行時handler就會執行,ide

深度遍歷:
watch: {
  obj: {
    handler(val) {
      console.log('item.a changed')
    },
      immediate: true,
      deep: true
  }
}

deep的做用是:在對象一層層往下遍歷,每一層都加上偵聽器函數

優化

可是使用deep屬性會給每一層都加上監聽器,性能開銷可能就會很是大了。這樣咱們能夠用字符串的形式來優化:post

watch: {
    'item.a': {
      handler(val) {
       console.log('item.a changed')
      },
      immediate: true
      // deep: true
    }
  }

直到遇到'item.a'屬性,纔會給該屬性設置監聽函數,提升性能。

源碼實現:
/* @flow */

import { _Set as Set, isObject } from '../util/index'
import type { SimpleSet } from '../util/index'
import VNode from '../vdom/vnode'

const seenObjects = new Set()

/**
 * Recursively traverse an object to evoke all converted
 * getters, so that every nested property inside the object
 * is collected as a "deep" dependency.
 */
export function traverse (val: any) {
  _traverse(val, seenObjects)
  seenObjects.clear()
}

function _traverse (val: any, seen: SimpleSet) {
  let i, keys
  const isA = Array.isArray(val)
  if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {
    return
  }
  if (val.__ob__) {
    const depId = val.__ob__.dep.id
    if (seen.has(depId)) {
      return
    }
    seen.add(depId)
  }
  if (isA) {
    i = val.length
    while (i--) _traverse(val[i], seen)
  } else {
    keys = Object.keys(val)
    i = keys.length
    while (i--) _traverse(val[keys[i]], seen)
  }
}

若是this.deep == true,即存在deep,則觸發每一個深層對象的依賴,追蹤其變化。traverse方法遞歸每個對象或者數組,觸發它們的getter,使得對象或數組的每個成員都被依賴收集,造成一個「深(deep)」依賴關係。這個函數實現還有一個小的優化,遍歷過程當中會把子響應式對象經過它們的 dep.id 記錄到 seenObjects,避免之後重複訪問。

參考學習:

https://cn.vuejs.org/v2/guide...

https://juejin.im/post/5b87f1...

相關文章
相關標籤/搜索