摘要:對如何生成等感興趣的可以去探究,這里不做具體的討論,本文只是想總結下整個渲染過程。最終的過程是以上是渲染的一個基本流程,下一篇計劃總結下更新的流程,即后發生了什么。
程序假設有如下 jsx
class Form extends React.Component { constructor() { super(); } render() { return (); } } ReactDOM.render( (
拿 ReactDOM render 的部分(
React.createElement( "div", { className: "test" }, React.createElement( "span", { onClick: function(){} }, "CLICK ME" ), React.createElement(Form, null) )
這里看下 API: React.createElement(component, props, ...children)。它生成一個 js 的對象,這個對象是用來代表一個真實的 dom node,這個 js 的對象就是我們俗稱的虛擬dom。(虛擬 dom 的意思是用 js 對象結構模擬出 html 中 dom 結構,批量的增刪改查先直接操作 js 對象,最后更新到真正的 dom 樹上。因為直接操作 js 對象的速度要比操作 dom 的那些 api 要快。) 譬如根元素 生成對應出來的虛擬 dom 是:
{ type: "div", props: { className: "test", children: [] } }
除了一些 dom 相關的屬性,虛擬 dom 對象還包括些 React 自身需要的屬性,譬如:ref,key。最終示例中的 jsx 生成出來的虛擬 dom,大致如下:
{ type: "div", props: { className: "xxx", children: [ { type: "span", props: { children: [ "CLICK ME" ] }, ref: key: }, { type: Form, props: { children: [] }, ref: key: } ] | Element } ref: "xxx", key: "xxx" }
有了虛擬 dom,接下來的工作就是把這個虛擬 dom 樹真正渲染成一個 dom 樹。React 的做法是針對不同的 type 構造相應的渲染對象,渲染對象提供一個 mountComponent 方法(負責把對應的某個虛擬 dom 的節點生成成具體的 dom node),然后循環迭代整個 vdom tree 生成一個完整的 dom node tree,最終插入容器節點。查看源碼你會發現如下代碼:
// vdom 是第3步生成出來的虛擬 dom 對象 var renderedComponent = instantiateReactComponent( vdom ); // dom node var markup = renderedComponent.mountComponent(); // 把生成的 dom node 插入到容器 node 里面,真正在頁面上顯示出來 // 下面是偽代碼,React 的 dom 操作封裝在 DOMLazyTree 里面 containerNode.appendChild( markup );
instantiateReactComponent 傳入的是虛擬 dom 節點,這個方法做的就是根據不同的 type 調用如下方法生成渲染對象:
// 如果節點是字符串或者數字 return ReactHostComponent.createInstanceForText( vdom(string|number) ); // 如果節點是宿主內置節點,譬如瀏覽器的 html 的節點 return ReactHostComponent.createInternalComponent( vdom ); // 如果是 React component 節點 return new ReactCompositeComponentWrapper( vdom );
ReactHostComponent.createXXX 也只是一層抽象,不是最終的的渲染對象,這層抽象屏蔽了宿主。譬如手機端(React native)和瀏覽器中同樣調用 ReactHostComponent.createInternalComponent( vdom ); 他生成的最終的渲染對象是不同的,我們當前只討論瀏覽器環境。字符串和數字沒有什么懸念,在這里我們就不深入探討了,再進一步看,div 等 html 的原生 dom 節點對應的渲染對象是 ReactDOMComponent 的實例。如何把 { type:"div", ... } 生成一個 dom node 就在這個類(的 mountComponent 方法)里面。(對如何生成 div、span、input、select 等 dom node 感興趣的可以去探究 ReactDOMComponent,這里不做具體的討論,本文只是想總結下 React 整個渲染過程。下面只給出一個最簡示例代碼:)
class ReactDOMComponent { constructor( vdom ) { this._currentElement = vdom; } mountComponent() { var result; var props = this._currentElement.props; if ( this._currentElement.type === "div" ) { result = document.createElement( "div" ); for(var key in props ) { result.setAttribute( key, props[ key ] ); } } else { // 其他類型 } // 迭代子節點 props.children.forEach( child=>{ var childRenderedComponent = = instantiateReactComponent( child ); var childMarkup = childRenderedComponent.mountComponent(); result.appendChild( childMarkup ); } ) return result; } }
我們再看下 React component 的渲染對象 ReactCompositeComponentWrapper(主要實現在 ReactCompositeComponent 里面,ReactCompositeComponentWrapper 只是一個防止循環引用的 wrapper)
// 以下是偽代碼 class ReactCompositeComponent { _currentElement: vdom, _rootNodeID: 0, _compositeType: _instance: _hostParent: _hostContainerInfo: // See ReactUpdateQueue _updateBatchNumber: _pendingElement: _pendingStateQueue: _pendingReplaceState: _pendingForceUpdate: _renderedNodeType: _renderedComponent: _context: _mountOrder: _topLevelWrapper: // See ReactUpdates and ReactUpdateQueue. _pendingCallbacks: // ComponentWillUnmount shall only be called once _calledComponentWillUnmount: // render to dom node mountComponent( transaction, hostParent, hostContainerInfo, context ) { // ---------- 初始化 React.Component -------------- var Component = this._currentElement.type; var publicProps = this._currentElement.props; /* React.Component 組件有2種: new Component(publicProps, publicContext, updateQueue); new StatelessComponent(Component); 對應的 compositeType 有三種 this._compositeType = StatelessFunctional | PureClass | ImpureClass, 組件種類和 compositeType 在源碼中都有區分,但是這里為了簡單,只示例最常用的一種組件的代碼 */ var inst = new Component(publicProps, publicContext, updateQueue); inst.props = publicProps; inst.context = publicContext; inst.refs = emptyObject; inst.updater = updateQueue; // 渲染對象存儲組件對象 this._instance = inst; // 通過 map 又把組件對象和渲染對象聯系起來 ReactInstanceMap.set(inst, this); /* ReactInstanceMap: { ----------------------------------------------- | | v | React.Component: ReactCompositeComponentWrapper { | _instance: <------------------------------------- } } 這樣雙方都在需要對方的時候可以獲得彼此的引用 */ // ---------- 生成 React.Component 的 dom -------------- // 組件生命周期函數 componentWillMount 被調用 inst.componentWillMount(); // 調用 render 方法返回組件的虛擬 dom var renderedElement = inst.render(); // save nodeType var nodeType = ReactNodeTypes.getType(renderedElement); this._renderedNodeType = nodeType; // 根據組件的虛擬 dom 生成渲染對象 var child = instantiateReactComponent(renderedElement) this._renderedComponent = child; // 生成真正的 dom node // 其實源碼中的真正代碼應該是 var markup = ReactReconciler.mountComponent( child, ... ), // 這里為了簡化說明,先不深究 ReactReconciler.mountComponent 還做了點什么 var markup = child.mountComponent(); // 把組件生命周期函數 componentDidMount 注冊到回調函數中,當整個 dom node tree 被添加到容器節點后觸發。 transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); return markup; } } // static member ReactCompositeComponentWrapper._instantiateReactComponent = instantiateReactComponent
最終的過程是:
CLICK ME
??|
babel and React.createElement
??|
??v
{ type: "div", props: { className: "xxx", children: [ { type: "span", props: { children: }, ref: key: }, { type: Form, props: { children: }, ref: key: } ] | Element } ref: "xxx", key: "xxx" }
??|
var domNode = new ReactDOMComponent( vdom ).mountComponent();
??|
??v
domNode = { ReactDOMComponent -> props: { children: [ ReactDOMComponent -> ReactCompositeComponentWrapper.render() -> vdom -> instantiateReactComponent(vdom) ->
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83010.html
摘要:組件生命周期嚴格定義了組件的生命周期,生命周期可能會經歷如下三個過程裝載過程也就是把組件第一次在樹上渲染的過程更新過程當組件被從新渲染的過程卸載過程組件從樹中刪除的過程。三種不同的過程,庫會調用組件的一些成員函數,即生命周期函數。 3. 組件生命周期 React嚴格定義了組件的生命周期,生命周期可能會經歷如下三個過程: 裝載過程(Mount):也就是把組件第一次在DOM樹上渲染的...
摘要:組件的性能優化高德納我們應該忘記忽略很小的性能優化,可以說的情況下,過早的優化是萬惡之源,而我們應該關心對性能影響最關鍵的另外的代碼。對多個組件的性能優化當一個組件被裝載更新和卸載時,組件的一序列生命周期函數會被調用。 React組件的性能優化 高德納: 我們應該忘記忽略很小的性能優化,可以說97%的情況下,過早的優化是萬惡之源,而我們應該關心對性能影響最關鍵的另外3%的代碼。...
摘要:組件裝載過程裝載過程依次調用的生命周期函數中每個類的構造函數,創造一個組件實例,當然會調用對應的構造函數。組件需要構造函數,是為了以下目的初始化,因為生命周期中任何函數都有可能訪問,構造函數是初始化的理想場所綁定成員函數的環境。 React系列---React(一)初識ReactReact系列---React(二)組件的prop和stateReact系列---之React(三)組件的生...
摘要:他們的應用是比較復雜的,組件樹也是非常龐大,假設有一千個組件要渲染,每個耗費一千個就是由于是單線程的,這里都在努力的干活,一旦開始,中間就不會停。 悄悄的, React v16.7 發布了。 React v16.7: No, This Is Not The One With Hooks. showImg(https://segmentfault.com/img/bVblq9L?w=97...
摘要:架構理解引用原文是核心算法正在進行的重新實現。構建的過程就是的過程,通過來調度執行一組任務,每完成一個任務后回來看看有沒有插隊的更緊急的,把時間控制權交還給主線程,直到下一次回調再繼續構建。 React Fiber 架構理解 引用原文:React Fiber ArchitectureReact Fiber is an ongoing reimplementation of Reacts...
閱讀 1202·2021-11-23 09:51
閱讀 1980·2021-10-08 10:05
閱讀 2339·2019-08-30 15:56
閱讀 1900·2019-08-30 15:55
閱讀 2640·2019-08-30 15:55
閱讀 2487·2019-08-30 13:53
閱讀 3498·2019-08-30 12:52
閱讀 1250·2019-08-29 10:57