在實際開發中,使用computed和mothod每每都能起到做用——返回或處理一個咱們要的值,可是適用場景不一樣;好比:當咱們要去時刻監控一個視圖層對應的數據層的值的變化時,使用computed就比較合理了,由於computed可緩存的,只要數據層所依賴的值不改變,computed就不會改變,而只要變了 ,computed的值就會實時更新到視圖層上,即computed是響應式的。而在這個例子中,若是使用watch也能夠實現,可是那就是對視圖層對應的數據層的值的依賴數據進行監聽,發生變化時再調用相應的函數更改該值,那麼watch和computed又有什麼區別呢? 異同以下:html
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; } } })
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的值會被更新
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 } }
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,避免之後重複訪問。
參考學習: