资讯专栏INFORMATION COLUMN

解读React源码(二):Virtual DOM模型

kuangcaibao / 1630人阅读

摘要:模型模型负责底层框架的构建工作它拥有一整套的标签并负责虚拟节点及其属性的构建更新删除等工作其实构建一套简易模型并不复杂它只需要具备一个标签所需的基本元素即可标签名属性样式子节点唯一标识中的节点称为它分为种类型其中又分为和创建元素输入输出通过

Virtual DOM模型

1.Virtual DOM模型负责Virtual DOM底层框架的构建工作,它拥有一整套的Virtual DOM标签,
并负责虚拟节点及其属性的构建,更新,删除等工作.
2.其实,构建一套简易Virtual DOM模型并不复杂,它只需要具备一个DOM标签所需的基本元素即可.

{
    // 标签名
    tagName: "div",
    // 属性
    properties: {
        // 样式
        style: {}
    },
    // 子节点
    children: [],
    // 唯一标识
    key: 1
}

3.Virtual DOM中的节点称为ReactNode,它分为3种类型:ReactElement,ReactFragment,ReactText.
其中,ReactElement又分为ReactComponentElement和ReactDOMElement.

创建React元素
// 输入jsx
const app = ;

// 输出js
const app = React.createElement(
    Nav,
    {color: "blue"},
    React.createElement(Profile, null, "click")
);

通过jsx创建的虚拟元素最终会被编译成调用React的createElement方法

// createElement只是做了简单修正,返回一个ReactElement实例对象
// 也就是虚拟元素的实例
ReactElement.createElement = function(type, config, children) {
    // 初始化参数
    var propName;
    var props = {};
    var key = null;
    var ref = null;
    var self = null;
    var source = null;

    // 如果存在config,则提取里面的内容
    if (config != null) {
        ref = config.ref === undefined ? null : config.ref;
        key = config.key === undefined ? null : "" + config.key;
        self = config._self === undefined ? null : config._self;
        source = config._source === undefined ? null : config._source;
        // 复制config里的内容到props(id和className等)
        for (propName in config) {
            if (config.hasOwnProperty(propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
                props[propName] = config[propName];
            }
        }
    }

    // 处理children,全部挂载到props的children属性上,如果只有一个参数,直接赋值给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 < childrenLength; i++) {
            childArray[i] = arguments[i + 2];
        }
        props.children = childArray;
    }

    // 如果某个prop为空且存在默认的prop,则将默认prop赋给当前的prop
    if (type && type.defaultProps) {
        var defaultProps = type.defaultProps;
        for (propName in defaultProps) {
            if (typeof props[propName] === "undefined") {
                props[propName] = defaultProps[propName]
            }
        }
    }

    // 返回一个ReactElement实例对象
    return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
}
初始化组件入口

1.当使用React创建组件时,首先会调用instantiateReactComponent,这就是初始化组件的入口函数,
它通过判断node类型来区分不同组件的入口.

// 初始化组件入口
function instantiateReactComponent(node, parentCompositeType) {
    var instance;

    // 空组件 (ReactEmptyComponent)
    if (node === null || node === false) {
        instance = ReactEmptyComponent.create(instantiateReactComponent);
    }

    if (typeof node === "object") {
        var element = node;
        if (typeof element.type === "string") {
            // DOM标签 (ReactDOMComponent)
            instance = ReactNativeComponent.createInternalComponent(element);
        } else if (isInternalComponentType(element.type)) {
            // 不是字符串表示的自定义组件暂无法使用,此处将不做组件初始化操作
            instance = new element.type(element);
        } else {
            // 自定义组件
            instance = new ReactCompositeComponentWrapper();
        }
    } else if (typeof node === "string" || typeof node === "number") {
        // 字符串或数字
        instance = ReactNativeComponent.createInstanceForText(node);
    } else {
        // 不做处理
    }

    // 设置实例
    instance.construct(node);
    // 初始化参数
    instance._mountIndex = 0;
    instance._mountImage = null;

    return instance;
}
文本组件

1.当node类型为文本节点时是不算Virtual DOM元素的,但React为了保持渲染的一致性,
将其封装为文本组件ReactDOMTextComponent.

