国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

前端-Virtual Dom

這篇具有很好參考價值的文章主要介紹了前端-Virtual Dom。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

為什么需要 Virtual Dom
眾所周知,操作 DOM 是很耗費性能的?件事情,既然如此,我們可以考慮通過 JS 對象來模擬 DOM 對象,畢竟操作 JS 對象?操作
DOM 省時的多
// 假設這?模擬?個 ul,其中包含了 5 個 li
[1, 2, 3, 4, 5]
// 這?替換上?的 li
[1, 2, 5, 4]
 從上述例?中,我們?眼就可以看出先前的 ul 中的第三個 li 被移除了,四五替換了位置。
  • 如果以上操作對應到 DOM 中,那么就是以下代碼

// 刪除第三個 li
ul.childNodes[2].remove()
// 將第四個 li 和第五個交換位置
let fromNode = ul.childNodes[4]
let toNode = node.childNodes[3]
let cloneFromNode = fromNode.cloneNode(true)
let cloenToNode = toNode.cloneNode(true)
ul.replaceChild(cloneFromNode, toNode)
ul.replaceChild(cloenToNode, fromNode)
  • 當然在實際操作中,我們還需要給每個節(jié)點?個標識,作為判斷是同?個節(jié)點的依據(jù)。所以這也是 Vue 和 React 中官?推薦列表?的節(jié)點使?唯?的key 來保證性能。
  • 那么既然 DOM 對象可以通過 JS 對象來模擬,反之也可以通過 JS 對象來渲染出對應的 DOM
  • 以下是?個 JS 對象模擬 DOM 對象的簡單實現(xiàn)

export default class Element {
/**
* @param {String} tag 'div'
* @param {Object} props { class: 'item' }
* @param {Array} children [ Element1, 'text']
* @param {String} key option
*/
constructor(tag, props, children, key) {
this.tag = tag
this.props = props
if (Array.isArray(children)) {

this.children = children
} else if (isString(children)) {
this.key = children
this.children = null
}
if (key) this.key = key
}
// 渲染
render() {
let root = this._createElement(
this.tag,
this.props,
this.children,
this.key
)
document.body.appendChild(root)
return root
}
create() {
return this._createElement(this.tag, this.props, this.children, this.ke
}
// 創(chuàng)建節(jié)點
_createElement(tag, props, child, key) {
// 通過 tag 創(chuàng)建節(jié)點
let el = document.createElement(tag)
// 設置節(jié)點屬性
for (const key in props) {
if (props.hasOwnProperty(key)) {
const value = props[key]
el.setAttribute(key, value)
}
}
if (key) {
el.setAttribute('key', key)
	}
// 遞歸添加?節(jié)點
if (child) {
		child.forEach(element => {
			let child
			if (element instanceof Element) {
			child = this._createElement(
					element.tag,
					element.props,
					element.children,
					element.key
				)
			} else {
				child = document.createTextNode(element)
			}
			el.appendChild(child)
		})
  }
	return el
	}
}
Virtual Dom 算法簡述
  • 既然我們已經(jīng)通過 JS 來模擬實現(xiàn)了 DOM ,那么接下來的難點就在于如何判斷舊的對象和新的對象之間的差異。

  • DOM 是多叉樹的結(jié)構(gòu),如果需要完整的對?兩顆樹的差異,那么需要的時間復雜度會是O(n ^ 3) ,這個復雜度肯定是不能接受的。于是 React 團隊優(yōu)化了算法,實現(xiàn)了O(n) 的復雜度來對?差異。

  • 實現(xiàn) O(n) 復雜度的關鍵就是只對?同層的節(jié)點,?不是跨層對?,這也是考慮到在實際業(yè)務中很少會去跨層的移動 DOM 元素

    所以判斷差異的算法就分為了兩步
    
  • ?先從上?下,從左往右遍歷對象,也就是樹的深度遍歷,這?步中會給每個節(jié)點添加索引,便于最后渲染差異

