vm.$watch
用法: vm.$watch( expOrFn, callback, [options] ),返回值為unwatch是一個函數(shù)用來取消觀察;下面主要理解options中的兩個參數(shù)deep和immediate以及unwatch
Vue.prototype.$watch = function (expOrFn, cb, options) { const vm = this options = options || {} const watcher = new Watcher(vm, expOrFn, cb, options) if(options.immediate) { cb.call(vm, watcher,.value) } return function unwatchFn() { watcher.teardown() } }immediate
從上面代碼中可以看出當(dāng)immediate為true時,就會直接進(jìn)行執(zhí)行回調(diào)函數(shù)
unwatch 實現(xiàn)方式是:將被訪問到的數(shù)據(jù)dep收集到watchs實例對象上,通過this.deps存起來
將被訪問到的數(shù)據(jù)dep.id收集到watchs實例對象上,通過this.depIds存起來
最后通過watchs實例對象的teardown進(jìn)行刪除
class Watcher { constructor (vm, expOrFn, cb) { this.vm = vm this.deps = [] this.depIds = new Set() if(typeof expOrFn === "function") { this.getter = expOrFn }else { this.getter = parsePath(expOrFn) } this.cb = cb this.value = this.get() } .... addDep (dep) { const id = dep.id //參數(shù)dep是Dep實例對象 if(!this.depIds.has(id)) { //判斷是否存在避免重復(fù)添加 this.depIds.add(id) this.deps.push(dep) dep.addSub(this) //this 是依賴 } } teardown () { let i = this.deps.length while (i--) { this.deps[i].removeSub(this) } } } let uid = 0 class Dep { constructor () { this.id = uid++ ... } ... depend () { if(window.target) { window.target.addDep(this) //將this即當(dāng)前dep對象加入到watcher對象上 } } removeSub (sub) { const index = this.subs.indexOf(sub) if(index > -1) { return this.subs.splice(index, 1) } } }分析
當(dāng)執(zhí)行teardown() 時需要循環(huán);因為例如expOrFn = function () { return this.name + this.age },這時會有兩個dep分別是name與age分別都加入了watcher依賴(this),都會加入到this.deps中,所以需要循環(huán)將含有依賴的dep都刪除其依賴
deep 需要明白的是deep干啥用的,例如data = {arr: [1, 2, {b: 6]},當(dāng)我們只是監(jiān)聽data.arr時,在[1, 2, {b: 66}]這個數(shù)值內(nèi)部發(fā)生變化時,也需要觸發(fā),即b = 888
怎么做呢?class Watcher { constructor (vm, expOrFn, cb, options) { this.vm = vm this.deps = [] this.depIds = new Set() if(typeof expOrFn === "function") { this.getter = expOrFn }else { this.getter = parsePath(expOrFn) } if(options) { //取值 this.deep = !!options.deep }else { this.deep = false } this.cb = cb this.value = this.get() } get () { window.target = this let value = this.getter.call(vm, vm) if(this.deep) { traverse(value) } window.target = undefined return value } ... } const seenObjects = new Set() function traverse (val) { _traverse(val, seenObjects) seenObjects.clear() } function _traverse(val, seen) { let i, keys const isA = Array.isArray(val) if((!isA && isObject(val)) || Object.isFrozen(val)) { //判斷val是否是對象或者數(shù)組以及是否被凍結(jié) return } if(val._ob_) { const depId = val._ob_.dep.id //可以看前面一篇我們對Observer類添加了this.dep = new Dep(),所以能訪問其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[i], seen) } }分析
window.target = this,寄存依賴
let value = this.getter.call(vm, vm) 訪問當(dāng)前val,并執(zhí)行get
的dep.depend(),如果發(fā)現(xiàn)val為數(shù)組,則將依賴加入到observer的dep中,也就實現(xiàn)了對當(dāng)前數(shù)組的攔截
traverse(value) 也就是執(zhí)行_traverse(val, seenObjects);核心就是對被Observer的val通過val[i]通過這種操作,間接觸發(fā)get,將依賴添加到當(dāng)前數(shù)值的dep中,這樣也就實現(xiàn)了,當(dāng)內(nèi)部數(shù)據(jù)發(fā)生變化,也會循環(huán)subs執(zhí)行依賴的update,從而觸發(fā)回調(diào);當(dāng)是數(shù)組時,只需進(jìn)行遍歷,看內(nèi)部是否有Object對象即可,因為在第二步的時候,會對val進(jìn)行判斷是否是數(shù)組,變改變七個方法的value,在遍歷;所以這邊只要是內(nèi)部數(shù)組都會進(jìn)行攔截操作,添加依賴,即對象{}這種沒沒添加依賴。
seenObjects.clear()當(dāng)內(nèi)部所以類型數(shù)據(jù)都添加好其依賴后,就清空。
window.target = undefined消除依賴
vm.$set用法: vm.$set(target, key, value)
作用對于數(shù)組,進(jìn)行set則是添加新元素,并需要觸發(fā)依賴更新
對于對象,如果key值存在,則是修改value;不存在,則是添加新元素,需新元素要進(jìn)行響應(yīng)式處理,以及觸發(fā)更新
對于對象本身不是響應(yīng)式,則直接添加key-value,無需處理
Vue.prototype.$set = function (target, key, val) { if(Array.isArray(target) && isValidArrayIndex(key)) { //是數(shù)組并且key有效 target.length = Math.max(target.length, key) //處理key > target.length target.splice(key, 1, val) //添加新元素,并輸出依賴更新同時新元素也會進(jìn)行`Obsever`處理 return val } if(key in targert && !(key in Object.prototype) { //能遍歷并且是自身key target[key] = val //觸發(fā)set,執(zhí)行依賴更新 return val } const ob = target._ob_ if(target.isVue || (ob && ob.vm.Count) { //不是vue實例也不是vue實例的根對象(即不是this.$data跟對象) //觸發(fā)警告 return } if(!ob) { //只添加 target[key] = val return val } defineReactive(ob.value, key, val) //進(jìn)行響應(yīng)式處理 ob.dep.notify() //觸發(fā)依賴更新 returnv val }vm.$delete
用法: vm.$delete( target, key)
作用對于數(shù)組,進(jìn)行delete則是刪除新元素,并需要觸發(fā)依賴更新
對于對象,如果key值不存在,直接return,存在,刪除元素,
對于對象本身不是響應(yīng)式,則只刪除key-value,無需其他處理
Vue.prototype.$delete = function (target, key) { if(Array.isArray(target) && isValidArrayIndex(key)) { target.splice(key, 1) return } const ob = target._ob_ if(target.isVue || (ob && ob.vm.Count) { //不是vue實例也不是vue實例的根對象(即不是this.$data跟對象) //觸發(fā)警告 return } if(!hasOwn(target, key)) { return } delete target[key] if(!ob) { return } ob.dep.notify() }
掘金地址
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109735.html
摘要:由來最近在看深入淺出,第一篇變化偵測,想把自己的理解總結(jié)一下。的變化偵測總結(jié)一下我看了后的理解將數(shù)據(jù)變成可響應(yīng)式的,即將數(shù)據(jù)變成可監(jiān)聽的。由來 最近在看深入淺出vuejs,第一篇變化偵測,想把自己的理解總結(jié)一下。 Object的變化偵測 總結(jié)一下我看了后的理解 將數(shù)據(jù)變成可響應(yīng)式的,即將數(shù)據(jù)變成可監(jiān)聽的。通過Observer類來實現(xiàn) 依賴是什么?就是這個數(shù)據(jù)在哪里用到了,相當(dāng)于this當(dāng)...
摘要:由來最近在看深入淺出,第一篇變化偵測,想把自己的理解總結(jié)一下。的變化偵測總結(jié)一下我看了后的理解將數(shù)據(jù)變成可響應(yīng)式的,即將數(shù)據(jù)變成可監(jiān)聽的。 由來 最近在看深入淺出vuejs,第一篇變化偵測,想把自己的理解總結(jié)一下。 Object的變化偵測 總結(jié)一下我看了后的理解 將數(shù)據(jù)變成可響應(yīng)式的,即將數(shù)據(jù)變成可監(jiān)聽的。通過Observer類來實現(xiàn) 依賴是什么?就是這個數(shù)據(jù)在哪里用到了,相當(dāng)于th...
摘要:總結(jié)最后我們依照下圖參考深入淺出,再來回顧下整個過程在后,會調(diào)用函數(shù)進(jìn)行初始化,也就是過程,在這個過程通過轉(zhuǎn)換成了的形式,來對數(shù)據(jù)追蹤變化,當(dāng)被設(shè)置的對象被讀取的時候會執(zhí)行函數(shù),而在當(dāng)被賦值的時候會執(zhí)行函數(shù)。 前言 Vue 最獨特的特性之一,是其非侵入性的響應(yīng)式系統(tǒng)。數(shù)據(jù)模型僅僅是普通的 JavaScript 對象。而當(dāng)你修改它們時,視圖會進(jìn)行更新。這使得狀態(tài)管理非常簡單直接,不過理解...
摘要:此模塊包含的設(shè)計思路即為預(yù)以匹配降級方案。沒有默認(rèn)編譯該模塊,以及利用該模塊判斷后提供平臺相關(guān)邏輯的主要原因在于其設(shè)計原則的代碼完成核心的功能。此處,也引出了代碼實現(xiàn)的另一個基本原則面向功能標(biāo)準(zhǔn),先功能覆蓋再優(yōu)雅降級。 在進(jìn)入 Zepto Core 模塊代碼之前,本節(jié)簡略列舉 Zepto 及其他開源庫中一些 Polyfill 的設(shè)計思路與實現(xiàn)技巧。 涉及模塊:IE/IOS 3/Dete...
閱讀 2739·2021-11-22 13:54
閱讀 1077·2021-10-14 09:48
閱讀 2302·2021-09-08 09:35
閱讀 1566·2019-08-30 15:53
閱讀 1178·2019-08-30 13:14
閱讀 616·2019-08-30 13:09
閱讀 2531·2019-08-30 10:57
閱讀 3344·2019-08-29 13:18