摘要:寫文章不容易,點個贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于版本如果你覺得排版難看,請點擊下面鏈接或者拉到下面關(guān)注公眾號也可以吧原理依賴收集源碼版之引用數(shù)據(jù)類型上
寫文章不容易,點個贊唄兄弟
專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧
研究基于 Vue版本 【2.5.17】
如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧
【Vue原理】依賴收集 - 源碼版之引用數(shù)據(jù)類型
上一篇,我們已經(jīng)分析過了 基礎(chǔ)數(shù)據(jù)類型的 依賴收集
【Vue原理】依賴收集 - 源碼版之基本數(shù)據(jù)類型
這一篇內(nèi)容是針對 引用數(shù)據(jù)類型的數(shù)據(jù)的 依賴收集分析,因為引用類型數(shù)據(jù)要復雜些,必須分開寫
文章很長,高能預(yù)警,做好準備耐下心好,肯定還是有點收獲的
但是兩個類型的數(shù)據(jù)的處理,又有很多重復的地方,所以打算只寫一些差異性的地方就好了,否則顯得廢話很多
兩個步驟,都有不同的地方
1、數(shù)據(jù)初始化
2、依賴收集
數(shù)據(jù)初始化流程如果數(shù)據(jù)類型是引用類型,需要對數(shù)據(jù)進行額外的處理。
處理又分了 對象 和 數(shù)組 兩種,會分開來講
1對象
1、遍歷對象的每個屬性,同樣設(shè)置響應(yīng)式,假設(shè)屬性都是基本類型,處理流程跟上一篇一樣
2、每個數(shù)據(jù)對象會增加一個 ob 屬性
比如設(shè)置一個 child 的數(shù)據(jù)對象
下圖,你可以看到 child 對象處理之后添加了一個 ob 屬性
ob_ 屬性有什么用啊?
你可以觀察到,__ob__ 有一個 dep 屬性,這個 dep 是不是有點屬性,是的,在上一篇基礎(chǔ)數(shù)據(jù)類型中講過
那么這個 ob 屬性有什么用?。?/p>
你可以觀察到,__ob__ 有一個 dep 屬性,這個 dep 是不是有點屬性,是的,在上一篇基礎(chǔ)數(shù)據(jù)類型中講過
dep 正是存儲依賴的地方
比如 頁面引用了 數(shù)據(jù)child,watch 引用了數(shù)據(jù)child,那么child 就會把這個兩個保存在 dep.subs 中
dep.subs = [ 頁面-watcher,watch-watcher ]
但是,在上一篇基礎(chǔ)類型種, dep 是作為閉包存在的啊,并不是保存在什么【__ob__.dep】 中啊
沒錯,這就是 引用類型 和 基礎(chǔ)類型的區(qū)別了
基礎(chǔ)數(shù)據(jù)類型,只使用 【閉包dep】 來存儲依賴
引用數(shù)據(jù)類型,使用 【閉包dep】 和 【 __ob__.dep】 兩種來存儲依賴
什么?你說閉包dep 在哪里?好吧,在 defineReactive 的源碼中,你去看看這個方法的源碼,下面有
那么,為什么,引用類型需要 使用__ob__.dep 存儲依賴呢?
首先,明確一點,存儲依賴,是為了數(shù)據(jù)變化時通知依賴,所以 __ob__.dep 也是為了變化后的通知
閉包 dep 只存在 defineReactive 中,其他地方無法使用到,所以需要保存另外一個在其他地方使用
在其他什么地方會使用呢?
在Vue掛載原型上的方法 set 和 del 中,源碼如下
function set(target, key, val) { var ob = (target).__ob__; // 通知依賴更新 ob.dep.notify(); } Vue.prototype.$set = set;
function del(target, key) { var ob = (target).__ob__; delete target[key]; if (!ob) return // 通知依賴更新 ob.dep.notify(); } Vue.prototype.$delete = del;
這兩個方法,大家應(yīng)該都用過,為了給對象動態(tài) 添加屬性和 刪除屬性
但是如果直接添加屬性或者刪除屬性,Vue 是監(jiān)聽不到的,比如下面這樣
child.xxxx=1 delete child.xxxx
所以必須要通過 Vue 包裝過的方法 set 和 del 來操作
在 set 和 del 執(zhí)行完,是需要通知依賴更新的,但是我怎么通知?
此時,【__ob__.dep】 就發(fā)揮作用了!就因為依賴多收集了一份在 __ob__.dep 中
使用就是上面一句話,通知更新
ob.dep.notify();
2、數(shù)組
1、需要遍歷數(shù)組,可能數(shù)組是對象數(shù)組,如下面
[{name:1},{name:888}]
遍歷時,如果遇到子項是對象的,會跟上面解析對象一樣操作
2、給數(shù)組保存一個 ob 屬性
比如設(shè)置一個 arr 數(shù)組
看到 arr數(shù)組 加多了一個 ob 屬性
其實這個 ob 屬性 和 上一段講對象 的作用是差不多的,這里也只是說 __ob__.dep
數(shù)組中的 __ob__.dep 存儲的也是依賴,給誰用呢?
給 Vue 封裝的數(shù)組方法使用,要知道要想數(shù)組變化也被監(jiān)聽到,是必須使用Vue封裝的數(shù)組方法的,否則無法實時更新
這里舉重寫方法之一 push,其他的還有 splice 等,Vue 官方文檔已經(jīng)有過說明
var original = Array.prototype.push; Array.prototype.push = function() { var args = [], len = arguments.length; // 復制 傳給 push 等方法的參數(shù) while (len--) args[len] = arguments[len]; // 執(zhí)行 原方法 var result = original.apply(this, args); var ob = this.__ob__; // notify change ob.dep.notify(); return resul }
看到在執(zhí)行完 數(shù)組方法之后,同樣需要通知依賴更新,也就是通知 __ob__.dep 中收集的依賴去更新
現(xiàn)在,我們知道了,響應(yīng)式數(shù)據(jù)對 引用類型做了什么額外的處理,主要是加了一個 ob 屬性
我們已經(jīng)知道了 ob 有什么用,現(xiàn)在看看源碼是怎么添加 ob 的
// 初始化Vue組件的數(shù)據(jù) function initData(vm) { var data = vm.$options.data; data = vm._data = typeof data === "function" ? data.call(vm, vm) : data || {}; ....遍歷 data 數(shù)據(jù)對象的key ,重名檢測,合規(guī)檢測 observe(data, true); } function observe(value) { if (Array.isArray(value) || typeof value == "object") { ob = new Observer(value); } return ob }
function Observer(value) { // 給對象生成依賴保存器 this.dep = new Dep(); // 給 每一個對象 添加一個 __ob__ 屬性,值為 Observer 實例 value.__ob__ = this if (Array.isArray(value)) { // 遍歷數(shù)組,每一項都需要通過 observe 處理,如果是對象就添加 __ob__ for (var i = 0, l =value.length; i < l; i++) { observe(value[i]); } } else { var keys = Object.keys(value); // 給對象的每一個屬性設(shè)置響應(yīng)式 for (var i = 0; i < keys.length; i++) { defineReactive(value, keys[i]); } } };
源碼的流程跟上一篇差不多,只是處理引用數(shù)據(jù)類型會增加多幾行源碼的額外處理
我們之前只說了一種對象數(shù)據(jù)類型,比如下面這樣
如果會嵌套多層對象呢?比如這樣,會怎么處理
沒錯,Vue 會遞歸處理,當遍歷屬性,使用 defineReactive 處理時,遞歸調(diào)用 observe 處理(源碼標紅加粗)
如果值是對象,那么同樣給 值加多一個 ob
如果不是,那么正常往下走,設(shè)置響應(yīng)式
源碼如下
function defineReactive(obj, key, value) { // dep 用于中收集所有 依賴我的 東西 var dep = new Dep(); var val = obj[key] // 返回的 childOb 是一個 Observer 實例 // 如果值是一個對象,需要遞歸遍歷對象 var childOb = observe(val); Object.defineProperty(obj, key, { get() {...依賴收集跟初始化無關(guān),下面會講}, set() { .... } }); }
畫一個流程圖,僅供參考
哈哈哈,上面寫得好長啊,是有點,但是沒辦法,想說詳細點啊,好吧,還有一段,但是比較短一些哈哈哈,反正看完的人,我jio 得很厲害了,答應(yīng)我,如果你仔細看完了,評論一下好嗎,讓我知道有人仔細看了
依賴收集流程收集流程,就是重點關(guān)注 Object.defineProperty 設(shè)置的 get 方法了
跟 基礎(chǔ)類型數(shù)據(jù) 對比,引用類型的 收集方法也只是多了幾行處理,差異在兩行代碼
childOb.dep.depend,被我 簡單化為 childOb.dep.addSub(Dep.target)
dependArray(value)
可以先看下源碼,如下
function defineReactive(obj, key, value) { var dep = new Dep(); var val = obj[key] var childOb = observe(val); Object.defineProperty(obj, key, { get() { var value = val if (Dep.target) { // 收集依賴進 dep.subs dep.addSub(Dep.target); // 如果值是一個對象,Observer 實例的 dep 也收集一遍依賴 if (childOb) { childOb.dep.addSub(Dep.target) if (Array.isArray(value)) { dependArray(value); } } } return value } }); }
上面的源碼,混雜了 對象和 數(shù)組的處理,我們分開說
1、對象
在數(shù)據(jù)初始化的流程中,我們已經(jīng)知道值是對象的話,會存儲多一份依賴在 __ob__.dep 中
就只有一句話
childOb.dep.depend();
數(shù)組還有另外一個處理,就是
dependArray(value);
看下源碼,如下
function dependArray(value) { for (var i = 0, l = value.length; i < l; i++) { var e = value[i]; // 只有子項是對象的時候,收集依賴進 dep.subs e && e.__ob__ && e.__ob__.dep.addSub(Dep.target); // 如果子項還是 數(shù)組,那就繼續(xù)遞歸遍歷 if (Array.isArray(e)) { dependArray(e); } } }
顯然,是為了防止數(shù)組里面有對象,從而需要給 數(shù)組子項對象也保存一份
你肯定會問,為什么子項對象也要保存一份依賴?
1、頁面依賴了數(shù)組,數(shù)組子項變化了,是不是頁面也需要更新?但是子項內(nèi)部變化怎么通知頁面更新?所以需要給子項對象也保存一份依賴?
2、數(shù)組子項數(shù)組變化,就是對象增刪屬性,必須用到Vue封裝方法 set 和 del,set 和 del 會通知依賴更新,所以子項對象也要保存
看個栗子
頁面模板
看到數(shù)組的數(shù)據(jù),就存在兩個 ob
總結(jié)到這里,就可以很清楚,引用類型和 基礎(chǔ)類型的處理差異了
1、引用類型會多添加一個 __ob__屬性,其中包含 dep,用于存儲 收集到的依賴
2、對象使用 __ob__.dep,作用在 Vue 自定義的方法 set 和 del 中
3、數(shù)組使用 __ob__.dep,作用在 Vue 重寫的數(shù)組方法 push 等中
終于寫完了,真的好長,但是我覺得值得了
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109952.html
摘要:當東西發(fā)售時,就會打你的電話通知你,讓你來領(lǐng)取完成更新。其中涉及的幾個步驟,按上面的例子來轉(zhuǎn)化一下你買東西,就是你要使用數(shù)據(jù)你把電話給老板,電話就是你的,用于通知老板記下電話在電話本,就是把保存在中。剩下的步驟屬于依賴更新 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于 Vue版本 【...
摘要:寫文章不容易,點個贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于版本如果你覺得排版難看,請點擊下面鏈接或者拉到下面關(guān)注公眾號也可以吧原理依賴更新源碼版如果對依賴收集完 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于...
寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【Vue原理】Props - 源碼版 今天記錄 Props 源碼流程,哎,這東西,就算是研究過了,也真是會隨著時間慢慢忘記的。 幸好我做...
寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【Vue原理】NextTick - 源碼版 之 服務(wù)Vue 初次看的兄弟可以先看 【Vue原理】NextTick - 白話版 簡單了解下...
摘要:因為失去焦點之后被強制更新了一波嗯,這就是的作用,把頁面上的顯示值也過濾一遍 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【Vue原理】VModel - 源碼版之input詳...
閱讀 920·2023-04-25 18:51
閱讀 1874·2021-09-09 11:39
閱讀 3285·2019-08-30 15:53
閱讀 2104·2019-08-30 13:03
閱讀 1314·2019-08-29 16:17
閱讀 586·2019-08-29 11:33
閱讀 1888·2019-08-26 14:00
閱讀 2126·2019-08-26 13:41