  • ?旦節(jié)點有?元素,就去判斷?元素是否有不同文章來源地址http://www.zghlxwxcb.cn/news/detail-629529.html

Virtual Dom 算法實現(xiàn)
樹的遞歸
  • ?先我們來實現(xiàn)樹的遞歸算法,在實現(xiàn)該算法前,先來考慮下兩個節(jié)點對?會有?種情況
  • 新的節(jié)點的 tagName 或者 key 和舊的不同,這種情況代表需要替換舊的節(jié)點,并且也不再需要遍歷新舊節(jié)點的?元素了,因為整個舊節(jié)點都被刪掉了
  • 新的節(jié)點的 tagName 和 key (可能都沒有)和舊的相同,開始遍歷?樹
  • 沒有新的節(jié)點,那么什么都不?做
import { StateEnums, isString, move } from './util'
import Element from './element'
export default function diff(oldDomTree, newDomTree) {
// ?于記錄差異
let pathchs = {}
// ?開始的索引為 0
dfs(oldDomTree, newDomTree, 0, pathchs)
return pathchs
}
function dfs(oldNode, newNode, index, patches) {
// ?于保存?樹的更改
let curPatches = []
// 需要判斷三種情況
// 1.沒有新的節(jié)點,那么什么都不?做
// 2.新的節(jié)點的 tagName 和 `key` 和舊的不同,就替換
// 3.新的節(jié)點的 tagName 和 key(可能都沒有) 和舊的相同,開始遍歷?樹
if (!newNode) {
} else if (newNode.tag === oldNode.tag && newNode.key === oldNode.key) {
// 判斷屬性是否變更
let props = diffProps(oldNode.props, newNode.props)
if (props.length) curPatches.push({ type: StateEnums.ChangeProps, props
// 遍歷?樹
diffChildren(oldNode.children, newNode.children, index, patches)
} else {
// 節(jié)點不同,需要替換
curPatches.push({ type: StateEnums.Replace, node: newNode })
}
if (curPatches.length) {
if (patches[index]) {
patches[index] = patches[index].concat(curPatches)
} else {
patches[index] = curPatches
}
}
}
判斷屬性的更改
判斷屬性的更改也分三個步驟
  • 遍歷舊的屬性列表,查看每個屬性是否還存在于新的屬性列表中
  • 遍歷新的屬性列表,判斷兩個列表中都存在的屬性的值是否有變化
  • 在第?步中同時查看是否有屬性不存在與舊的屬性列列表中
function diffProps(oldProps, newProps) {
// 判斷 Props 分以下三步驟
// 先遍歷 oldProps 查看是否存在刪除的屬性
// 然后遍歷 newProps 查看是否有屬性值被修改
// 最后查看是否有屬性新增
let change = []
for (const key in oldProps) {
if (oldProps.hasOwnProperty(key) && !newProps[key]) {
change.push({
prop: key
})
}
}
for (const key in newProps) {
if (newProps.hasOwnProperty(key)) {
const prop = newProps[key]
if (oldProps[key] && oldProps[key] !== newProps[key]) {
change.push({
prop: key,
value: newProps[key]
})
} else if (!oldProps[key]) {
change.push({
prop: key,
value: newProps[key]
})
}
}
}
return change
}
判斷列表差異算法實現(xiàn)
  • 這個算法是整個 Virtual Dom 中最核?的算法,且讓我??為你道來。 這?的主要步驟其實和判斷屬性差異是類似的,也是分為三步
  • 遍歷舊的節(jié)點列表,查看每個節(jié)點是否還存在于新的節(jié)點列表中
  • 遍歷新的節(jié)點列表,判斷是否有新的節(jié)點
  • 在第?步中同時判斷節(jié)點是否有移動
  • PS:該算法只對有 key 的節(jié)點做處理
function listDiff(oldList, newList, index, patches) {
// 為了遍歷?便,先取出兩個 list 的所有 keys
let oldKeys = getKeys(oldList)
let newKeys = getKeys(newList)
let changes = []
// ?于保存變更后的節(jié)點數(shù)據(jù)
// 使?該數(shù)組保存有以下好處
// 1.可以正確獲得被刪除節(jié)點索引
// 2.交換節(jié)點位置只需要操作?遍 DOM
// 3.?于 `diffChildren` 函數(shù)中的判斷,只需要遍歷
// 兩個樹中都存在的節(jié)點,?對于新增或者刪除的節(jié)點來說,完全沒必要
// 再去判斷?遍
let list = []
oldList &&
oldList.forEach(item => {
let key = item.key
if (isString(item)) {
key = item
}
// 尋找新的 children 中是否含有當前節(jié)點
// 沒有的話需要刪除
let index = newKeys.indexOf(key)
if (index === -1) {
list.push(null)
} else list.push(key)
})
// 遍歷變更后的數(shù)組
let length = list.length
// 因為刪除數(shù)組元素是會更改索引的
// 所有從后往前刪可以保證索引不變
for (let i = length - 1; i >= 0; i--) {
// 判斷當前元素是否為空,為空表示需要刪除
if (!list[i]) {
list.splice(i, 1)
changes.push({
type: StateEnums.Remove,
index: i
})
}
}
// 遍歷新的 list,判斷是否有節(jié)點新增或移動
// 同時也對 `list` 做節(jié)點新增和移動節(jié)點的操作
newList &&
newList.forEach((item, i) => {
let key = item.key


if (isString(item)) {
key = item
}
// 尋找舊的 children 中是否含有當前節(jié)點
let index = list.indexOf(key)
// 沒找到代表新節(jié)點,需要插?
if (index === -1 || key == null) {
changes.push({
type: StateEnums.Insert,
node: item,
index: i
})
list.splice(i, 0, key)
} else {
// 找到了,需要判斷是否需要移動
if (index !== i) {
changes.push({
type: StateEnums.Move,
from: index,
to: i
})
move(list, index, i)
}
}
})
return { changes, list }
}
function getKeys(list) {
let keys = []
let text
list &&
list.forEach(item => {
let key
if (isString(item)) {
key = [item]
} else if (item instanceof Element) {
key = item.key
}
keys.push(key)
})
return keys
}
遍歷?元素打標識
  • 對于這個函數(shù)來說,主要功能就兩個
  • 判斷兩個列表差異
    • 給節(jié)點打上標記
    • 總體來說,該函數(shù)實現(xiàn)的功能很簡單
function diffChildren(oldChild, newChild, index, patches) {
let { changes, list } = listDiff(oldChild, newChild, index, patches)
if (changes.length) {
if (patches[index]) {
patches[index] = patches[index].concat(changes)
} else {
patches[index] = changes
}
}
// 記錄上?個遍歷過的節(jié)點
let last = null
oldChild &&
oldChild.forEach((item, i) => {
let child = item && item.children
if (child) {
index =
last && last.children ? index + last.children.length + 1 : index
let keyIndex = list.indexOf(item.key)
let node = newChild[keyIndex]
// 只遍歷新舊中都存在的節(jié)點,其他新增或者刪除的沒必要遍歷
if (node) {
dfs(item, node, index, patches)
}
} else index += 1
last = item
})
}
渲染差異
通過之前的算法,我們已經(jīng)可以得出兩個樹的差異了。既然知道了差異,就需要局部去更新 DOM 了,下?就讓我們來看看 Virtual 
Dom 算法的最后?步驟
這個函數(shù)主要兩個功能
  • 深度遍歷樹,將需要做變更操作的取出來
  • 局部更新 DOM
let index = 0
export default function patch(node, patchs) {
let changes = patchs[index]
let childNodes = node && node.childNodes
// 這?的深度遍歷和 diff 中是?樣的
if (!childNodes) index += 1
if (changes && changes.length && patchs[index]) {
changeDom(node, changes)
}
let last = null
if (childNodes && childNodes.length) {
childNodes.forEach((item, i) => {
index =
last && last.children ? index + last.children.length + 1 : index +
patch(item, patchs)
last = item
})
}
}
function changeDom(node, changes, noChild) {
changes &&
changes.forEach(change => {
let { type } = change
switch (type) {
case StateEnums.ChangeProps:
let { props } = change
props.forEach(item => {
if (item.value) {
node.setAttribute(item.prop, item.value)
} else {
node.removeAttribute(item.prop)
}
})
break
case StateEnums.Remove:
node.childNodes[change.index].remove()
break
case StateEnums.Insert:
let dom
if (isString(change.node)) {
dom = document.createTextNode(change.node)
} else if (change.node instanceof Element) {
dom = change.node.create()
}
node.insertBefore(dom, node.childNodes[change.index])
break
case StateEnums.Replace:
node.parentNode.replaceChild(change.node.create(), node)
break
case StateEnums.Move:
let fromNode = node.childNodes[change.from]
let toNode = node.childNodes[change.to]
let cloneFromNode = fromNode.cloneNode(true)
let cloenToNode = toNode.cloneNode(true)
node.replaceChild(cloneFromNode, toNode)
node.replaceChild(cloenToNode, fromNode)
break
default:
break
}
})
}

Virtual Dom 算法的實現(xiàn)也就是以下三步
  • 通過 JS 來模擬創(chuàng)建 DOM 對象
  • 判斷兩個對象的差異
  • 渲染差異
let test4 = new Element('div', { class: 'my-div' }, ['test4'])
let test5 = new Element('ul', { class: 'my-div' }, ['test5'])
let test1 = new Element('div', { class: 'my-div' }, [test4])
let test2 = new Element('div', { id: '11' }, [test5, test4])
let root = test1.render()
let pathchs = diff(test1, test2)
console.log(pathchs)
setTimeout(() => {
console.log('開始更新')
patch(root, pathchs)
console.log('結(jié)束更新')
}, 1000)

到了這里,關于前端-Virtual Dom的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 〖大前端 - 基礎入門三大核心之JS篇?〗- JavaScript 的DOM簡介

    〖大前端 - 基礎入門三大核心之JS篇?〗- JavaScript 的DOM簡介

    說明:該文屬于 大前端全棧架構(gòu)白寶書專欄, 目前階段免費 , 如需要項目實戰(zhàn)或者是體系化資源,文末名片加V! 作者:不渴望力量的哈士奇(哈哥),十余年工作經(jīng)驗, 從事過全棧研發(fā)、產(chǎn)品經(jīng)理等工作,目前在公司擔任研發(fā)部門CTO。 榮譽: 2022年度博客之星Top4、2023年度超

    2024年02月04日
    瀏覽(25)
  • 前端學習記錄~2023.8.3~JavaScript重難點實例精講~第5章 DOM與事件

    前端學習記錄~2023.8.3~JavaScript重難點實例精講~第5章 DOM與事件

    本章是第五章DOM與事件相關的內(nèi)容。 DOM是文檔對象模型,全稱為Document Object Model。DOM用一個邏輯樹來表示一個文檔,樹的每個分支終點都是一個節(jié)點,每個節(jié)點都包含著對象。DOM提供了對文檔結(jié)構(gòu)化的表述,通過綁定不同的事件可以改變文檔的結(jié)構(gòu)、樣式和內(nèi)容,從而能實現(xiàn)

    2024年02月12日
    瀏覽(25)
  • React原理 - React Virtual DOM 原理

    React原理 - React Virtual DOM 原理

    目錄 擴展學習資料 ?Virtual DOM 是什么【虛擬dom】 React渲染 Virtual DOM VS 原生DOM【vDom是否比原生Dom更高效】 Virtual DOM數(shù)據(jù)結(jié)構(gòu) Virtaual DOM Diff【虛擬dom前后比對,更新不同dom的算法】 源碼解讀 react源碼組織方式: React Stack Reconciler【react 棧 協(xié)調(diào)】 react v15.6.2 源碼 練習 名稱 鏈接

    2024年02月11日
    瀏覽(22)
  • 大型醫(yī)院云HIS系統(tǒng):采用前后端分離架構(gòu),前端由Angular語言、JavaScript開發(fā);后端使用Java語言開發(fā) 融合B/S版電子病歷系統(tǒng)

    大型醫(yī)院云HIS系統(tǒng):采用前后端分離架構(gòu),前端由Angular語言、JavaScript開發(fā);后端使用Java語言開發(fā) 融合B/S版電子病歷系統(tǒng)

    一套醫(yī)院云his系統(tǒng)源碼 采用前后端分離架構(gòu),前端由Angular語言、JavaScript開發(fā);后端使用Java語言開發(fā)。融合B/S版電子病歷系統(tǒng),支持電子病歷四級,HIS與電子病歷系統(tǒng)均擁有自主知識產(chǎn)權(quán)。 文末卡片獲取聯(lián)系! 基于云計算技術(shù)的B/S架構(gòu)的醫(yī)院管理系統(tǒng)(簡稱云HIS),采用前后

    2024年02月03日
    瀏覽(31)
  • 前端框架技術(shù)革新歷程:從原生DOM操作、數(shù)據(jù)雙向綁定到虛擬DOM等框架原理深度解析,Web開發(fā)與用戶體驗的共贏

    前端框架技術(shù)革新歷程:從原生DOM操作、數(shù)據(jù)雙向綁定到虛擬DOM等框架原理深度解析,Web開發(fā)與用戶體驗的共贏

    前端的發(fā)展與前端框架的發(fā)展相輔相成,形成了相互驅(qū)動、共同演進的關系。前端技術(shù)的進步不僅催生了前端框架的產(chǎn)生,也為其發(fā)展提供了源源不斷的動力。 前端,即Web前端,是指在創(chuàng)建Web應用程序或網(wǎng)站過程中負責用戶界面(User Interface, UI)構(gòu)建與交互的部分,是用戶與

    2024年04月26日
    瀏覽(59)
  • Vue3前端開發(fā),如何獲取組件內(nèi)dom對象以及子組件的屬性和方法

    Vue3前端開發(fā),如何獲取組件內(nèi)dom對象以及子組件的屬性和方法

    Vue3前端開發(fā),借助Ref來獲取組件內(nèi)dom對象,借助defineExpose編譯宏可以獲取到子組件的屬性和方法。 app入口文件,我們作為父組件,在里面調(diào)用了自定義組件TestCom.vue。 先做了一個測試,借助于ref來訪問自身的dom對象。如圖所示是可以拿到的。 ref又稱謂鉤子函數(shù),在vue2版本中

    2024年01月22日
    瀏覽(40)
  • 一文讀懂JavaScript DOM節(jié)點操作(JavaScript DOM節(jié)點操作詳解)

    一文讀懂JavaScript DOM節(jié)點操作(JavaScript DOM節(jié)點操作詳解)

    一、什么是節(jié)點 DOM模型是樹狀結(jié)構(gòu)模型,二組成這棵樹的就是一個個點,在網(wǎng)絡術(shù)語中稱之為節(jié)點。 節(jié)點是一個模型中最基本的組成單位。DOM模型是由一個個節(jié)點組成的,DOM節(jié)點也有其不同的類型。 二、節(jié)點類型 DOM節(jié)點分為5種類型: 文檔節(jié)點(就是整個HTML文檔,也就是

    2024年01月22日
    瀏覽(31)
  • 快速認識,前端必學編程語言:JavaScript

    快速認識,前端必學編程語言:JavaScript

    JavaScript是構(gòu)建Web應用必學的一門編程語言,也是最受開發(fā)者歡迎的熱門語言之一。所以,如果您還不知道JavaScript的用處、特點的話,趕緊補充一下這塊基礎知識。 JavaScript 是一種高級、單線程、垃圾收集、解釋或即時編譯、基于原型、多范式、動態(tài)語言,具有非阻塞事件循

    2024年02月05日
    瀏覽(26)
  • 前端開發(fā)——Javascript知識(介紹)

    目錄 有關JavaScript的知識? JavaScript的優(yōu)點? ?JavaScript的領域 JavaScript的組成 JavaScript的特點 第一個JavaScript程序 在 HTML 文檔中嵌入 JavaScript 代碼 在腳本文件中編寫 JavaScript 代碼 JavaScript內(nèi)容? Html內(nèi)容? JavaScript 代碼執(zhí)行順序 JavaScript中的幾個重要概念 標識符 保留字 區(qū)分

    2024年02月01日
    瀏覽(25)
  • 前端開發(fā)——JavaScript的條件語句

    ? 世界不僅有黑,又或者白 世界而是一道精致的灰 ?——Lungcen ? ? 目錄 條件判斷語句 if 語句 if else 語句 if else if else 語句 ?switch語句 break case 子句 default語句 while循環(huán)語句 do while循環(huán)語句 for循環(huán)語句 for 循環(huán)中的三個表達式 for 循環(huán)嵌套 for 循環(huán)變體——for in for 循環(huán)

    2023年04月21日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包