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