深入浅出react---2.解读react源码

1.virtual dom模型

Virtual DOM之于react,就好比一个虚拟空间,react的所有工作几乎都是基于Virtual DOM完成的。其中,Virtual DOM模型负责Virtual DOM底层架构的构建工作,它拥有一整套的Virtual DOM标签,并负责虚拟节点及其属性的构建,更新,删除等工作。那么,

1.Virtual DOM模型到底是如何构建虚拟节点?

2.如何更新节点属性?

构建一套简易 Virtual DOM 模型并不复杂,它只需要具备一个 DOM 标签所需的基本 元素即可
:1.标签名  2.节点属性 ,包括样式,属性,事件等  3.子节点  4. 表识 id
示例代码如下:
{
  // 标签名
  tagName: 'div',
  // 属性
  properties: {
    style: {}
  },
  // 子节点
  children: [],
  // 唯一标识
  key: 1,
}

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

1.创建 React 元素

通过jsx创建的虚拟元素最终会编译成调用React 的 createElement 方法。那么,createElement 方法到底做了什呢?我们来解读相关源码(源码路径: /v15.0.0/src/isomorphic/classic/element/ReactElement.js):

Virtual DOM 模型通过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);
};

2.初始化组件入口

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

1.当node为空时,说明node不存在,则初始化空组ReactEmptyComponent.create(instantiateReactComponent)

2.当node类型为对象时,即是dom标签组件或自定义组件,那么如果element类型为字符串时,则初始化dom标签组件ReactNativeComponent.createInternalComponent (element),否则初始化自定义组件ReactCompositeComponentWrapper()。

3.当node类型为字符串/数字时,则初始化文本组件ReactNativeComponent.create InstanceForText(node)

4.如果是其他情况,则不做处理。

instantiateReactComponent方 法 的 源 码 如 下 ( 源 码 路 径 : /v15.0.0/src/renderers/shared/

reconciler/instantiateReactComponent.js):

// 初始化组件入口 var instance;
// 空组件(ReactEmptyComponent)
function instantiateReactComponent(node, parentCompositeType) {
  var instance;
  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 {
      // 自定义组件(ReactCompositeComponent)
      instance = new ReactCompositeComponentWrapper();
    }
  } else if (typeof node === 'string' || typeof node === 'number') {
    // 字符串或数字(ReactTextComponent)
    instance = ReactNativeComponent.createInstanceForText(node);
  } else {
    // 不做处理
  }
  // 设置实例 
  instance.construct(node);
  // 初始化参数
  instance._mountIndex = 0; instance._mountImage = null;
  return instance;
}

3.文本组件

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

在执行 mountComponent 方法时,ReactDOMTextComponent 通过 transaction.useCreateElement 判断该文本是否是通过 createElement 方法创建的节点,如果是,则为该节点创建相应的标签和标 识 domID,这样每个文本节点也能与其他 React 节点一样拥有自己的唯一标识,同时也拥有了 Virtual DOM diff 的权利。但如果不是通过 createElement 创建的文本,React 将不再为其创建 <span> 和 domID 标识,而是直接返回文本内容。

在执行 receiveComponent 方法时,可以通过 DOMChildrenOperations.replaceDelimitedText

(commentNodes[0], commentNodes[1], nextStringText) 来更新文本内容。

ReactDOMTextComponent 的源码(源码路径:/v15.0.0/src/renderers/dom/shared/ReactDOM-

TextComponent.js)如下:

// 创建文本组件,这是 ReactText,并不是 ReactElement
var ReactDOMTextComponent = function (text) {
  // 保存当前的字符串 
  this._currentElement = text;
  this._stringText = '' + text;
  // ReactDOMComponentTree 需要使用的参数 
  this._nativeNode = null;
  this._nativeParent = null;
  // 属性
  this._domID = null;
  this._mountIndex = 0;
  this._closingComment = null;
  this._commentNodes = null;
};
Object.assign(ReactDOMTextComponent.prototype, {
  mountComponent: function (transaction, nativeParent, nativeContainerInfo, context) {
    var domID = nativeContainerInfo._idCounter++;
    var openingValue = ' react-text: ' + domID + ' '; var closingValue = ' /react-text ';
    this._domID = domID;
    this._nativeParent = nativeParent;
    // 如果使用 createElement 创建文本标签,则该文本会带上标签和 domID 
    if (transaction.useCreateElement) {
      var ownerDocument = nativeContainerInfo._ownerDocument;
      var openingComment = ownerDocument.createComment(openingValue);
      var closingComment = ownerDocument.createComment(closingValue);
      var lazyTree = DOMLazyTree(ownerDocument.createDocumentFragment()); // 开始标签
      DOMLazyTree.queueChild(lazyTree, DOMLazyTree(openingComment));
      // 如果是文本类型,则创建文本节点
      if (this._stringText) {
        DOMLazyTree.queueChild(lazyTree, DOMLazyTree(ownerDocument.createTextNode(this._stringText)));
      }
      // 结束标签
      DOMLazyTree.queueChild(lazyTree, DOMLazyTree(closingComment)); ReactDOMComponentTree.precacheNode(this, openingComment);
      this._closingComment = closingComment;
      return lazyTree;
    } else {
      var escapedText = escapeTextContentForBrowser(this._stringText); // 静态页面下直接返回文本
      if (transaction.renderToStaticMarkup) {
        return escapedText;
      }
      // 如果不是通过 createElement 创建的文本,则将标签和属性注释掉,直接返回文本内容 
      return (
        '<!--' + openingValue + '-->' + escapedText +
        '<!--' + closingValue + '-->');
    }
  },


  // 更新文本内容
  receiveComponent: function (nextComponent, transaction) {
    if (nextText !== this._currentElement) {
      this._currentElement = nextText;
      var nextStringText = '' + nextText
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值