>웹 프론트엔드 >JS 튜토리얼 >React의 첫 번째 렌더링 분석(순수 DOM 요소)

React의 첫 번째 렌더링 분석(순수 DOM 요소)

不言
不言앞으로
2018-10-20 14:38:002693검색

이 글의 내용은 React(순수 DOM 요소)의 첫 번째 렌더링 분석에 관한 것입니다. 필요한 친구들이 참고할 수 있기를 바랍니다.

React는 ReactDom과 ReactNative는 물론 서버 렌더링 등을 동시에 고려해야 하기 때문에 코드의 추상화 수준이 높고 중첩 수준이 매우 깊습니다. 소스 코드를 읽는 것은 매우 힘든 과정입니다. React 소스 코드를 배우는 과정에서 이 시리즈의 글이 가장 큰 도움이 되었기 때문에, 이 시리즈의 글을 바탕으로 제가 이해한 내용을 이야기해 보기로 했습니다. 이 글에서는 원문의 예문을 많이 사용하겠습니다. 원문의 느낌을 경험하고 싶다면 원문을 읽어 보시는 걸 추천합니다.

이 시리즈의 기사는 React 15.4.2를 기반으로 합니다.

  • React.createElement

우리는 보통 JSX를 직접 사용하여 작성합니다. 결국 HTML 태그는 Babel에 의해 컴파일된 후 React.createElement의 함수 형식으로 변환됩니다. 좀 더 깊이 있게 이해하고 싶다면 제가 이전에 쓴 기사: 당신이 모르는 Virtual DOM (1): Virtual Dom 소개를 읽어보세요. 기사의 h 함수는 Babel에서 구성되지 않은 경우 기본적으로 React.createElement로 설정됩니다.

아래에서는 가장 간단한 예에서 React가 어떻게 렌더링되는지 살펴보겠습니다.

ReactDOM.render(
    <h1>hello world</h1>,
    document.getElementById('root')
);

JSX 컴파일 후에는 다음과 같습니다

ReactDOM.render(
    React.createElement(
        'h1',
        { style: { "color": "blue" } },
        'hello world'
    ),
    document.getElementById('root')
);

먼저 React.createElement의 소스코드를 살펴보겠습니다. React.createElement的源码。

// 文件位置:src/isomorphic/React.js

var ReactElement = require('ReactElement');

...

var createElement = ReactElement.createElement;

...

var React = {
    ...
    
    createElement: createElement,
    
    ...
}

module.exports = React;

最终的实现需要查看ReactElement.createElement

// 文件位置:src/isomorphic/classic/element/ReactElement.js

