摘要:的做法比較簡(jiǎn)單,它會(huì)先刪除整個(gè)子樹(shù),然后再重新創(chuàng)建一遍。同樣道理,當(dāng)節(jié)點(diǎn)改為節(jié)點(diǎn)時(shí),整棵子樹(shù)也會(huì)被刪掉,節(jié)點(diǎn)會(huì)重新創(chuàng)建。更新為和中較大的。到此為止,整個(gè)源碼解讀系列先告一段落了,后會(huì)有期。
歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:
React 是一個(gè)十分龐大的庫(kù),由于要同時(shí)考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級(jí)非常深。閱讀 React 源碼是一個(gè)非常艱辛的過(guò)程,在學(xué)習(xí)過(guò)程中給我?guī)椭畲蟮木褪沁@個(gè)系列文章。作者對(duì)代碼的調(diào)用關(guān)系梳理得非常清楚,而且還有配圖幫助理解,非常值得一讀。站在巨人的肩膀之上,我嘗試再加入自己的理解,希望對(duì)有志于學(xué)習(xí) React 源碼的讀者帶來(lái)一點(diǎn)啟發(fā)。
本系列文章基于 React 15.4.2 ,以下是本系列其它文章的傳送門(mén):
React 源碼深度解讀(一):首次 DOM 元素渲染 - Part 1
React 源碼深度解讀(二):首次 DOM 元素渲染 - Part 2
React 源碼深度解讀(三):首次 DOM 元素渲染 - Part 3
React 源碼深度解讀(四):首次自定義組件渲染 - Part 1
React 源碼深度解讀(五):首次自定義組件渲染 - Part 2
React 源碼深度解讀(六):依賴(lài)注入
React 源碼深度解讀(七):事務(wù) - Part 1
React 源碼深度解讀(八):事務(wù) - Part 2
React 源碼深度解讀(九):?jiǎn)蝹€(gè)元素更新
React 源碼深度解讀(十):Diff 算法詳解
React 在比較新舊 2 棵虛擬 DOM 樹(shù)的時(shí)候,會(huì)同時(shí)考慮兩點(diǎn):
盡量少的創(chuàng)建 / 刪除節(jié)點(diǎn),多使用移動(dòng)節(jié)點(diǎn)的方式
比較次數(shù)要盡量少,算法要足夠的快
如果只考慮第一點(diǎn),算法的時(shí)間復(fù)雜度要達(dá)到 O(n3) 的級(jí)別。也就是說(shuō)對(duì)于一個(gè)有 1000 個(gè)節(jié)點(diǎn)的樹(shù),要進(jìn)行 10 億次的比較,這對(duì)于前端應(yīng)用來(lái)說(shuō)是完全不可接受的。因此,React 選用了啟發(fā)式的算法,將時(shí)間復(fù)雜度控制在 O(n) 的級(jí)別。這個(gè)算法基于以下 2 個(gè)假設(shè):
如果 2 個(gè)節(jié)點(diǎn)的類(lèi)型不一樣,以這 2 個(gè)節(jié)點(diǎn)為根結(jié)點(diǎn)的樹(shù)會(huì)完全不同
對(duì)于多次 render 中結(jié)構(gòu)保持不變的節(jié)點(diǎn),開(kāi)發(fā)者會(huì)用一個(gè) key 屬性標(biāo)識(shí)出來(lái),以便重用
另外,React 只會(huì)對(duì)同一層的節(jié)點(diǎn)作比較,不會(huì)跨層級(jí)比較,如圖所示:
Diff 使用的是深度優(yōu)先算法,當(dāng)遇到下圖這樣的情況:
最高效的算法應(yīng)該是直接將 A 子樹(shù)移動(dòng)到 D 節(jié)點(diǎn),但這樣就涉及到跨層級(jí)比較,時(shí)間復(fù)雜度會(huì)陡然上升。React 的做法比較簡(jiǎn)單,它會(huì)先刪除整個(gè) A 子樹(shù),然后再重新創(chuàng)建一遍。結(jié)合到實(shí)際的使用場(chǎng)景,改變一個(gè)組件的從屬關(guān)系的情況也是很少的。
同樣道理,當(dāng) D 節(jié)點(diǎn)改為 G 節(jié)點(diǎn)時(shí),整棵 D 子樹(shù)也會(huì)被刪掉,E、F 節(jié)點(diǎn)會(huì)重新創(chuàng)建。
對(duì)于列表的 Diff,節(jié)點(diǎn)的 key 有助于節(jié)點(diǎn)的重用:
如上圖所示,當(dāng)沒(méi)有 key 的時(shí)候,如果中間插入一個(gè)新節(jié)點(diǎn),Diff 過(guò)程中從第三個(gè)節(jié)點(diǎn)開(kāi)始的節(jié)點(diǎn)都是刪除舊節(jié)點(diǎn),創(chuàng)建新節(jié)點(diǎn)。當(dāng)有 key 的時(shí)候,除了第三個(gè)節(jié)點(diǎn)是新創(chuàng)建外,第四和第五個(gè)節(jié)點(diǎn)都是通過(guò)移動(dòng)實(shí)現(xiàn)的。
三、無(wú) Key Diff在了解了總體的 Diff 策略后,我們來(lái)近距離的審視源碼。先更新示例代碼,注意 li 元素沒(méi)有使用 key :
class App extends Component { constructor(props) { super(props); this.state = { data : ["one", "two"], }; this.timer = setInterval( () => this.tick(), 5000 ); } tick() { this.setState({ data: ["new", "one", "two"], }); } render() { return (
元素變化如下圖所示,5 秒后會(huì)新增一個(gè) new 元素。
我們跳過(guò)前面的邏輯,直接來(lái)看 Diff 的代碼
_updateRenderedComponent: function (transaction, context) { var prevComponentInstance = this._renderedComponent; // 之前的 Virtual DOM var prevRenderedElement = prevComponentInstance._currentElement; // 最新的 Virtual DOM var nextRenderedElement = this._renderValidatedComponent(); var debugID = 0; if (__DEV__) { debugID = this._debugID; } if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) { ReactReconciler.receiveComponent( prevComponentInstance, nextRenderedElement, transaction, this._processChildContext(context) ); } else { ... } }, // shouldUpdateReactComponent.js function shouldUpdateReactComponent(prevElement, nextElement) { var prevEmpty = prevElement === null || prevElement === false; var nextEmpty = nextElement === null || nextElement === false; if (prevEmpty || nextEmpty) { return prevEmpty === nextEmpty; } var prevType = typeof prevElement; var nextType = typeof nextElement; if (prevType === "string" || prevType === "number") { return (nextType === "string" || nextType === "number"); } else { return ( nextType === "object" && prevElement.type === nextElement.type && prevElement.key === nextElement.key ); } }
_updateRenderedComponent方法位于 ReactCompositeComponent 內(nèi)。它先獲取新、舊 2 個(gè) Virtual DOM,然后通過(guò)shouldUpdateReactComponent判斷節(jié)點(diǎn)類(lèi)型是否相同。在我們的例子里,跟節(jié)點(diǎn)都是 ul 元素,因此跳過(guò)中間的層級(jí)后,會(huì)走到 ReactDOMComponent 的 updateComponent 方法。他會(huì)更新屬性和子元素,更新屬性部分上一篇文章已經(jīng)講清楚了,下面看看更新子元素部分。最終會(huì)調(diào)用 ReactMultiChild 的 _updateChildren :
_updateChildren: function (nextNestedChildrenElements, transaction, context) { ... var nextChildren = this._reconcilerUpdateChildren( prevChildren, nextNestedChildrenElements, mountImages, removedNodes, transaction, context ); ... for (name in nextChildren) { if (!nextChildren.hasOwnProperty(name)) { continue; } var prevChild = prevChildren && prevChildren[name]; var nextChild = nextChildren[name]; if (prevChild === nextChild) { updates = enqueue( updates, this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex) ); lastIndex = Math.max(prevChild._mountIndex, lastIndex); prevChild._mountIndex = nextIndex; } else { if (prevChild) { // Update `lastIndex` before `_mountIndex` gets unset by unmounting. lastIndex = Math.max(prevChild._mountIndex, lastIndex); // The `removedNodes` loop below will actually remove the child. } // The child must be instantiated before it"s mounted. updates = enqueue( updates, this._mountChildAtIndex( nextChild, mountImages[nextMountIndex], lastPlacedNode, nextIndex, transaction, context ) ); nextMountIndex++; } nextIndex++; lastPlacedNode = ReactReconciler.getHostNode(nextChild); } // Remove children that are no longer present. for (name in removedNodes) { if (removedNodes.hasOwnProperty(name)) { updates = enqueue( updates, this._unmountChild(prevChildren[name], removedNodes[name]) ); } } if (updates) { processQueue(this, updates); } this._renderedChildren = nextChildren; }, _reconcilerUpdateChildren: function ( prevChildren, nextNestedChildrenElements, mountImages, removedNodes, transaction, context ) { var nextChildren; var selfDebugID = 0; nextChildren = flattenChildren(nextNestedChildrenElements, selfDebugID); ReactChildReconciler.updateChildren( prevChildren, nextChildren, mountImages, removedNodes, transaction, this, this._hostContainerInfo, context, selfDebugID ); return nextChildren; },
在開(kāi)始 Diff li 元素之前,它會(huì)先調(diào)用_reconcilerUpdateChildren去更新 li 元素的子元素,也就是文本。在函數(shù)中調(diào)用了flattenChildren方法,將數(shù)組轉(zhuǎn)換成對(duì)象:
function flattenSingleChildIntoContext( traverseContext: mixed, child: ReactElement < any > , name: string, selfDebugID ? : number, ): void { // We found a component instance. if (traverseContext && typeof traverseContext === "object") { const result = traverseContext; const keyUnique = (result[name] === undefined); if (keyUnique && child != null) { result[name] = child; } } } function flattenChildren( children: ReactElement < any > , selfDebugID ? : number, ): ? { [name: string]: ReactElement < any > } { if (children == null) { return children; } var result = {}; traverseAllChildren(children, flattenSingleChildIntoContext, result); return result; } // traverseAllChildren.js function traverseAllChildren(children, callback, traverseContext) { if (children == null) { return 0; } return traverseAllChildrenImpl(children, "", callback, traverseContext); } function traverseAllChildrenImpl( children, nameSoFar, callback, traverseContext ) { var type = typeof children; if (type === "undefined" || type === "boolean") { // All of the above are perceived as null. children = null; } if (children === null || type === "string" || type === "number" || // The following is inlined from ReactElement. This means we can optimize // some checks. React Fiber also inlines this logic for similar purposes. (type === "object" && children.$$typeof === REACT_ELEMENT_TYPE)) { callback( traverseContext, children, // If it"s the only child, treat the name as if it was wrapped in an array // so that it"s consistent if the number of children grows. nameSoFar === "" ? SEPARATOR + getComponentKey(children, 0) : nameSoFar ); return 1; } var child; var nextName; var subtreeCount = 0; // Count of children found in the current subtree. var nextNamePrefix = nameSoFar === "" ? SEPARATOR : nameSoFar + SUBSEPARATOR; if (Array.isArray(children)) { for (var i = 0; i < children.length; i++) { child = children[i]; nextName = nextNamePrefix + getComponentKey(child, i); subtreeCount += traverseAllChildrenImpl( child, nextName, callback, traverseContext ); } } ... return subtreeCount; } function getComponentKey(component, index) { // Do some typechecking here since we call this blindly. We want to ensure // that we don"t block potential future ES APIs. if (component && typeof component === "object" && component.key != null) { // Explicit key return KeyEscapeUtils.escape(component.key); } // Implicit key determined by the index in the set return index.toString(36); }
flattenSingleChildIntoContext作為flattenChildren的回調(diào)函數(shù),會(huì)作用在每一個(gè)數(shù)組元素上,構(gòu)造一個(gè)對(duì)象(result)。對(duì)象的 key 是通過(guò)getComponentKey這個(gè)方法生成的,可以看到如果沒(méi)有定義 key 屬性,則默認(rèn)會(huì)用數(shù)組的 index 作為 key 。最終構(gòu)造出來(lái)的對(duì)象是這個(gè)樣子的:
然后就會(huì)調(diào)用ReactChildReconciler.updateChildren方法,去更新 li 的文本和創(chuàng)建新的 li 元素。
updateChildren: function ( prevChildren, nextChildren, mountImages, removedNodes, transaction, hostParent, hostContainerInfo, context, selfDebugID // 0 in production and for roots ) { if (!nextChildren && !prevChildren) { return; } var name; var prevChild; for (name in nextChildren) { if (!nextChildren.hasOwnProperty(name)) { continue; } prevChild = prevChildren && prevChildren[name]; var prevElement = prevChild && prevChild._currentElement; var nextElement = nextChildren[name]; if (prevChild != null && shouldUpdateReactComponent(prevElement, nextElement)) { ReactReconciler.receiveComponent( prevChild, nextElement, transaction, context ); nextChildren[name] = prevChild; } else { if (prevChild) { removedNodes[name] = ReactReconciler.getHostNode( prevChild); ReactReconciler.unmountComponent(prevChild, false); } // The child must be instantiated before it"s mounted. var nextChildInstance = instantiateReactComponent( nextElement, true); nextChildren[name] = nextChildInstance; // Creating mount image now ensures refs are resolved in right order // (see https://github.com/facebook/react/pull/7101 for explanation). var nextChildMountImage = ReactReconciler.mountComponent( nextChildInstance, transaction, hostParent, hostContainerInfo, context, selfDebugID ); mountImages.push(nextChildMountImage); } } // Unmount children that are no longer present. for (name in prevChildren) { if (prevChildren.hasOwnProperty(name) && !(nextChildren && nextChildren.hasOwnProperty(name))) { prevChild = prevChildren[name]; removedNodes[name] = ReactReconciler.getHostNode( prevChild); ReactReconciler.unmountComponent(prevChild, false); } } },
在更新 li 前,會(huì)根據(jù) key 去取上一次 render 對(duì)應(yīng)的元素prevChild = prevChildren && prevChildren[name]。以第 0 個(gè)元素為例,prevElement 為 ReactElement[3]( key 為‘.0’),而 nextElement 為 ReactElement[2],因此會(huì)調(diào)用ReactReconciler.receiveComponent來(lái)更新文本元素,過(guò)程與上一篇文章一樣。
當(dāng)遍歷到最后一個(gè) li ,由于沒(méi)有 prevChild,會(huì)創(chuàng)建一個(gè)新的實(shí)例。
再回到 ReactMultiChild 的 _updateChildren 方法,這時(shí)nextChildren已經(jīng)創(chuàng)建好,開(kāi)始遍歷:
_updateChildren: function (nextNestedChildrenElements, transaction, context) { ... var nextChildren = this._reconcilerUpdateChildren( prevChildren, nextNestedChildrenElements, mountImages, removedNodes, transaction, context ); ... for (name in nextChildren) { if (!nextChildren.hasOwnProperty(name)) { continue; } var prevChild = prevChildren && prevChildren[name]; var nextChild = nextChildren[name]; if (prevChild === nextChild) { ------------------------------------ 1) updates = enqueue( updates, this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex) ); lastIndex = Math.max(prevChild._mountIndex, lastIndex); prevChild._mountIndex = nextIndex; } else { ------------------------------------ 2) if (prevChild) { // Update `lastIndex` before `_mountIndex` gets unset by unmounting. lastIndex = Math.max(prevChild._mountIndex, lastIndex); // The `removedNodes` loop below will actually remove the child. } // The child must be instantiated before it"s mounted. updates = enqueue( updates, this._mountChildAtIndex( nextChild, mountImages[nextMountIndex], lastPlacedNode, nextIndex, transaction, context ) ); nextMountIndex++; } nextIndex++; lastPlacedNode = ReactReconciler.getHostNode(nextChild); } // Remove children that are no longer present. for (name in removedNodes) { if (removedNodes.hasOwnProperty(name)) { updates = enqueue( updates, this._unmountChild(prevChildren[name], removedNodes[name]) ); } } if (updates) { processQueue(this, updates); } this._renderedChildren = nextChildren; },
前面 2 個(gè) li 元素會(huì)走到分支 1),第三個(gè)元素會(huì)到分支 2),創(chuàng)建新的 li 元素,過(guò)程與上一篇的類(lèi)似。
四、有 Key Diff例子改一下,加入 key:
class App extends Component { constructor(props) { super(props); this.state = { data: ["one", "two"] }; this.timer = setTimeout(() => this.tick(), 5000); } tick() { this.setState({ data: ["new", "one", "two"] }); } render() { return (
流程與無(wú) key 類(lèi)似,不再贅述。flattenChildren后的對(duì)象是這個(gè)樣子的:
由于使用了 key ,ReactChildReconciler.updateChildren不再需要更新 text 了,只需要?jiǎng)?chuàng)建一個(gè)新的實(shí)例。
然后到 ReactMultiChild 的 _updateChildren :
_updateChildren: function (nextNestedChildrenElements, transaction, context) { ... for (name in nextChildren) { if (!nextChildren.hasOwnProperty(name)) { continue; } var prevChild = prevChildren && prevChildren[name]; var nextChild = nextChildren[name]; if (prevChild === nextChild) { ------------------------------------ 1) updates = enqueue( updates, this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex) ); lastIndex = Math.max(prevChild._mountIndex, lastIndex); prevChild._mountIndex = nextIndex; } else { ------------------------------------ 2) if (prevChild) { // Update `lastIndex` before `_mountIndex` gets unset by unmounting. lastIndex = Math.max(prevChild._mountIndex, lastIndex); // The `removedNodes` loop below will actually remove the child. } // The child must be instantiated before it"s mounted. updates = enqueue( updates, this._mountChildAtIndex( nextChild, mountImages[nextMountIndex], lastPlacedNode, nextIndex, transaction, context ) ); nextMountIndex++; } nextIndex++; lastPlacedNode = ReactReconciler.getHostNode(nextChild); } // Remove children that are no longer present. for (name in removedNodes) { if (removedNodes.hasOwnProperty(name)) { updates = enqueue( updates, this._unmountChild(prevChildren[name], removedNodes[name]) ); } } if (updates) { processQueue(this, updates); } this._renderedChildren = nextChildren; },
匹配第一個(gè)元素的時(shí)候,會(huì)到分支 2),后面 2 個(gè)元素都是走分支 1)。
有 key 跟沒(méi) key 相比,減少了 2 次文本元素的更新,提高了效率。
五、深挖 Key Diff再來(lái)考慮更復(fù)雜的情況:
假設(shè)要做上圖的變化,再來(lái)分析下源碼:
_updateChildren: function (nextNestedChildrenElements, transaction, context) { ... var nextIndex = 0; var lastIndex = 0; var nextMountIndex = 0; var lastPlacedNode = null; for (name in nextChildren) { if (!nextChildren.hasOwnProperty(name)) { continue; } var prevChild = prevChildren && prevChildren[name]; var nextChild = nextChildren[name]; if (prevChild === nextChild) { ------------------------------------ 1) updates = enqueue( updates, this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex) ); lastIndex = Math.max(prevChild._mountIndex, lastIndex); prevChild._mountIndex = nextIndex; } else { ------------------------------------ 2) if (prevChild) { // Update `lastIndex` before `_mountIndex` gets unset by unmounting. lastIndex = Math.max(prevChild._mountIndex, lastIndex); // The `removedNodes` loop below will actually remove the child. } // The child must be instantiated before it"s mounted. updates = enqueue( updates, this._mountChildAtIndex( nextChild, mountImages[nextMountIndex], lastPlacedNode, nextIndex, transaction, context ) ); nextMountIndex++; } nextIndex++; lastPlacedNode = ReactReconciler.getHostNode(nextChild); } ... }, moveChild: function (child, afterNode, toIndex, lastIndex) { // If the index of `child` is less than `lastIndex`, then it needs to // be moved. Otherwise, we do not need to move it because a child will be // inserted or moved before `child`. if (child._mountIndex < lastIndex) { return makeMove(child, afterNode, toIndex); } },
這里要先搞清楚 3 個(gè) index:
nextIndex:遍歷 nextChildren 時(shí)候的 index,每遍歷一個(gè)元素加 1
lastIndex:上一次從 prevChildren 中取出來(lái)元素時(shí),這個(gè)元素在 prevChildren 中的 index
_mountIndex:元素在數(shù)組中的位置
下面我們來(lái)走一遍流程:
nextChildren 的第一個(gè)元素是 B ,在 prevChildren 中的位置是 1(_mountIndex),nextIndex 和 lastIndex 都是 0。節(jié)點(diǎn)移動(dòng)的前提是_mountIndex < lastIndex,因此 B 不需要移動(dòng)。lastIndex 更新為 _mountIndex 和 lastIndex 中較大的:1 。nextIndex 自增:1;
nextChildren 的第二個(gè)元素是 A ,在 prevChildren 中的位置是 0(_mountIndex),nextIndex 和 lastIndex 都是 1。這時(shí)_mountIndex < lastIndex,因此將 A 移動(dòng)到 lastPlacedNode (B)的后面 。lastIndex 更新為 _mountIndex 和 lastIndex 中較大的:1 。nextIndex 自增:2;
nextChildren 的第三個(gè)元素是 D ,中 prevChildren 中的位置是 3(_mountIndex),nextIndex 是 2 ,lastIndex 是 1。這時(shí)不滿足_mountIndex < lastIndex,因此 D 不需要移動(dòng)。lastIndex 更新為 _mountIndex 和 lastIndex 中較大的:3 。nextIndex 自增:3;
nextChildren 的第四個(gè)元素是 C ,中 prevChildren 中的位置是 2(_mountIndex),nextIndex 是 3 ,lastIndex 是 3。這時(shí)_mountIndex < lastIndex,因此將 C 移動(dòng)到 lastPlacedNode (D)的后面。循環(huán)結(jié)束。
觀察整個(gè)過(guò)程,移動(dòng)的原則是將原來(lái)的元素往右邊移,通過(guò) lastIndex 來(lái)控制。在 lastIndex 左邊的,就往 lastIndex 的右邊移動(dòng);在 lastIndex 左邊的,不需要?jiǎng)印?/p> 六、總結(jié)
本文詳細(xì)講解了 Diff 過(guò)程中 key 的作用以及復(fù)用節(jié)點(diǎn)的移動(dòng)原則,并結(jié)合源碼做了深入的討論。到此為止,整個(gè) React 源碼解讀系列先告一段落了,后會(huì)有期。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/108606.html
摘要:在學(xué)習(xí)源碼的過(guò)程中,給我?guī)椭畲蟮木褪沁@個(gè)系列文章,于是決定基于這個(gè)系列文章談一下自己的理解。到此為止,首次渲染就完成啦總結(jié)從啟動(dòng)到元素渲染到頁(yè)面,并不像看起來(lái)這么簡(jiǎn)單,中間經(jīng)歷了復(fù)雜的層級(jí)調(diào)用。 前言 React 是一個(gè)十分龐大的庫(kù),由于要同時(shí)考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級(jí)非常深,閱讀其源碼是一個(gè)非常艱辛的過(guò)...
摘要:依賴(lài)注入和控制反轉(zhuǎn),這兩個(gè)詞經(jīng)常一起出現(xiàn)。一句話表述他們之間的關(guān)系依賴(lài)注入是控制反轉(zhuǎn)的一種實(shí)現(xiàn)方式。而兩者有大量的代碼都是可以共享的,這就是依賴(lài)注入的使用場(chǎng)景了。下一步就是創(chuàng)建具體的依賴(lài)內(nèi)容,然后注入到需要的地方這里的等于這個(gè)對(duì)象。 前言 React 是一個(gè)十分龐大的庫(kù),由于要同時(shí)考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級(jí)...
摘要:本篇開(kāi)始介紹自定義組件是如何渲染的。組件將自定義組件命名為,結(jié)構(gòu)如下經(jīng)過(guò)編譯后,生成如下代碼構(gòu)建頂層包裝組件跟普通元素渲染一樣,第一步先會(huì)執(zhí)行創(chuàng)建為的。調(diào)用順序已在代碼中注釋。先看圖,這部分內(nèi)容將在下回分解 前言 React 是一個(gè)十分龐大的庫(kù),由于要同時(shí)考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級(jí)非常深,閱讀其源碼是一個(gè)非...
摘要:前言是一個(gè)十分龐大的庫(kù),由于要同時(shí)考慮和,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級(jí)非常深,閱讀其源碼是一個(gè)非常艱辛的過(guò)程。在學(xué)習(xí)源碼的過(guò)程中,給我?guī)椭畲蟮木褪沁@個(gè)系列文章,于是決定基于這個(gè)系列文章談一下自己的理解。 前言 React 是一個(gè)十分龐大的庫(kù),由于要同時(shí)考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級(jí)非常...
摘要:本文將要講解的調(diào)用棧是下面這個(gè)樣子的平臺(tái)無(wú)關(guān)相關(guān)如果看源碼,我們會(huì)留意到很多相關(guān)的代碼,我們暫時(shí)先將其忽略,會(huì)在后續(xù)的文章中進(jìn)行講解。現(xiàn)在我們來(lái)看一下各實(shí)例間的關(guān)系目前為止的調(diào)用棧平臺(tái)無(wú)關(guān)相關(guān)下一篇講解三總結(jié)本文講解了轉(zhuǎn)化為的過(guò)程。 歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 R...
閱讀 3589·2021-11-17 17:01
閱讀 3998·2021-11-08 13:12
閱讀 2558·2021-10-08 10:04
閱讀 782·2021-09-29 09:35
閱讀 1491·2021-09-26 10:12
閱讀 2205·2021-09-07 09:58
閱讀 2048·2019-08-30 15:55
閱讀 2201·2019-08-30 13:14