摘要:函數依次做了這幾件事調用函數,對比新舊兩個,根據兩者的不同得到需要修改的補丁將補丁到真實上當計數器小于等于的時候,將加,再繼續下一次當計數器大于的時候,結束下面我們來實現函數和函數。
上集回顧
【React進階系列】從零開始手把手教你實現一個Virtual DOM(二)
上集我們實現了首次渲染從JSX=>Hyperscript=>VDOM=>DOM的過程,今天我們來看一下當數據變動的時候怎么更新DOM,也就是下圖的右半邊部分。
function view(count) { const r = [...Array(count).keys()] return
我們的view函數接收一個參數count,變量r表示從0到count-1的一個數組。假如count=3, r=[0, 1, 2]。ul的className的值有三種可能:list-0, list-1, list-2。li的數量取決于count。
改寫render()function render(el) { const initialCount = 0 el.appendChild(createElement(view(initialCount))) setTimeout(() => tick(el, initialCount), 1000) } function tick(el, count) { const patches = diff(view(count + 1), view(count)) patch(el, patches) if(count > 5) { return } setTimeout(() => tick(el, count + 1), 1000) }
render函數有兩個修改,首先調用view()的時候傳入count=0。其次,寫了一個定時器,1秒后悔執行tick函數。tick函數接收兩個參數,el代表節點元素,count是當前計數值。
tick函數依次做了這幾件事:
調用diff函數,對比新舊兩個VDOM,根據兩者的不同得到需要修改的補丁
將補丁patch到真實DOM上
當計數器小于等于5的時候,將count加1,再繼續下一次tick
當計數器大于5的時候,結束
下面我們來實現diff函數和patch函數。
我們先列出來新舊兩個VDOM對比,會有哪些不同。在index.js文件的最前面聲明一下幾個常量。
const CREATE = "CREATE" //新增一個節點 const REMOVE = "REMOVE" //刪除原節點 const REPLACE = "REPLACE" //替換原節點 const UPDATE = "UPDATE" //檢查屬性或子節點是否有變化 const SET_PROP = "SET_PROP" //新增或替換屬性 const REMOVE_PROP = "REMOVE PROP" //刪除屬性diff()
function diff(newNode, oldNode) { if (!oldNode) { return { type: CREATE, newNode } } if (!newNode) { return { type: REMOVE } } if (changed(newNode, oldNode)) { return { type: REPLACE, newNode } } if (newNode.type) { return { type: UPDATE, props: diffProps(newNode, oldNode), children: diffChildren(newNode, oldNode) } } }
假如舊節點不存在,我們返回的patches對象, 類型為新增節點;
假如新節點不存在,表示是刪除節點;
假如兩者都存在的話,調用changed函數判斷他們是不是有變動;
假如兩者都存在,且changed()返回false的話,判斷新節點是否是VDOM(根據type是否存在來判斷的,因為type不存在的話,newNode要么是空節點,要么是字符串)。假如新節點是VDOM,則返回一個patches對象,類型是UPDATE,同時對props和children分別進行diffProps和diffChildren操作。
下面我們一次看一下changed, diffProps, diffChildren函數。
changed()function changed(node1, node2) { return typeof(node1) !== typeof(node2) || typeof(node1) === "string" && node1 !== node2 || node1.type !== node2.type }
檢查新舊VDOM是否有變動的方法很簡單,
首先假如數據類型都不一樣,那肯定是變動了;
其次假如兩者的類型都是純文本,則直接比較兩者是否相等;
最后比較兩者的類型是否相等。
diffProps()function diffProps(newNode, oldNode) { let patches = [] let props = Object.assign({}, newNode.props, oldNode.props) Object.keys(props).forEach(key => { const newVal = newNode.props[key] const oldVal = oldNode.props[key] if (!newVal) { patches.push({type: REMOVE_PROP, key, value: oldVal}) } if (!oldVal || newVal !== oldVal) { patches.push({ type: SET_PROP, key, value: newVal}) } }) return patches }
比較新舊VDOM的屬性的變化,并返回相應的patches。
首先我們采用最大可能性原則,將新舊VDOM的所有屬性都合并賦值給一個新的變量props
遍歷props變量的所有Keys,依次比較新舊VDOM對于這個KEY的值
假如新值不存在,表示這個屬性被刪除了
假如舊值不存在,或者新舊值不同,則表示我們需要重新設置這個屬性
diffChildren()function diffChildren(newNode, oldNode) { let patches = [] const maximumLength = Math.max( newNode.children.length, oldNode.children.length ) for(let i = 0; i < maximumLength; i++) { patches[i] = diff( newNode.children[i], oldNode.children[i] ) } return patches }
同樣采用最大可能性原則,取新舊VDOM的children的最長值作為遍歷children的長度。然后依次比較新舊VDOM的在相同INDEX下的每一個child。
這里需要強烈注意一下
為了簡化,我們沒有引入key的概念,直接比較的是相同index下的child。所以假如說一個列表ul有5項,分別是li1, li2, li3, li4, li5; 如果我們刪掉了第一項,新的變成了li2, li3, li4, li5。那么diffchildren的時候,我們會拿li1和li2比較,依次類推。這樣一來,本來只是刪除了li1, 而li2, li3, li4, li5沒有任何變化,我們得出的diff結論卻是[li替換,li2替換, li3替換, li4替換, li5刪除]。所以react讓大家渲染列表的時候,必須添加Key。
截止到現在,我們已經得到了我們需要的補丁。下面我們要將補丁Patch到DOM里。
patch()function patch(parent, patches, index = 0) { if (!patches) { return } const el = parent.childNodes[index] switch (patches.type) { case CREATE: { const { newNode } = patches const newEl = createElement(newNode) parent.appendChild(newEl) break } case REMOVE: { parent.removeChild(el) break } case REPLACE: { const {newNode} = patches const newEl = createElement(newNode) return parent.replaceChild(newEl, el) break } case UPDATE: { const {props, children} = patches patchProps(el, props) for(let i = 0; i < children.length; i++) { patch(el, children[i], i) } } } }
首先當patches不存在時,直接return,不進行任何操作
利用childNodes和Index取出當前正在處理的這個節點,賦值為el
開始判斷補丁的類型
當類型是CREATE時,生成一個新節點,并append到根節點
當類型是REMOVE時,直接刪除當前節點el
當類型是REPLACE時,生成新節點,同時替換掉原節點
當類型是UPDATE時,需要我們特殊處理
調用patchProps將我們之前diffProps得到的補丁渲染到節點上
遍歷之前diffChildren得到的補丁列表,再依次遞歸調用patch
最后我們再補充一下patchProps函數
patchPropsfunction patchProps(parent, patches) { patches.forEach(patch => { const { type, key, value } = patch if (type === "SET_PROP") { setProp(parent, key, value) } if (type === "REMOVE_PROP") { removeProp(parent, key, value) } }) } function removeProp(target, name, value) { //@ if (name === "className") { return target.removeAttribute("class") } target.removeAttribute(name) }
這個就不用我解釋了,代碼很直觀,setProp函數在上一集我們已經定義過了。這樣一來,我們就完成了整個數據更新導致DOM更新的完整過程。
npm run compile后打開瀏覽器查看效果,你應該看到是一個背景顏色在不同變化,同時列表項在逐漸增加的列表。
至此,我們的VDOM就全部完成了。系列初我提出的那幾個問題不知道你現在是否有了答案。有答案的童鞋可以在文章評論區將你的見解跟大家分享一下。分析全面且準確的會收到我的特殊獎勵。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/94686.html
摘要:可實際上并不是創造的,將這個概念拿過來以后融會貫通慢慢地成為目前前端最炙手可熱的框架之一。則是將再抽象一層生成的簡化版對象,這個對象也擁有上的一些屬性,比如等,但它是完全脫離于瀏覽器而存在的。所以今天我要手把手教大家怎么從零開始實現。 假如你的項目使用了React,你知道怎么做性能優化嗎?你知道為什么React讓你寫shouldComponentUpdate或者React.PureCo...
摘要:上集回顧從零開始手把手教你實現一個一上一集我們介紹了什么是,為什么要用,以及我們要怎樣來實現一個。完成后,在命令行中輸入安裝下依賴。最后返回這個目標節點。明天,我們迎接挑戰,開始處理數據變動引起的重新渲染,我們要如何新舊,生成補丁,修改。 上集回顧 從零開始手把手教你實現一個Virtual DOM(一)上一集我們介紹了什么是VDOM,為什么要用VDOM,以及我們要怎樣來實現一個VDOM...
摘要:但如果你想更加高效地使用來開發,成為大師,那下面我要傳授的這五招你一定得認真學習一下了。雖然損失了一丟丟性能,但避免了無限的。所以我們需要設置,這些默認行為將會被去掉以上兩點的優化才能成功。陸續可能還會更新一些別的招數,敬請期待。 本文面向對象是有一定Vue.js編程經驗的開發者。如果有人需要Vue.js入門系列的文章可以在評論區告訴我,有空就給你們寫。 對大部分人來說,掌握Vue.j...
摘要:模塊化是隨著前端技術的發展,前端代碼爆炸式增長后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調也不等同于異步。將會討論安全的類型檢測惰性載入函數凍結對象定時器等話題。 Vue.js 前后端同構方案之準備篇——代碼優化 目前 Vue.js 的火爆不亞于當初的 React,本人對寫代碼有潔癖,代碼也是藝術。此篇是準備篇,工欲善其事,必先利其器。我們先在代...
摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運,我不曉得。我只曉得,不認命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...
閱讀 2141·2021-11-18 10:07
閱讀 3517·2021-09-04 16:48
閱讀 3221·2019-08-30 15:53
閱讀 1245·2019-08-30 12:55
閱讀 2460·2019-08-29 15:08
閱讀 3161·2019-08-29 15:04
閱讀 2885·2019-08-29 14:21
閱讀 2915·2019-08-29 11:21