摘要:寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專(zhuān)注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版今天繼續(xù)探索源碼,廢話不
寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟
專(zhuān)注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧
研究基于 Vue版本 【2.5.17】
如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧
【Vue原理】Watch - 源碼版
今天繼續(xù)探索 Watch 源碼,廢話不多說(shuō)了
帶著我的幾個(gè)疑問(wèn)開(kāi)始
1、什么時(shí)候初始化 2、怎么確定監(jiān)聽(tīng)哪些值 3、深度監(jiān)聽(tīng)怎么回事 4、怎么觸發(fā)我的函數(shù)
這些問(wèn)題的答案會(huì)摻雜在源碼的解析中,我發(fā)現(xiàn)這幾篇的寫(xiě)作套路都差不多.....
也可以看查一下我的白話版
什么時(shí)候初始化首先,從這個(gè)問(wèn)題開(kāi)始我們今天的探索之旅,請(qǐng)看源碼
function Vue(){ ... 其他處理 initState(this) ...解析模板,生成DOM 插入頁(yè)面 } function initState(vm) { ...處理 data,props,computed 等數(shù)據(jù) if (opts.watch) { initWatch(this, vm.$options.watch); } }
沒(méi)錯(cuò),當(dāng)你調(diào)用 Vue 創(chuàng)建實(shí)例過(guò)程中,會(huì)去處理各種選項(xiàng),其中包括處理 watch
initWatch處理 watch的方法是 initWatch,下面就呈上 源碼
function initWatch(vm, watch) { for (var key in watch) { var watchOpt = watch[key]; createWatcher(vm, key, handler); } }
然后這段源碼并沒(méi)有做什么驚天地泣鬼神的事情,只是簡(jiǎn)簡(jiǎn)單單的一個(gè)遍歷,然后每個(gè)watch 都使用一個(gè)叫什么 createWatcher 的東西去處理
這個(gè)函數(shù)到底是干嘛的?暗藏著什么秘密,歡迎來(lái)到我們今晚的 《走近科學(xué)》《源碼解析》
createWatcher 來(lái)看看他的真身
function createWatcher( // expOrFn 是 key,handler 可能是對(duì)象 vm, expOrFn, handler,opts ) { // 監(jiān)聽(tīng)屬性的值是一個(gè)對(duì)象,包含handler,deep,immediate if (typeof handler ==="object") { opts= handler handler = handler.handler } // 回調(diào)函數(shù)是一個(gè)字符串,從 vm 獲取 if (typeof handler === "string") { handler = vm[handler] } // expOrFn 是 key,options 是watch 的全部選項(xiàng) vm.$watch(expOrFn, handler, opts) }
大概就這樣吧
1、獲取到監(jiān)聽(tīng)回調(diào)
2、調(diào)用 vm.$watch
1、獲取監(jiān)聽(tīng)回調(diào)首先,你傳入的 watch 配置可能是這三種(還有更多,差不多,不解釋?zhuān)鬯牢遥?/p>
如果配置是個(gè)對(duì)象,就取handler 字段
如果配置是函數(shù),那么直接就是 監(jiān)聽(tīng)回調(diào)
如果配置是字符串,從實(shí)例上獲取函數(shù)
2、調(diào)用 vm.$watch看著這個(gè)方法,簡(jiǎn)直 mmp,還有完沒(méi)完,我從一個(gè)函數(shù)進(jìn)入一個(gè)函數(shù),又從這個(gè)函數(shù)進(jìn)入到另一個(gè)函數(shù),迷宮啊這是.....
顯然你不用急,下面還有更多......
好吧,還是先看源碼
Vue.prototype.$watch = function( // expOrFn 是 監(jiān)聽(tīng)的 key,cb 是監(jiān)聽(tīng)回調(diào),opts 是所有選項(xiàng) expOrFn, cb, opts ){ // expOrFn 是 監(jiān)聽(tīng)的 key,cb 是監(jiān)聽(tīng)的回調(diào),opts 是 監(jiān)聽(tīng)的所有選項(xiàng) var watcher = new Watcher(this, expOrFn, cb, opts); // 設(shè)定了立即執(zhí)行,所以馬上執(zhí)行回調(diào) if (opts.immediate) { cb.call(this, watcher.value); } };
看完了吧?這么短,你們肯定看得懂的啦,就兩件事
1、判斷是否立即執(zhí)行監(jiān)聽(tīng)回調(diào)如果你設(shè)置了 immediate 的話,表示不用等我數(shù)據(jù)變化,初始化時(shí)馬上執(zhí)行一遍,執(zhí)行的代碼就是直接調(diào)用 回調(diào),綁定上下文,傳入監(jiān)聽(tīng)值
2、每個(gè) watch 配發(fā) watcher代碼從這里開(kāi)始變得沉重,各位觀眾,喝口水,恰口飯,屏息觀看操作
看看 watcher 的源碼
“watcher 的源碼之前的文章也講過(guò)很多,但是對(duì)于每種選項(xiàng)的涉及的細(xì)節(jié)是不一樣的,所以每次都放上來(lái),但是只放跟本內(nèi)容相關(guān)的部分代碼,其他的省去以便我們快速理解”
var Watcher = function (vm, key, cb, opt) { this.vm = vm; this.deep = opt.deep; this.cb = cb; // 這里省略處理 xx.xx.xx 這種較復(fù)雜的key this.getter = function(obj) { return obj[key] }; // this.get 作用就是執(zhí)行 this.getter函數(shù) this.value = this.get(); };
再看看,新建 watcher 的時(shí)候 ,傳入了什么
1、監(jiān)聽(tīng)的 key
2、監(jiān)聽(tīng)回調(diào) (Watch 中的cb)
3、監(jiān)聽(tīng)配置的options
這里會(huì)涉及到三個(gè)問(wèn)題,現(xiàn)在來(lái)解釋
1、怎么對(duì)設(shè)置的 key 進(jìn)行監(jiān)聽(tīng)?我們要先對(duì) Watch 中的 this.getter 的函數(shù)進(jìn)行理解,他的本質(zhì)是為了獲取對(duì)象的key值
然后 getter 是在 watcher.get 中執(zhí)行的,看下 get 源碼
// 對(duì)本問(wèn)題進(jìn)行了獨(dú)家簡(jiǎn)單化的源碼 Watcher.prototype.get = function() { var value = this.getter(this.vm); return value };
你能看到,Watch 在結(jié)尾會(huì)立即執(zhí)行一次 watcher.get,其中便會(huì)執(zhí)行 getter,便會(huì)根據(jù)你監(jiān)聽(tīng)的key,去實(shí)例上讀取并返回,存放在 watcher.value 上
看到了嗎,從實(shí)例上讀取屬性,這句話。
首先,watch 初始化之前,data 應(yīng)該初始化完畢了,每個(gè) data 數(shù)據(jù)都已經(jīng)是響應(yīng)式的
使用例子來(lái)說(shuō)明一下
當(dāng) watch.getter 執(zhí)行,而讀取了 vm.name 的時(shí)候,name的依賴(lài)收集器就會(huì)收集到 watch-watcher
于是 name 變化的時(shí)候,會(huì)可以通知到 watch,監(jiān)聽(tīng)就成功了
2、如何進(jìn)行深度監(jiān)聽(tīng)?首先,深度監(jiān)聽(tīng),是你設(shè)置了 deep 的時(shí)候,如下
然后,觀察上面的 Watch 源碼,deep 會(huì)保存在watcher 中,以便后用
話鋒一轉(zhuǎn)
上一問(wèn)題說(shuō)過(guò),在 新建 watcher 的時(shí)候,會(huì)馬上執(zhí)行一個(gè) get,上個(gè)問(wèn)題的 get 源碼簡(jiǎn)化很多,把 處理深度監(jiān)聽(tīng)的部分去掉了,這里露出來(lái)了
Watcher.prototype.get = function() { Dep.target= this var value = this.getter(this.vm) if (this.deep) traverse(value) Dep.target= null return value };
沒(méi)錯(cuò),處理深度監(jiān)聽(tīng)只有一條語(yǔ)句!
if (this.deep) traverse(value)
value 是 getter 從實(shí)例上讀取監(jiān)聽(tīng)key 得到的值,沒(méi)有疑問(wèn)
但是 traverse 是何方神圣?come on 讓我們深入....
function traverse(val) { var i, keys; // 數(shù)組逐個(gè)遍歷 if (Array.isArray(val)) { i = val.length; // val[i] 就是讀取值了,然后值的對(duì)象就能收集到 watch-watcher while (i--) { traverse(val[i]) } } else { keys = Object.keys(val); i = keys.length; // val[keys[i]] 就是讀取值了,然后值的對(duì)象就能收集到 watch-watcher while (i--) { traverse(val[keys[i]]) } } }
你看它這段代碼長(zhǎng),其實(shí)是個(gè)紙老虎,做的就是一個(gè)事情,不斷遞歸深入讀取對(duì)象
他的想法是這樣的
因?yàn)樽x取,就可以讓這個(gè)屬性收集到 watch-watcher 的原則
就算是深層級(jí)的對(duì)象,其中的每個(gè)屬性也都是響應(yīng)式的,每個(gè)屬性都有自己的依賴(lài)收集器
通過(guò)不斷深入的讀取每個(gè)屬性,這樣每個(gè)屬性就都可以收集到 watch-watcher 了
這樣不管對(duì)象內(nèi)多深的屬性變化,都會(huì)通知到 watch-watcher
于是這樣就完成了深度監(jiān)聽(tīng)
3、監(jiān)聽(tīng)值變化,如何觸發(fā)監(jiān)聽(tīng)函數(shù)?通過(guò)上面的問(wèn)題,我們已經(jīng)了解了大部分了
監(jiān)聽(tīng)的數(shù)據(jù)變化的時(shí)候,就能通知 watch-watcher 更新,所謂通知更新,就是手動(dòng)調(diào)用 watch.update
速度看下 watcher.update 源碼
Watcher.prototype.update= function() { var value = this.get(); if (this.deep) { var oldValue = this.value; this.value = value; // cb 是監(jiān)聽(tīng)回調(diào) this.cb.call(this.vm, value, oldValue); } };
很簡(jiǎn)單嘛,就是讀取一遍值,然后保存新值,接著 調(diào)用 監(jiān)聽(tīng)回調(diào),并傳入新值和 舊值
ok,就這樣
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105380.html
摘要:而是在初始化時(shí),在讀取了監(jiān)聽(tīng)的數(shù)據(jù)的值之后,便立即調(diào)用一遍你設(shè)置的監(jiān)聽(tīng)回調(diào),然后傳入剛讀取的值設(shè)置了時(shí),如何工作我們都知道有一個(gè)選項(xiàng),是用來(lái)深度監(jiān)聽(tīng)的。 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專(zhuān)注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下...
寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專(zhuān)注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧 【Vue原理】Mixins - 源碼版 今天探索的是 mixins 的源碼,mixins 根據(jù)不同的選項(xiàng)類(lèi)型會(huì)做不同的處理 篇幅會(huì)有些長(zhǎng),...
摘要:而我覺(jué)得現(xiàn)在出一個(gè)白話版,是讓大家有興趣去研究源碼的時(shí)候,可以提前理清一下思路。相當(dāng)于封裝,提取公共部分。顯然,今天我不是來(lái)教大家怎么用的,怎么用看文檔就好了,我是講解生命的真諦內(nèi)部的工作原理。而這個(gè)不會(huì)合并,直接替換掉整個(gè)選項(xiàng) 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專(zhuān)注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版...
寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專(zhuān)注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧 【Vue原理】Props - 源碼版 今天記錄 Props 源碼流程,哎,這東西,就算是研究過(guò)了,也真是會(huì)隨著時(shí)間慢慢忘記的。 幸好我做...
寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專(zhuān)注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧 【Vue原理】NextTick - 源碼版 之 服務(wù)Vue 初次看的兄弟可以先看 【Vue原理】NextTick - 白話版 簡(jiǎn)單了解下...
閱讀 1922·2021-11-12 10:36
閱讀 2401·2021-09-01 10:29
閱讀 2422·2019-08-30 15:56
閱讀 1064·2019-08-30 12:56
閱讀 2387·2019-08-26 13:58
閱讀 2346·2019-08-23 18:38
閱讀 1552·2019-08-23 18:32
閱讀 2151·2019-08-23 16:53