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