Vue偵測相關(guān)api的實現(xiàn)?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
創(chuàng)新互聯(lián)自2013年創(chuàng)立以來,公司以成都網(wǎng)站設(shè)計、做網(wǎng)站、成都外貿(mào)網(wǎng)站建設(shè)公司、系統(tǒng)開發(fā)、網(wǎng)絡(luò)推廣、文化傳媒、企業(yè)宣傳、平面廣告設(shè)計等為主要業(yè)務(wù),適用行業(yè)近百種。服務(wù)企業(yè)客戶上千多家,涉及國內(nèi)多個省份客戶。擁有多年網(wǎng)站建設(shè)開發(fā)經(jīng)驗。為企業(yè)提供專業(yè)的網(wǎng)站建設(shè)、創(chuàng)意設(shè)計、宣傳推廣等服務(wù)。 通過專業(yè)的設(shè)計、獨特的風(fēng)格,為不同客戶提供各種風(fēng)格的特色服務(wù)。
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 時,就會直接進行執(zhí)行回調(diào)函數(shù)
unwatch
實現(xiàn)方式是:
將被訪問到的數(shù)據(jù) dep 收集到 watchs 實例對象上,通過 this.deps 存起來
將被訪問到的數(shù)據(jù) dep.id 收集到 watchs 實例對象上,通過 this.depIds 存起來
最后通過 watchs 實例對象的 teardown 進行刪除
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ù)組時,只需進行遍歷,看內(nèi)部是否有 Object 對象即可,因為在第二步的時候,會對 val 進行判斷是否是數(shù)組,變改變七個方法的value,在遍歷;所以這邊只要是內(nèi)部數(shù)組都會進行攔截操作,添加依賴,即對象 {} 這種沒沒添加依賴。
seenObjects.clear() 當(dāng)內(nèi)部所以類型數(shù)據(jù)都添加好其依賴后,就清空。
window.target = undefined 消除依賴
vm.$set
用法: vm.$set(target, key, value)
作用
對于數(shù)組,進行 set 則是添加新元素,并需要觸發(fā)依賴更新
對于對象,如果 key 值存在,則是修改 value ;不存在,則是添加新元素,需新元素要進行響應(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) //添加新元素,并輸出依賴更新同時新元素也會進行`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) //進行響應(yīng)式處理 ob.dep.notify() //觸發(fā)依賴更新 returnv val }
vm.$delete
用法: vm.$delete( target, key)
作用
對于數(shù)組,進行 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() }
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。
本文名稱:Vue偵測相關(guān)api的實現(xiàn)
分享網(wǎng)址:http://jinyejixie.com/article42/jogghc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、營銷型網(wǎng)站建設(shè)、Google、關(guān)鍵詞優(yōu)化、軟件開發(fā)、企業(yè)建站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)