React 是一個十分龐大的庫,因爲要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,致使其代碼抽象化程度很高,嵌套層級很是深,閱讀其源碼是一個很是艱辛的過程。在學習 React 源碼的過程當中,給我幫助最大的就是這個系列文章,因而決定基於這個系列文章談一下本身的理解。本文會大量用到原文中的例子,想體會原汁原味的感受,推薦閱讀原文。javascript
本系列文章基於 React 15.4.2 ,如下是本系列其它文章的傳送門:
React 源碼深度解讀(一):首次 DOM 元素渲染 - Part 1
React 源碼深度解讀(二):首次 DOM 元素渲染 - Part 2
React 源碼深度解讀(三):首次 DOM 元素渲染 - Part 3
React 源碼深度解讀(四):首次自定義組件渲染 - Part 1
React 源碼深度解讀(五):首次自定義組件渲染 - Part 2
React 源碼深度解讀(六):依賴注入
React 源碼深度解讀(七):事務 - Part 1
React 源碼深度解讀(八):事務 - Part 2
React 源碼深度解讀(九):單個元素更新
React 源碼深度解讀(十):Diff 算法詳解java
上一篇講解了平臺無關的代碼,這篇繼續來說針對與 HTML DOM 操做的代碼。react
|=ReactMount.render(nextElement, container, callback) ___ |=ReactMount._renderSubtreeIntoContainer() | |-ReactMount._renderNewRootComponent() | |-instantiateReactComponent() | |~batchedMountComponentIntoNode() upper half |~mountComponentIntoNode() (平臺無關) |-ReactReconciler.mountComponent() | |-ReactCompositeComponent.mountComponent() | |-ReactCompositeComponent.performInitialMount() | |-instantiateReactComponent() _|_ |-ReactDOMComponent.mountComponent() lower half |-_mountImageIntoNode() (HTML DOM 相關) _|_
先來看看 ReactDOMComponent.mountComponent 作了什麼:算法
// 文件位置:src/renderers/dom/shared/ReactDomComponent.js tComponent: function ( transaction, hostParent, hostContainerInfo, context ) { ... var mountImage; var ownerDocument = hostContainerInfo._ownerDocument; var el; // document.createElement 建立 h1 元素 el = ownerDocument.createElement(this._currentElement .type, props.is); ... // 建立 component 與 DOM 的雙向連接 // ReactDOMComponent[ins]._hostNode 指向 DOM // DOM 的 __reactInternalInstance 指向 component ReactDOMComponentTree.precacheNode(this, el); // 設置屬性 this._updateDOMProperties(null, props, transaction); var lazyTree = DOMLazyTree(el); // 循環建立子元素 this._createInitialChildren(transaction, props, context, lazyTree); mountImage = lazyTree; ... return mountImage; },
到此爲止,實例間的關係是這樣的:segmentfault
DOM 元素建立完成後,剩下的就是將其掛載到 container 上面去了。這裏調用的是 ReactMount 的 _mountImageIntoNode:服務器
|=ReactMount.render(nextElement, container, callback) ___ |=ReactMount._renderSubtreeIntoContainer() | |-ReactMount._renderNewRootComponent() | |-instantiateReactComponent() | |~batchedMountComponentIntoNode() upper half |~mountComponentIntoNode( (平臺無關) wrapperInstance, // scr: -> not of interest now | container, // scr: --> document.getElementById(‘root’) transaction, // scr: --> not of interest | shouldReuseMarkup, // scr: -------> null | context, // scr: -------> not of interest ) | |-ReactReconciler.mountComponent() | |-ReactCompositeComponent.mountComponent() | |-ReactCompositeComponent.performInitialMount() | |-instantiateReactComponent() _|_ |-ReactDOMComponent.mountComponent() | /* we are here */ lower half |-_mountImageIntoNode() (HTML DOM 相關) markup, // scr: --> DOMLazyTree[ins] | container, // scr: --> document.getElementById(‘root’) wrapperInstance, // scr:----> same | shouldReuseMarkup, // scr:--> same | transaction, // scr: -------> same | )
具體實現:app
_mountImageIntoNode: function ( markup, // DOMLazyTree[ins] container, // document.getElementById(‘root’) instance, shouldReuseMarkup, transaction ) { ... while (container.lastChild) { container.removeChild(container.lastChild); } DOMLazyTree.insertTreeBefore(container, markup, null); ... }
DOMLazyTree.insertTreeBefore 最終會調用 parentNode.insertBefore,將元素掛載到 container 上。到此爲止,首次渲染就完成啦!dom
從 React 啓動到元素渲染到頁面,並不像看起來這麼簡單,中間經歷了複雜的層級調用。原文的這張圖總結得很是好:學習