日常抄书之一次性搞懂React中Virtual DOM

一、Virtual DOM (虚拟DOM)

虚拟DOM就是React节点的对象树结构,React的工作原理就是基于虚拟DOM完成。这个对象树结构中每个节点的内容都包含了一个原生DOM所具有的属性。如:标签名称,属性,子节点,id等。如下这种模式:

{
    tagName: '',
    properties: {
        
    },
    children: [],
    key: ''
}
复制代码

在React中虚拟DOM称为ReactNode。它有三种类型:ReactElementReactFragmentReactTextReactElement又分为ReactComponentElementReactDOMElement

//ReactNode不用类型节点所需的基础元素
type ReactNode = ReactElement | ReactFragment | ReactText
type ReactElement = ReactDOMElement | ReactComponentElement
type ReactDOMElemnt = {
    type: string,
    props: {
        children: ReactNodeList,
        className: string,
        style: object,
        ...etc
    }
    key: string | boolean | number | null,
    ref: string | null
}
type ReactComponentElement<TProps> = {
    type: ReactClass<Tprops>,
    props: TProps,
    key; string | boolean | number | null,
    ref: string | null
}
type ReactFragment = Array<ReactNode | ReactEmpty>
type ReactNodeList = ReactNode | ReactEmpty
type ReactText = string | number
type ReactEmpty = null | undefined | boolean
复制代码

创建一个React元素

创建一个React元素,它调用的是React.createElement方法。那么这个方法做了什么? 看一下JSX和编译后的JavaScript文件:

const app = <Nav ref="nav" key="1" color="blue"><Profile>click</Profile>我是文本</Nav>;
const app = React.createElement(
    Nav,
    { 
        ref: 'nav',
        key: 1,
        color: 'blue' 
        
    },
    React.createElement(Profile, {}, "click" )
)
复制代码

通过JSX创建的虚拟元素最终会编译成React的createElement方法。 源码目录在此:

我们把复制出来看一下,顺便加点注释:

//react中createElement方法来源于 ReactElement.js

//createElement类似一个工厂方法,只是做了简单的参数修正,返回一个ReactElement实例对象。
/**
* 传入了如下参数:
* type: "Nav"
* config: { ref: "nav", key: "1" }
* children:  1.react.createElement(...)
*            2.我是文本
*            
*/
export function createElement(type, config, children) {

  //可以看到这几个声明的变量就是一个初始化参数的作用
  let propName;
  const props = {};
  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  //这里判断如果config不为空的话则提取config上的内容,赋值给前面初始化的变量
  if (config != null) {
    if (hasValidRef(config)) {
      ref = config.ref;  //将config上的ref赋值给ref
    }
    if (hasValidKey(config)) {
      key = '' + config.key; //将config上的key赋值给key
    }
    //将config上的self赋值给self
    self = config.__self === undefined ? null : config.__self; 
    //将config上的source赋值给source
    source = config.__source === undefined ? null : config.__source; 
    //下面的循环是将config上的内容复制到props上
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
  }

  //这里是对children的操作,如果childrenLength为1,那么表示只有一个children,那么就直接赋值给props的children属性
  //如果childrenLength大于1,则表示有多个children,那么要做合并操作,将他们放到一个数组里面。然后赋值给props的children属性
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }

  // Resolve default props
  //这个type是创建的标签名称
  //这里处理默认的props,如果存在默认的props,则将默认的props赋值给当前的props
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  //最后返回一个ReactElement实例对象
  return ReactElement(
    type, //这个type代表的是组件名
    key, 
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}
复制代码

ReactCurrentOwner.current是个啥子东西?

const ReactCurrentOwner = {
  /**
   * @internal
   * @type {ReactComponent}
   */
  current: (null: null | Fiber),
  currentDispatcher: (null: null | Dispatcher),
};

// 实际上这个current初始时是null,类型可以是Fiber或null
复制代码

最后返回的是一个ReactElement实例对象,那么这个个ReactElement主要做了些什么呢?

//简化一下,发现最后返回的是这个element对象。这就是最终的虚拟DOM
const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    //这个标志表示这个元素是一个React Element 
    $$typeof: REACT_ELEMENT_TYPE,

    //将属性注入到元素上
    type: type,
    key: key,
    ref: ref,
    props: props,

    // 记录创建此元素的组建
    _owner: owner,
  };

  return element;
};
复制代码

可以看到这个方法最后返回的是一个对象,这个对象结构对应着创建一个DOM的条件。这就是创建了一个ReactNode。发现也是很简单的吧。哈哈。

二、小知识点

在使用React创建组件之前我们还有一个操作,就是初始化组建入口。通过判断不同node类型来区分不同组件的入口。通过调用instantiateReactComponent方法进行初始化。

源码目录在此(v15.0.0版本)

把代码复制出来看下:


// Given a ReactNode, create an instance that will actually be mounted
// @param {ReactNode} node //参数是一个ReactNode
 
 //参数是传一个虚拟DOM节点
function instantiateReactComponent(node) { 
  var instance;

  //如果ReactNode是一个空组件
  //通过ReactEmptyComponent.create初始化一个空组件
  if (node === null || node === false) {
    instance = ReactEmptyComponent.create(instantiateReactComponent);
  //如果ReactNode是一个对象(DOM标签或者自定义组件)
  } else if (typeof node === 'object') {
    var element = node;
    invariant(
      element && (typeof element.type === 'function' ||
                  typeof element.type === 'string'),
      'Element type is invalid: expected a string (for built-in components) ' +
      'or a class/function (for composite components) but got: %s.%s',
      element.type == null ? element.type : typeof element.type,
      getDeclarationErrorAddendum(element._owner)
    );

   //如果element的type类型为string
   //则调用ReactNativeComponent.createInternalComponent
    if (typeof element.type === 'string') {
      instance = ReactNativeComponent.createInternalComponent(element);
    } else if (isInternalComponentType(element.type)) {
      instance = new element.type(element);
    //否则初始化自定义组件
    } else {
      instance = new ReactCompositeComponentWrapper(element);
    }
  //如果ReactNode是一个字符串或者数字
  //那么调用ReactNativeComponent.createInstanceForText
  } else if (typeof node === 'string' || typeof node === 'number') {
    instance = ReactNativeComponent.createInstanceForText(node);
  } else {
    invariant(
      false,
      'Encountered invalid React node of type %s',
      typeof node
    );
  }
  instance._mountIndex = 0;
  instance._mountImage = null;


  return instance;
}
复制代码

转载于:https://juejin.im/post/5c812273f265da2dc675e6cc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值