ReactElement.createElement = function (type, config, children) {
    ...

    // 1. 将过滤后的有效的属性,从config拷贝到props
    if (config != null) {
        
        ...
        
        for (propName in config) {
            if (hasOwnProperty.call(config, propName) &&
                !RESERVED_PROPS.hasOwnProperty(propName)) {
                props[propName] = config[propName];
            }
        }
    }

    // 2. 将children以数组的形式拷贝到props.children属性
    var childrenLength = arguments.length - 2;
    if (childrenLength === 1) {
        props.children = children;
    } else if (childrenLength > 1) {
        var childArray = Array(childrenLength);
        for (var i = 0; i <p>本质上只做了3件事:</p><ol class=" list-paddingleft-2">
<li><p>将过滤后的有效的属性,从config拷贝到props</p></li>
<li><p>将children以数组的形式拷贝到props.children属性</p></li>
<li><p>默认属性赋值</p></li>
</ol><p>最终的返回值是<code>ReactElement</code>。我们再来看看它做了什么</p><pre class="brush:php;toolbar:false">// 文件位置:src/isomorphic/classic/element/ReactElement.js

var ReactElement = function (type, key, ref, self, source, owner, props) {
    var element = {
        // This tag allow 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;
};

最终只是返回了一个简单对象。调用栈是这样的:

React.createElement
|=ReactElement.createElement(type, config, children)
    |-ReactElement(type,..., props)

这里生成的 ReactElement 我们将其命名为ReactElement[1],它将作为参数传入到 ReactDom.render。

  • ReactDom.render

ReactDom.render 最终会调用 ReactMount 的 _renderSubtreeIntoContainer:

// 文件位置:src/renderers/dom/client/ReactMount.js

_renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
    ...
    var nextWrappedElement = React.createElement(
        TopLevelWrapper, 
        {
            child: nextElement
        }
    );

    ...
    
    var component = ReactMount._renderNewRootComponent(
        nextWrappedElement,
        container,
        shouldReuseMarkup,
        nextContext
    )._renderedComponent.getPublicInstance();
    
    ...
    
    return component;
},

...

var TopLevelWrapper = function () {
    this.rootID = topLevelRootCounter++;
};

TopLevelWrapper.prototype.isReactComponent = {};

TopLevelWrapper.prototype.render = function () {
    return this.props.child;
};

TopLevelWrapper.isReactTopLevelWrapper = true;

...

_renderNewRootComponent: function (
    nextElement,
    container,
    shouldReuseMarkup,
    context
) {
    ...
    
    var componentInstance = instantiateReactComponent(nextElement, false);

    ...

    return componentInstance;
},

这里又会调用到另一个文件 instantiateReactComponent:

// 文件位置:src/renders/shared/stack/reconciler/instantiateReactComponent.js

function instantiateReactComponent(node, shouldHaveDebugID) {
    var instance;

    ...

    instance = new ReactCompositeComponentWrapper(element);
    
    ...

    return instance;
}

// To avoid a cyclic dependency, we create the final class in this module
var ReactCompositeComponentWrapper = function (element) {
    this.construct(element);
};

Object.assign(
    ReactCompositeComponentWrapper.prototype,
    ReactCompositeComponent, 
    {
        _instantiateReactComponent: instantiateReactComponent,
    }
);

这里又会调用到另一个文件 ReactCompositeComponent:

// 文件位置:src/renders/shared/stack/reconciler/ReactCompositeComponent.js

var ReactCompositeComponent = {
    construct: function (element) {
        this._currentElement = element;
        this._rootNodeID = 0;
        this._compositeType = null;
        this._instance = null;
        this._hostParent = null;
        this._hostContainerInfo = null;

        // See ReactUpdateQueue
        this._updateBatchNumber = null;
        this._pendingElement = null;
        this._pendingStateQueue = null;
        this._pendingReplaceState = false;
        this._pendingForceUpdate = false;

        this._renderedNodeType = null;
        this._renderedComponent = null;
        this._context = null;
        this._mountOrder = 0;
        this._topLevelWrapper = null;

        // See ReactUpdates and ReactUpdateQueue.
        this._pendingCallbacks = null;

        // ComponentWillUnmount shall only be called once
        this._calledComponentWillUnmount = false;

        if (__DEV__) {
            this._warnedAboutRefsInRender = false;
        }
    }
    
    ...
}

我们用ReactCompositeComponent[T]

ReactDOM.render
|=ReactMount.render(nextElement, container, callback)
|=ReactMount._renderSubtreeIntoContainer()
    |-ReactMount._renderNewRootComponent(
        nextWrappedElement, // scr:------------------> ReactElement[2]
        container,          // scr:------------------> document.getElementById('root')
        shouldReuseMarkup,  // scr: null from ReactDom.render()
        nextContext,        // scr: emptyObject from ReactDom.render()
    )
    |-instantiateReactComponent(
          node,             // scr:------------------> ReactElement[2]
          shouldHaveDebugID /* false */
        )
        |-ReactCompositeComponentWrapper(
            element         // scr:------------------> ReactElement[2]
        );
        |=ReactCompositeComponent.construct(element)
최종 구현은 ReactElement.createElement를 확인해야 합니다.

rrreee

기본적으로 3가지 작업만 수행합니다.

    필터링된 유효한 속성을 구성에서 소품으로 복사React의 첫 번째 렌더링 분석(순수 DOM 요소)

    자식을 배열로 복사 양식은 다음과 같습니다. props.children 속성에 복사됨


    기본 속성 할당

    #🎜🎜#
#🎜🎜#최종 반환 값은 입니다. ReactElement. 그것이 무엇을 하는지 살펴봅시다#🎜🎜#rrreee#🎜🎜#결국에는 단순한 객체를 반환합니다. 호출 스택은 다음과 같습니다. #🎜🎜#rrreee#🎜🎜#여기에서 생성된 ReactElement의 이름을 ReactElement[1]로 지정하고 ReactDom.render에 매개변수로 전달됩니다. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#ReactDom.render#🎜🎜##🎜🎜##🎜🎜##🎜🎜#ReactDom.render는 결국 ReactMount의 _renderSubtreeIntoContainer를 호출합니다. #🎜 🎜#아아아아# 🎜🎜#다른 파일 instantiateReactComponent는 여기에서 호출됩니다: #🎜🎜#rrreee#🎜🎜#다른 파일 ReactCompositeComponent는 여기에서 호출됩니다: #🎜🎜#rrreee#🎜🎜#우리는 ReactCompositeComponent[ T]를 사용합니다 여기에서 생성된 최상위 구성 요소를 나타냅니다. #🎜🎜##🎜🎜#전체 호출 스택은 다음과 같습니다. #🎜🎜#rrreee#🎜🎜#구성 요소 간의 계층 구조는 다음과 같습니다. #🎜🎜##🎜🎜##🎜🎜##🎜🎜# #🎜🎜##🎜🎜##🎜🎜#최상위 구성 요소가 빌드된 후 다음 단계는 batedMountComponentIntoNode(ReactMount의 _renderNewRootComponent 메서드)를 호출하여 페이지를 렌더링하는 것입니다. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#

위 내용은 React의 첫 번째 렌더링 분석(순수 DOM 요소)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제