DOM标签组件

1.Virtual DOM模型涵盖了几乎所有的原生DOM标签,如

,

,等.
当开发者使用React时,此时的

并不是原生的
标签,他其实是React生成的
Virtual DOM对象,只不过标签名称相同罢了.

_createOpenTagMarkupAndPutListeners: function(transaction, props) {
    var ret = "<" + this._currentElement.type;
    // 拼凑出属性
    for (var propKey in props) {
        var propValue = props[propKey];

        if (registrationNameModules.hasOwnProperty(propKey)) {
            // 针对当前的节点添加事件代理
            if (propValue) {
                enqueuePutListener(this, propKey, propValue, transaction);
            }
        } else {
            if (propKey === STYLE) {
                if (propValue) {
                    // 合并样式
                    propValue = this._previousStyleCopy = Object.assign({}, props.style);
                }
                propValue = CSSPropertyOperations.createMarkupForStyles(propValue, this);
            }
            // 创建属性标识
            var markup = null;
            if (this._tag != null && isCustomComponent(this._tag, props)) {
                markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
            }
            if (markup) {
                ret += " " + markup;
            }
        }
    }
    // 对于静态页面,不需要设置react-id,这样可以节省大量字节
    if (transaction.renderToStaticMarkup) {
        return ret;
    }
    // 设置reactid
    if (!this._nativeParent) {
        ret += " " + DOMPropertyOperations.createMarkupForRoot();
    }
    ret += " " + DOMPropertyOperations.createMarkupForID(this._domID);

    return ret;
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84660.html

相关文章

  • 解读React源码(一):初探React源码

    摘要:前言的基本概念组件的构建方法以及高级用法这背后的一切如何运转深入内部的实现机制和原理初探源码代码组织结构包含一系列的工具方法插件包含一系列同构方法包含一些公用或常用方法如等包含一些测试方法等包含一些边界错误的测试用例是代码的核心部分它包含了 前言 React的基本概念,API,组件的构建方法以及高级用法,这背后的一切如何运转,深入Virtual DOM内部的实现机制和原理. 初探Rea...

    Eminjannn 评论0 收藏0
  • React 源码深度解读(一):首次DOM元素渲染 - Part 1

    摘要:调用栈是这样的这里生成的我们将其命名为,它将作为参数传入到。整个的调用栈是这样的组件间的层级结构是这样的到此为止,顶层对象已经构造完毕,下一步就是调用来自的方法,进行页面的渲染了。通过表达的结构最终会转化为一个纯对象,用于下一步的渲染。 欢迎关注我的公众号睿Talk,获取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言...

    daydream 评论0 收藏0
  • React 源码深度解读):首次 DOM 元素渲染 - Part 2

    摘要:本文将要讲解的调用栈是下面这个样子的平台无关相关如果看源码,我们会留意到很多相关的代码,我们暂时先将其忽略,会在后续的文章中进行讲解。现在我们来看一下各实例间的关系目前为止的调用栈平台无关相关下一篇讲解三总结本文讲解了转化为的过程。 欢迎关注我的公众号睿Talk,获取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 R...

    wean 评论0 收藏0
  • React 源码深度解读(九):单个元素更新

    摘要:作为声明式的框架,接管了所有页面更新相关的操作。是用于内部操作的实例,这里将它的初始化为空数组并插入一个新的。连续次后,期望的结果应该是。原因很简单,因为次的时候,取到的都是在完后不会同步更新。 前言 React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读其源码是一个非常艰辛的过程...

    kid143 评论0 收藏0
  • React 源码深度解读(十):Diff 算法详解

    摘要:的做法比较简单,它会先删除整个子树,然后再重新创建一遍。同样道理,当节点改为节点时,整棵子树也会被删掉,节点会重新创建。更新为和中较大的。到此为止,整个源码解读系列先告一段落了,后会有期。 欢迎关注我的公众号睿Talk,获取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 React 是一个十分庞大的库,由于要同时考虑...

    sutaking 评论0 收藏0

发表评论

0条评论

kuangcaibao

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<