淺談從React渲染流程分析Diff算法
React中最神奇的部分莫過于虛擬DOM,以及其高效的Diff算法。這讓我們可以無需擔(dān)心性能問題而”毫無顧忌”的隨時“刷新”整個頁面,由虛擬DOM來確保只對界面上真正變化的部分進(jìn)行實際的DOM操作。React在這一部分已經(jīng)做到足夠透明,在實際開發(fā)中我們基本無需關(guān)心虛擬DOM是如何運作的。然而,理解其運行機制不僅有助于更好的理解React組件的生命周期,而且對于進(jìn)一步優(yōu)化React程序也會有很大幫助。
1、什么是虛擬DOM
在React中,render執(zhí)行的結(jié)果得到的并不是真正的DOM節(jié)點,結(jié)果僅僅是輕量級的JavaScript對象,我們稱之為virtual DOM。
簡單的說,其實所謂的virtual DOM就是JavaScript對象到Html DOM節(jié)點的映射;即使用JavaScript對象將Html結(jié)構(gòu)表示出來,而這個對象就是virtual DOM。
eg:
Html:
<ul id='list'> <li class='item'>Item 1</li> <li class='item'>Item 2</li> </ul>
JavaScript對象表示(virtual DOM)
{
tagName: 'ul',
props: {
id: 'list'
},
children: [
{tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
{tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
]
}
2、什么時候會生成到virtual DOM
React生命周期擁有裝載、更新、卸載的三個階段;附上一張React生命周期圖

前面提到:render執(zhí)行的結(jié)果得到的并不是真正的DOM節(jié)點,結(jié)果僅僅是輕量級的JavaScript對象,即在render函數(shù)調(diào)用時將會創(chuàng)建出虛擬DOM;
class Tab extends React.Component {
render() {
React.createElement(
'p',
{ className: 'class'},
'Hello React'
)
}
}

通過React.createElemen創(chuàng)建出虛擬DOM,而該函數(shù)只在Render函數(shù)中調(diào)用,所以在React裝載和更新的過程中才會有虛擬DOM的生成;至于掛載到真實DOM自然而然是ReactDom.render函數(shù)啦。
3、virtual DOM如何實現(xiàn)
實現(xiàn)其實很簡單,主要是定義一個函數(shù)并把我們傳進(jìn)去的參數(shù)組成一個React元素對象,而type就是我們傳進(jìn)去的組件類型,可以是一個類、函數(shù)或字符串(如'div')
React大致源碼:
function createElement(type, config, children) {
let propName;
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
if (hasValidRef(config)) {
// 如果有ref,將它取出來
ref = config.ref;
}
if (hasValidKey(config)) {
// 如果有key,將它取出來
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
// 將除ref,key等這些特殊的屬性放到新的props對象里
props[propName] = config[propName];
}
}
}
// 獲取子元素
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// 添加默認(rèn)props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
const ReactElement = function(type, key, ref, self, source, owner, props) {
// 最終得到的React元素
const element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner,
};
return element;
};
打印出組件:

4、為什么需要使用virtual DOM
DOM管理歷史階段:
- JS 或者 jQuery 操作 DOM: 當(dāng)應(yīng)用程序越來越復(fù)雜,需要在JS里面維護(hù)的字段也越來越多,需要監(jiān)聽事件和在事件回調(diào)用更新頁面的DOM操作也越來越多,應(yīng)用程序會變得非常難維護(hù)。
- 后來產(chǎn)出 MVC、MVP 的架構(gòu)模式,期望從代碼組織方式來降低維護(hù)難度。但是 MVC 架構(gòu)并沒辦法減少維護(hù)的狀態(tài),也沒有降低狀態(tài)更新時需要對頁面的更新操作,你需要操作的DOM還是需要操作,只是換了個地方。
- 既然狀態(tài)改變了要操作相應(yīng)的DOM元素,為什么不做一個東西讓視圖和狀態(tài)進(jìn)行綁定,狀態(tài)變更了視圖自動變更。這就是后來人們想出了 MVVM 模式,只要在模版中聲明視圖組件是和什么狀態(tài)進(jìn)行綁定的,雙向綁定引擎就會在狀態(tài)更新的時候自動更新視圖;
- 但MVVM雙向數(shù)據(jù)綁定并不是唯一的辦法,還有一個非常直觀的方法:一旦狀態(tài)發(fā)生了變化,就用模版引擎重新渲染整個視圖,然后用新的視圖更換掉舊的視圖。
React采用的就是第四種模式;但是我們都知道對于操作DOM成本太高,而相對操作JavaScript就快速多了,而Html DOM可以很簡單的用JavaScript對象表示出來(Virtual DOM就這樣誕生了)
這樣的做法會導(dǎo)致很多的問題,最大的問題就是這樣做會很慢,因為即使一個小小的狀態(tài)變更都要重新構(gòu)造整棵 DOM,性價比太低;而React Virtual DOM在狀態(tài)更新過程加了一些特別的操作來避免整棵 DOM 樹變更(它就是接下來的Diff算法)。
接下來的Diff算法即將更新,敬請期待~~~
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
解決react-connect中使用forwardRef遇到的問題
這篇文章主要介紹了解決react-connect中使用forwardRef遇到的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05
深入理解React調(diào)度(Scheduler)原理
本文主要介紹了深入理解React調(diào)度(Scheduler)原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
D3.js(v3)+react 實現(xiàn)帶坐標(biāo)與比例尺的散點圖 (V3版本)
散點圖(Scatter Chart),通常是一橫一豎兩個坐標(biāo)軸,數(shù)據(jù)是一組二維坐標(biāo),分別對應(yīng)兩個坐標(biāo)軸,與坐標(biāo)軸對應(yīng)的地方打上點。由此可以猜到,需要的元素包括circle(圓)和axis(坐標(biāo)軸),接下來通過本文大家分享D3.js(v3)+react 實現(xiàn)帶坐標(biāo)與比例尺的散點圖 (V3版本) ,一起看看2019-05-05

