Vue源码分析
文章目錄
- Vue源碼分析
- vue源碼
- Object.defineProperty
- MVVM
- MVVM 類的實(shí)現(xiàn)
- 模板編譯 Compile 類的實(shí)現(xiàn)
- 1、解析根節(jié)點(diǎn)內(nèi)的 Dom 結(jié)構(gòu)
- 2、編譯文檔碎片中的結(jié)構(gòu)
- 3、CompileUtil 對(duì)象中指令方法的實(shí)現(xiàn)
- 觀察者 Watcher 類的實(shí)現(xiàn)
- 發(fā)布訂閱 Dep 類的實(shí)現(xiàn)
- 數(shù)據(jù)劫持 Observer 類的實(shí)現(xiàn)
- 驗(yàn)證 MVVM
- 虛擬dom
- 什么是dom?
- DIFF算法
Vue源碼分析
vue源碼
- Object.defineProperty
- 數(shù)據(jù)劫持Observer
- 依賴Dep
- Watcher
- xxxMinxin(混合)
- proxy(代理)
- defineReactive(響應(yīng)式)
Object.defineProperty
- 動(dòng)態(tài)給對(duì)象添加屬性
- 實(shí)現(xiàn)屬性的訪問(wèn)攔截
訪問(wèn)屬性 obj.name 一定會(huì)調(diào)用get方法
寫(xiě)入屬性 obj.name=xxx 一定會(huì)調(diào)用set方法
MVVM
MVVM 設(shè)計(jì)模式,是由 MVC(最早來(lái)源于后端)、MVP 等設(shè)計(jì)模式進(jìn)化而來(lái),M - 數(shù)據(jù)模型(Model),VM - 視圖模型(ViewModel),V - 視圖層(View)。
在 MVC 模式中,除了 Model 和 View 層以外,其他所有的邏輯都在 Controller 中,Controller 負(fù)責(zé)顯示頁(yè)面、響應(yīng)用戶操作、網(wǎng)絡(luò)請(qǐng)求及與 Model 的交互,隨著業(yè)務(wù)的增加和產(chǎn)品的迭代,Controller 中的處理邏輯越來(lái)越多、越來(lái)越復(fù)雜,難以維護(hù)。為了更好的管理代碼,為了更方便的擴(kuò)展業(yè)務(wù),必須要為 Controller “瘦身”,需要更清晰的將用戶界面(UI)開(kāi)發(fā)從應(yīng)用程序的業(yè)務(wù)邏輯與行為中分離,MVVM 為此而生。
Vue是什么? — 函數(shù)
- vue是不是一個(gè)mvvm框架?
- model—數(shù)據(jù)模型 數(shù)據(jù) data
- view—視圖–展示 {{xxx}}
- viewmodel—數(shù)據(jù)驅(qū)動(dòng)
proxy
- obj.name
- vm–vue實(shí)例
- data–定義的屬性
- 把data中的屬性定義在vm實(shí)例上 vm._data.xxx
頁(yè)面渲染過(guò)程
- 取data里的數(shù)據(jù)定義在vm實(shí)例上
- 遍歷data屬性—對(duì)每一個(gè)屬性進(jìn)行監(jiān)聽(tīng)–Observer
- 在Observer里使用defineReactive把屬性定義成響應(yīng)式 {{obj.name}}
讀取屬性–進(jìn)入defineReactive定義的攔截器中
更新屬性–進(jìn)入defineReactive定義的攔截器中
defineReactive每定義一個(gè)屬性都有一個(gè)Dep 是不是閉包?
name屬性里面也有一個(gè)Dep - 什么是Dep?
用來(lái)保存Watcher(渲染W(wǎng)atcher),通知Watcher更新頁(yè)面 - 當(dāng)data初始化完畢后
name–dep,頁(yè)面渲染時(shí){{obj.name}}會(huì)執(zhí)行渲染W(wǎng)atcher,Dep.target是什么?
執(zhí)行渲染W(wǎng)atcher—執(zhí)行g(shù)et()—putTarget(this)—Dep.target==渲染W(wǎng)atcher
進(jìn)入defineReactive攔截器—添加依賴關(guān)系 屬性—dep—Watcher - 響應(yīng)式如何實(shí)現(xiàn)?
修改obj.name=xxx,會(huì)進(jìn)入defineReactive的set方法—dep.notify()—通知所有Watcher更新–虛擬dom–diff–渲染頁(yè)面
在 Vue 的 MVVM 設(shè)計(jì)中,我們主要針對(duì) Compile(模板編譯)、Observer(數(shù)據(jù)劫持)、Watcher(數(shù)據(jù)監(jiān)聽(tīng))和 Dep(發(fā)布訂閱)幾個(gè)部分來(lái)實(shí)現(xiàn),核心邏輯流程可參照下圖:
MVVM 類的實(shí)現(xiàn)
在 Vue 中,對(duì)外只暴露了一個(gè)名為 Vue 的構(gòu)造函數(shù),在使用的時(shí)候 new 一個(gè) Vue 實(shí)例,然后傳入了一個(gè) options 參數(shù),類型為一個(gè)對(duì)象,包括當(dāng)前 Vue 實(shí)例的作用域 el、模板綁定的數(shù)據(jù) data 等等。
我們模擬這種 MVVM 模式的時(shí)候也構(gòu)建一個(gè)類,名字就叫 MVVM,在使用時(shí)同 Vue 框架類似,需要通過(guò) new 指令創(chuàng)建 MVVM 的實(shí)例并傳入 options。
文件:MVVM.js
通過(guò)上面代碼,我們可以看出,在我們 new 一個(gè) MVVM 的時(shí)候,在參數(shù) options 中傳入了一個(gè) Dom 的根元素節(jié)點(diǎn)和數(shù)據(jù) data 并掛在了當(dāng)前的 MVVM 實(shí)例上。
當(dāng)存在根節(jié)點(diǎn)的時(shí)候,通過(guò) Observer類 對(duì) data 數(shù)據(jù)進(jìn)行了劫持,并通過(guò) MVVM 實(shí)例的方法 proxyData 把 data 中的數(shù)據(jù)掛在當(dāng)前 MVVM 實(shí)例上,同樣對(duì)數(shù)據(jù)進(jìn)行了劫持,是因?yàn)槲覀冊(cè)讷@取和修改數(shù)據(jù)的時(shí)候可以直接通過(guò) this 或 this.$data,在 Vue 中實(shí)現(xiàn)數(shù)據(jù)劫持的核心方法是 Object.defineProperty,我們也使用這個(gè)方式通過(guò)添加 getter 和 setter 來(lái)實(shí)現(xiàn)數(shù)據(jù)劫持。
最后使用 Compile 類對(duì)模板和綁定的數(shù)據(jù)進(jìn)行了解析和編譯,并渲染在根節(jié)點(diǎn)上,之所以數(shù)據(jù)劫持和模板解析都使用類的方式實(shí)現(xiàn),是因?yàn)榇a方便維護(hù)和擴(kuò)展,其實(shí)不難看出,MVVM 類其實(shí)作為了 Compile 類和 Observer 類的一個(gè)橋梁。
模板編譯 Compile 類的實(shí)現(xiàn)
Compile 類在創(chuàng)建實(shí)例的時(shí)候需要傳入兩個(gè)參數(shù),第一個(gè)參數(shù)是當(dāng)前 MVVM 實(shí)例作用的根節(jié)點(diǎn),第二個(gè)參數(shù)就是 MVVM 實(shí)例,之所以傳入 MVVM 的實(shí)例是為了更方便的獲取 MVVM 實(shí)例上的屬性。
在 Compile 類中,我們會(huì)盡量的把一些公共的邏輯抽取出來(lái)進(jìn)行最大限度的復(fù)用,避免冗余代碼,提高維護(hù)性和擴(kuò)展性,我們把 Compile 類抽取出的實(shí)例方法主要分為兩大類,輔助方法和核心方法,在代碼中用注釋標(biāo)明。
1、解析根節(jié)點(diǎn)內(nèi)的 Dom 結(jié)構(gòu)
文件:Compile.js
class Compile {constructor(el, vm) {this.el = this.isElementNode(el) ? el : document.querySelector(el);this.vm = vm;// 如過(guò)傳入的根元素存在,才開(kāi)始編譯if (this.el) {// 1、把這些真實(shí)的 Dom 移動(dòng)到內(nèi)存中,即 fragment(文檔碎片)let fragment = this.node2fragment(this.el);}}/* 輔助方法 */// 判斷是否是元素節(jié)點(diǎn)isElementNode(node) {return node.nodeType === 1;}/* 核心方法 */// 將根節(jié)點(diǎn)轉(zhuǎn)移至文檔碎片node2fragment(el) {// 創(chuàng)建文檔碎片let fragment = document.createDocumentFragment();// 第一個(gè)子節(jié)點(diǎn)let firstChild;// 循環(huán)取出根節(jié)點(diǎn)中的節(jié)點(diǎn)并放入文檔碎片中while (firstChild = el.firstChild) {fragment.appendChild(firstChild);}return fragment;} }上面編譯模板的過(guò)程中,前提條件是必須存在根元素節(jié)點(diǎn),傳入的根元素節(jié)點(diǎn)允許是一個(gè)真實(shí)的 Dom 元素,也可以是一個(gè)選擇器,所以我們創(chuàng)建了輔助方法 isElementNode 來(lái)幫我們判斷傳入的元素是否是 Dom,如果是就直接使用,是選擇器就獲取這個(gè) Dom,最終將這個(gè)根節(jié)點(diǎn)存入 this.el 屬性中。
解析模板的過(guò)程中為了性能,我們應(yīng)取出根節(jié)點(diǎn)內(nèi)的子節(jié)點(diǎn)存放在文檔碎片中(內(nèi)存),需要注意的是將一個(gè) Dom 節(jié)點(diǎn)內(nèi)的子節(jié)點(diǎn)存入文檔碎片的過(guò)程中,會(huì)在原來(lái)的 Dom 容器中刪除這個(gè)節(jié)點(diǎn),所以在遍歷根節(jié)點(diǎn)的子節(jié)點(diǎn)時(shí),永遠(yuǎn)是將第一個(gè)節(jié)點(diǎn)取出存入文檔碎片,直到節(jié)點(diǎn)不存在為止。
2、編譯文檔碎片中的結(jié)構(gòu)
在 Vue 中的模板編譯的主要就是兩部分,也是瀏覽器無(wú)法解析的部分,元素節(jié)點(diǎn)中的指令和文本節(jié)點(diǎn)中的 Mustache 語(yǔ)法(雙大括號(hào))。
文件:Compile.js —— 完善
上面代碼新增內(nèi)容得主要邏輯就是做了兩件事:
- 調(diào)用 compile 方法對(duì) fragment 文檔碎片進(jìn)行編譯,即替換內(nèi)部指令和 Mustache 語(yǔ)法中變量對(duì)應(yīng)的值;
將編譯好的 fragment 文檔碎片塞回根節(jié)點(diǎn)。 - 在第一個(gè)步驟當(dāng)中邏輯是比較繁瑣的,首先在 compile 方法中獲取所有的子節(jié)點(diǎn),循環(huán)進(jìn)行編譯,如果是元素節(jié)點(diǎn)需要遞歸 compile,傳入當(dāng)前元素節(jié)點(diǎn)。在這個(gè)過(guò)程當(dāng)中抽取出了兩個(gè)方法,compileElement 和 compileText 用來(lái)對(duì)元素節(jié)點(diǎn)的屬性和文本節(jié)點(diǎn)進(jìn)行處理。
compileElement 中的核心邏輯就是處理指令,取出元素節(jié)點(diǎn)所有的屬性判斷是否是指令,是指令則調(diào)用指令對(duì)應(yīng)的方法。compileText 中的核心邏輯就是取出文本的內(nèi)容通過(guò)正則表達(dá)式匹配出被 Mustache 語(yǔ)法的 “{{ }}” 包裹的內(nèi)容,并調(diào)用處理文本的 text 方法。
文本節(jié)點(diǎn)的內(nèi)容有可能存在 “{{ }} {{ }} {{ }}”,正則匹配默認(rèn)是貪婪的,為了防止第一個(gè) “{” 和最后一個(gè) “}” 進(jìn)行匹配,所以在正則表達(dá)式中應(yīng)使用非貪婪匹配。
在調(diào)用指令的方法時(shí)都是調(diào)用的 CompileUtil 下對(duì)應(yīng)的方法,我們之所以單獨(dú)把這些指令對(duì)應(yīng)的方法抽離出來(lái)存儲(chǔ)在 CompileUtil 對(duì)象下的目的是為了解耦,因?yàn)楹竺嫫渌念愡€要使用。
3、CompileUtil 對(duì)象中指令方法的實(shí)現(xiàn)
CompileUtil 中存儲(chǔ)著所有的指令方法及指令對(duì)應(yīng)的更新方法,由于 Vue 的指令很多,我們這里只實(shí)現(xiàn)比較典型的 v-model 和 “{{ }}” 對(duì)應(yīng)的方法,考慮到后續(xù)更新的情況,我們統(tǒng)一把設(shè)置值到 Dom 中的邏輯抽取出對(duì)應(yīng)上面兩種情況的方法,存放到 CompileUtil 的 updater 對(duì)象中。
文件:CompileUtil.js
這部分的整個(gè)思路就是在 Compile 編譯模板后處理 v-model 和 “{{ }}” 時(shí),其實(shí)都是用 data 中的數(shù)據(jù)替換掉 fragment 文檔碎片中對(duì)應(yīng)的節(jié)點(diǎn)中的變量。因此會(huì)經(jīng)常性的獲取 data 中的值,在更新節(jié)點(diǎn)時(shí)又會(huì)重新設(shè)置 data 中的值,所以我們抽離出了三個(gè)方法 getVal、getTextVal 和 setVal 掛在了 CompileUtil 對(duì)象下。
獲取和設(shè)置 data 的值兩個(gè)方法 getVal 和 setVal 思路相似,由于獲取的變量層級(jí)不定,可能是 data.a,也可能是 data.obj.a.b,所以都是使用歸并的思路,借用 reduce 方法實(shí)現(xiàn)的,區(qū)別在于 setVal 方法在歸并過(guò)程中需要判斷是不是歸并到最后一級(jí),如果是則設(shè)置新值,而 getTextVal 就是在 getVal 外包了一層處理 “{{ }}” 的邏輯。
在這些準(zhǔn)備工作就緒以后就可以實(shí)現(xiàn)我們的主邏輯,即對(duì) Compile 類中解析的文本節(jié)點(diǎn)和元素節(jié)點(diǎn)指令中的變量用 data 值進(jìn)行替換,還記得前面說(shuō)針對(duì) v-model 和 “{{ }}” 進(jìn)行處理,因此設(shè)計(jì)了 model 和 text 兩個(gè)核心方法。
model和text兩個(gè)方法邏輯相似,都獲取了各自的 updater 中的方法,對(duì)值進(jìn)行設(shè)置,并且在設(shè)置的同時(shí)為了后續(xù) data 中的數(shù)據(jù)修改,視圖的更新,創(chuàng)建了 Watcher 的實(shí)例,并在內(nèi)部用新值重新更新節(jié)點(diǎn),不同的是 Vue 的 v-model 指令在表單中實(shí)現(xiàn)了雙向數(shù)據(jù)綁定,只要表單元素的 value 值發(fā)生變化,就需要將新值更新到 data 中,并響應(yīng)到頁(yè)面上。
所以我們的實(shí)現(xiàn)方式是給這個(gè)綁定了 v-model 的表單元素監(jiān)聽(tīng)了 input 事件,并在事件中實(shí)時(shí)的將新的 value 值更新到 data 中,至于 data 中的改變后響應(yīng)到頁(yè)面中需要另外三個(gè)類 Watcher、Observer 和 Dep 共同實(shí)現(xiàn),我們下面就來(lái)實(shí)現(xiàn) Watcher 類。
觀察者 Watcher 類的實(shí)現(xiàn)
在 CompileUtil 對(duì)象的方法中創(chuàng)建 Watcher 實(shí)例的時(shí)候傳入了三個(gè)參數(shù),即 MVVM 的實(shí)例、模板綁定數(shù)據(jù)的變量名 exp 和一個(gè) callback,這個(gè) callback 內(nèi)部邏輯是為了更新數(shù)據(jù)到 Dom,所以我們的 Watcher 類內(nèi)部要做的事情就清晰了,獲取更改前的值存儲(chǔ)起來(lái),并創(chuàng)建一個(gè) update 實(shí)例方法,在值被更改時(shí)去執(zhí)行實(shí)例的 callback 以達(dá)到視圖的更新。
文件:Watcher.js
看到上面代碼一定有兩個(gè)疑問(wèn):
- 使用 get 方法獲取舊值得時(shí)候?yàn)槭裁匆獙?dāng)前的實(shí)例掛在 Dep 上,在獲取值后為什么又清空了;
- update 方法內(nèi)部執(zhí)行了 callback 函數(shù),但是 update 在什么時(shí)候執(zhí)行。
這就是后面兩個(gè)類 Dep 和 observer 要做的事情,我們首先來(lái)介紹 Dep,再介紹 Observer 最后把他們之間的關(guān)系整個(gè)串聯(lián)起來(lái)。
發(fā)布訂閱 Dep 類的實(shí)現(xiàn)
其實(shí)發(fā)布訂閱說(shuō)白了就是把要執(zhí)行的函數(shù)統(tǒng)一存儲(chǔ)在一個(gè)數(shù)組中管理,當(dāng)達(dá)到某個(gè)執(zhí)行條件時(shí),循環(huán)這個(gè)數(shù)組并執(zhí)行每一個(gè)成員。
文件:Dep.js
在 Dep 類中只有一個(gè)屬性,就是一個(gè)名為 subs 的數(shù)組,用來(lái)管理每一個(gè) watcher,即 Watcher 類的實(shí)例,而 addSub 就是用來(lái)將 watcher 添加到 subs 數(shù)組中的,我們看到 notify 方法就解決了上面的一個(gè)疑問(wèn),Watcher 類的 update 方法是怎么執(zhí)行的,就是這樣循環(huán)執(zhí)行的。
接下來(lái)我們整合一下盲點(diǎn):
- Dep 實(shí)例在哪里創(chuàng)建聲明,又是在哪里將 watcher 添加進(jìn) subs 數(shù)組的;
- Dep 的 notify 方法應(yīng)該在哪里調(diào)用;
- Watcher 內(nèi)容中,使用 get 方法獲取舊值得時(shí)候?yàn)槭裁匆獙?dāng)前的實(shí)例掛在 Dep 上,在獲取值后為什么又清空了。
這些問(wèn)題在最后一個(gè)類 Observer 實(shí)現(xiàn)的時(shí)候都將清晰,下面我們重點(diǎn)來(lái)看最后一部分核心邏輯。
數(shù)據(jù)劫持 Observer 類的實(shí)現(xiàn)
還記得實(shí)現(xiàn) MVVM 類的時(shí)候就創(chuàng)建了這個(gè)類的實(shí)例,當(dāng)時(shí)傳入的參數(shù)是 MVVM 實(shí)例的 data 屬性,在 MVVM 中把數(shù)據(jù)通過(guò) Object.defineProperty 掛到了實(shí)例上,并添加了 getter 和 setter,其實(shí) Observer 類主要目的就是給 data 內(nèi)的所有層級(jí)的數(shù)據(jù)都進(jìn)行這樣的操作。
文件:Observer.js
在上面的代碼中,observe 的目的是遍歷對(duì)象,在內(nèi)部對(duì)數(shù)據(jù)進(jìn)行劫持,即添加 getter 和 setter,我們把劫持的邏輯單獨(dú)抽取成 defineReactive 方法,需要注意的是 observe 方法在執(zhí)行最初就對(duì)當(dāng)前的數(shù)據(jù)進(jìn)行了數(shù)據(jù)類型驗(yàn)證,然后再循環(huán)對(duì)象每一個(gè)屬性進(jìn)行劫持,目的是給同為 Object 類型的子屬性遞歸調(diào)用 observe 進(jìn)行深度劫持。
在 defineReactive 方法中,創(chuàng)建了 Dep 的實(shí)例,并對(duì) data 的數(shù)據(jù)使用 get 和 set 進(jìn)行劫持,還記得在模板編譯的過(guò)程中,遇到模板中綁定的變量,就會(huì)解析,并創(chuàng)建 watcher,會(huì)在 Watcher 類的內(nèi)部獲取舊值,即當(dāng)前的值,這樣就觸發(fā)了 get,在 get 中就可以將這個(gè) watcher 添加到 Dep 的 subs 數(shù)組中進(jìn)行統(tǒng)一管理,因?yàn)樵诖a中獲取 data 中的值操作比較多,會(huì)經(jīng)常觸發(fā) get,我們又要保證 watcher 不會(huì)被重復(fù)添加,所以在 Watcher 類中,獲取舊值并保存后,立即將 Dep.target 賦值為 null,并且在觸發(fā) get 時(shí)對(duì) Dep.target 進(jìn)行了短路操作,存在才調(diào)用 Dep 的 addSub 進(jìn)行添加。
而 data 中的值被更改時(shí),會(huì)觸發(fā) set,在 set 中做了性能優(yōu)化,即判斷重新賦的值與舊值是否相等,如果相等就不重新渲染頁(yè)面,不等的情況有兩種,如果原來(lái)這個(gè)被改變的值是基本數(shù)據(jù)類型沒(méi)什么影響,如果是引用類型,我們需要對(duì)這個(gè)引用類型內(nèi)部的數(shù)據(jù)進(jìn)行劫持,因此遞歸調(diào)用了 observe,最后調(diào)用 Dep 的 notify 方法進(jìn)行通知,執(zhí)行 notify 就會(huì)執(zhí)行 subs 中所有被管理的 watcher 的 update,就會(huì)執(zhí)行創(chuàng)建 watcher 時(shí)的傳入的 callback,就會(huì)更新頁(yè)面。
在 MVVM 類將 data 的屬性掛在 MVVM 實(shí)例上并劫持與通過(guò) Observer 類對(duì) data 的劫持還有一層聯(lián)系,因?yàn)檎麄€(gè)發(fā)布訂閱的邏輯都是在 data 的 get 和 set 上,只要觸發(fā)了 MVVM 中的 get 和 set 內(nèi)部會(huì)自動(dòng)返回或設(shè)置 data 對(duì)應(yīng)的值,就會(huì)觸發(fā) data 的 get 和 set,就會(huì)執(zhí)行發(fā)布訂閱的邏輯。
通過(guò)上面長(zhǎng)篇大論的敘述后,這個(gè) MVVM 模式用到的幾個(gè)類的關(guān)系應(yīng)該完全敘述清晰了,雖然比較抽象,但是細(xì)心琢磨還是會(huì)明白之間的關(guān)系和邏輯,下面我們就來(lái)對(duì)我們自己實(shí)現(xiàn)的這個(gè) MVVM 進(jìn)行驗(yàn)證。
驗(yàn)證 MVVM
按照 Vue 的方式根據(jù)自己的 MVVM 實(shí)現(xiàn)的內(nèi)容簡(jiǎn)單的寫(xiě)了一個(gè)模板如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>MVVM</title> </head> <body><div id="app"><!-- 雙向數(shù)據(jù)綁定 靠的是表單 --><input type="text" v-model="message"><div>{{message}}</div><ul><li>{{message}}</li></ul>{{message}}</div><!-- 引入依賴的 js 文件 --><script src="./js/Watcher.js"></script><script src="./js/Observer.js"></script><script src="./js/Compile.js"></script><script src="./js/CompileUtil.js"></script><script src="./js/Dep.js"></script><script src="./js/MVVM.js"></script><script>let vm = new MVVM({el: '#app',data: {message: 'hello world!'}});</script> </body> </html>打開(kāi) Chrom 瀏覽器的控制臺(tái),在上面通過(guò)下面操作來(lái)驗(yàn)證:
- 輸入 vm.message = “hello” 看頁(yè)面是否更新;
- 輸入 vm.$data.message = “hello” 看頁(yè)面是否更新;
- 改變文本輸入框內(nèi)的值,看頁(yè)面的其他元素是否更新。
通過(guò)上面的測(cè)試,相信應(yīng)該理解了 MVVM 模式對(duì)于前端開(kāi)發(fā)重大的意義,實(shí)現(xiàn)了雙向數(shù)據(jù)綁定,實(shí)時(shí)保證 View 層與 Model 層的數(shù)據(jù)同步,并可以讓我們?cè)陂_(kāi)發(fā)時(shí)基于數(shù)據(jù)編程,而最少的操作 Dom,這樣大大提高了頁(yè)面渲染的性能,也可以使我們把更多的精力用于業(yè)務(wù)邏輯的開(kāi)發(fā)上。
參考文章:https://www.jianshu.com/p/b87e6b689a3a感謝作者的分享!
源碼鏈接:https://gitee.com/monkeyhlj/vue-learning/tree/master/20210812-%E6%89%8B%E5%86%99%E4%B8%80%E4%B8%AAMVVM
虛擬dom
什么是dom?
- <div>xxx</div>
操作dom工具,jq,原生js
頻繁操作dom性能很差 – 數(shù)據(jù)量大時(shí)
改善:先記著dom更新,定時(shí)批量修改,通過(guò)算法判斷哪些需要修改 - 虛擬dom (js對(duì)象)
DIFF算法
- 將虛擬dom映射到真實(shí)dom
- 同級(jí)比較
- 有2對(duì)指針,舊首,舊尾,新首,新尾(見(jiàn)下下圖)
1、從左到右比較
將舊首的元素與新首的元素進(jìn)行比較,若相同則向右移動(dòng)一位
若新元素有多的則直接添加到舊元素上
2、從右到左比較
3、從舊首與新尾節(jié)點(diǎn)比較
4、從新首與舊尾節(jié)點(diǎn)比較
5、從舊首—舊尾區(qū)間查找有沒(méi)有相同的節(jié)點(diǎn),有則復(fù)用,無(wú)則創(chuàng)建
總結(jié)
- 上一篇: 前后端分离项目部署(服务器或本地)
- 下一篇: html5倒计时秒杀怎么做,vue 设