摘要:直到內(nèi)部的全部循環(huán)結(jié)束為止,才進(jìn)入下一個(gè)元素,當(dāng)循環(huán)結(jié)束時(shí),內(nèi)部的節(jié)點(diǎn)都已經(jīng)生成好了。
自己實(shí)現(xiàn)虛擬 DOM 從 HTML 中提煉數(shù)據(jù)結(jié)構(gòu)
先來看下我們的 HTML
傅雷家書
讀家書,想付雷
從 HTML 中我們可以抽離出它的數(shù)據(jù)結(jié)構(gòu):
首先頁(yè)面中只需要一個(gè)根節(jié)點(diǎn)root,定義為:nodesDate數(shù)組
root內(nèi)有兩個(gè)子元素h1和span,數(shù)組有兩項(xiàng),每項(xiàng)為內(nèi)容為tag和children
接下來內(nèi)部所有元素都是如此定義,直到遇到文本元素,將他定義為text
nodesDate = { tag:"div", children:[{ tag:"h1", children:[{ tag:"span", children:[{ tag:"#text", text:"傅雷家書" }] }] },{ tag:"span", children:[{ tag:"#text", text:"讀家書,想傅雷" }] }] }
用這種視野在看 HTML 的話,就不是單純的 HTML 了,而是一堆hash
從上面數(shù)據(jù)結(jié)構(gòu)中我們可以提煉出3個(gè)有用的屬性,分別是tag、children、text,那我們是不是可以定義一個(gè)方法,傳遞這三個(gè)參數(shù),就能滿足我們的需求呢?
構(gòu)造 HTML 方法想一下我們拿到這三個(gè)參數(shù)后,要干什么呢?
當(dāng)然是在頁(yè)面中生成 DOM 元素?。?/p>
對(duì),這三個(gè)參數(shù)是我們各自私有屬性,通過這三個(gè)屬性能生成各自的 DOM,生成 DOM 的方法是不是公用的呢?
所以可以用用構(gòu)造函數(shù)模式創(chuàng)建我們要的方法,(PS:之前講過new操作符的背后的邏輯,不理解的可移步:使用 new 操作符內(nèi)部到底在做什么)
function vNode(tag,children,text){ this.tag = tag this.children = children this.text = text } vNode.prototype.render = function(){ //如 tag 為文本的話,創(chuàng)建一個(gè)文本節(jié)點(diǎn) if(this.tag === "#text"){ return document.createTextNode(this.text) // 返回文本 } //tag 不是文本的話,創(chuàng)建一個(gè) DOM 節(jié)點(diǎn),并且遍歷 children,每次遍歷都是調(diào)用自身的 render 方法 let element = document.createElement(this.tag) this.children.forEach((vChild)=> { element.appendChild(vChild.render()) //在遍歷 h1 時(shí),沒有直接跳出,而是在其內(nèi)部不斷循環(huán)。直到 h1 內(nèi)部的 children 全部循環(huán)結(jié)束為止,才進(jìn)入下一個(gè)元素 span,當(dāng) h1 循環(huán)結(jié)束時(shí),h1 內(nèi)部的節(jié)點(diǎn)都已經(jīng)生成好了。 }) return element //返回節(jié)點(diǎn) } function v(tag,children,text){ //如果 chilren 為字符串,那么就把 children 賦值給 text,并把 children 初始化為 [],不然后面會(huì)報(bào)錯(cuò) if(typeof children === "string"){ text = children children = [] } return new vNode(tag,children,text) } //格式參見 nodesData,vNode 的實(shí)例化 let vNodes = v("div",[ v("h1",[ v("span",[ v("#text","傅雷家書")]) ]), v("span",[ v("#text","——傅敏")]) ]) const root = document.querySelector(".root") //獲取 root 節(jié)點(diǎn) root.appendChild(vNodes.render()) //這里只運(yùn)行一次,把最終的 DOM 添加進(jìn)頁(yè)面中實(shí)現(xiàn)增刪改
如果此時(shí)一個(gè)數(shù)據(jù)變動(dòng)比如,按照以前的邏輯
root.innerText = "" root.appendChild(vNodes.render())
如果數(shù)據(jù)非常大,用這種方法根本沒啥意義,每一次改動(dòng) DOM 樹都要重新渲染一遍,造成性能低下,有什么好的方法可以實(shí)現(xiàn)呢?
function patchElement(parent, newVNodes, oldVNodes, index = 0) { //如果沒有傳遞老的 VNodes,默認(rèn)就是新的 if(!oldVNodes) { parent.appendChild(newVNodes.render()) } else if(!newVNodes) { parent.removeChild(parent.childNodes[index]) } else if(newVNodes.tag !== oldVNodes.tag || newVNodes.text !== oldVNodse.text) { //如果元素不一樣或者文本不一樣,走這邊 //當(dāng)有走這邊時(shí),newVNodes 是和 oldVNodes 不同的那個(gè)值,這里的 parent 是當(dāng)前元素或文本的 parent //replaceChild(sp1,sp2),是將 sp2 換成 sp1 parent.replaceChild(newVNodes.render(), parent.childNodes[index]) } else { for(let i = 0; i < newVNodes.children.length || i < oldVNodes.children.length; i++) { //取值永遠(yuǎn)是 newVNodes.length,除非不傳 newVNodes //這里 index 只有當(dāng) i 變化時(shí),下一次才是 index 才等于 i 的值 //當(dāng) i = 0 時(shí),這次的 parent.childNode[index],是下一次的 parent,所以這里要用 index //當(dāng)循環(huán)走完,發(fā)現(xiàn)元素或者文本不一樣時(shí),才走第三個(gè)邏輯 patchElement(parent.childNodes[index], newVNodes.children[i], oldVNodes.children[i], i) } } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/96998.html
摘要:什么是虛擬舉例說明如果網(wǎng)頁(yè)中有一個(gè)表格,表頭是姓名,年級(jí),分?jǐn)?shù)。即我們用虛擬的結(jié)構(gòu)替換需要處理的結(jié)構(gòu),對(duì)虛擬的進(jìn)行操作之后再進(jìn)行渲染,就成為了真實(shí)的數(shù)據(jù)。當(dāng)狀態(tài)變更的時(shí)候用修改后的新渲染的的對(duì)象和舊的虛擬對(duì)象作對(duì)比,記錄著兩棵樹的差異。 虛擬DOM 可以看看這個(gè)文章如何理解虛擬DOM? - 戴嘉華的回答 - 知乎 https://www.zhihu.com/questio... 深度剖...
摘要:什么是虛擬舉例說明如果網(wǎng)頁(yè)中有一個(gè)表格,表頭是姓名,年級(jí),分?jǐn)?shù)。即我們用虛擬的結(jié)構(gòu)替換需要處理的結(jié)構(gòu),對(duì)虛擬的進(jìn)行操作之后再進(jìn)行渲染,就成為了真實(shí)的數(shù)據(jù)。當(dāng)狀態(tài)變更的時(shí)候用修改后的新渲染的的對(duì)象和舊的虛擬對(duì)象作對(duì)比,記錄著兩棵樹的差異。 虛擬DOM 可以看看這個(gè)文章如何理解虛擬DOM? - 戴嘉華的回答 - 知乎 https://www.zhihu.com/questio... 深度剖...
摘要:前言是前端最受歡迎的框架之一,解讀其源碼的文章非常多,但是我想從另一個(gè)角度去解讀從零開始實(shí)現(xiàn)一個(gè),從層面實(shí)現(xiàn)的大部分功能,在這個(gè)過程中去探索為什么有虛擬為什么這樣設(shè)計(jì)等問題。 前言 React是前端最受歡迎的框架之一,解讀其源碼的文章非常多,但是我想從另一個(gè)角度去解讀React:從零開始實(shí)現(xiàn)一個(gè)React,從API層面實(shí)現(xiàn)React的大部分功能,在這個(gè)過程中去探索為什么有虛擬DOM、d...
摘要:要構(gòu)建自己的虛擬,需要知道兩件事?,F(xiàn)在來看看如何處理上面描述的所有情況。代碼如下節(jié)點(diǎn)的替換首先,需要編寫一個(gè)函數(shù)來比較兩個(gè)節(jié)點(diǎn)舊節(jié)點(diǎn)和新節(jié)點(diǎn),并告訴節(jié)點(diǎn)是否真的發(fā)生了變化??偨Y(jié)現(xiàn)在我們已經(jīng)編寫了虛擬實(shí)現(xiàn)及了解它的工作原理。 showImg(https://segmentfault.com/img/bVbmPue?w=2000&h=684); 要構(gòu)建自己的虛擬DOM,需要知道兩件事。你甚...
閱讀 3389·2021-11-24 09:39
閱讀 3165·2021-11-23 09:51
閱讀 971·2021-11-18 10:07
閱讀 3610·2021-10-11 10:57
閱讀 2846·2021-10-08 10:04
閱讀 3080·2021-09-26 10:11
閱讀 1160·2021-09-23 11:21
閱讀 2984·2019-08-29 17:28