react系列-元素渲染

118 阅读1分钟

背景

我们平时习惯写react,可能比较少的关注是怎么渲染的,比如你写了一个组件,这个组件是怎么被渲染成dom元素显示的。

例子

const element = <h1 style={{color: 'red'}}>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));

babel编译完以后生成一个react节点对象

image.png

属性介绍

$$typeof

节点标识,简单说就是预防xss漏洞,因为Symbol无法被序列化【后端直接返回json给前端渲染】。

const ele = {
    type: "div",
    props: {
      dangerouslySetInnerHTML: {
        __html: '<img src="x" onerror="alert(1)">'
      },
    },
    // $$typeof: Symbol.for('react.element'),
    ref: null
  }
ReactDOM.render(ele, document.getElementById('root'));

如果没有$$typeof属性,会直接报错,运行效果如下:

image.png 现在直接加上$$typeof:

const ele = {
    type: "div",
    props: {
      dangerouslySetInnerHTML: {
        __html: '<img src="x" onerror="alert(1)">'
      },
    },
    $$typeof: Symbol.for('react.element'),
    ref: null
  }
ReactDOM.render(ele, document.getElementById('root'));

运行效果如下:

image.png

key

react节点唯一标识符,方便节点做diff,并进行节点复用,减少不必要的dom渲染,这里为null是没有传key的值,对于非list节点,变动不大,可以直接进行逐层对比,官方并没有强制每一个元素都传key,也是这个原因。

props

节点上包含的所有属性,包括字节点,方便后续渲染。

ref

dom元素上使用,就是为了获取当前的dom元素

const inputRef = useRef();
<input ref={inputRef} /> // inputRef来访问input元素

在自定义组件上使用,获取相关的实力

type

节点类型,代表是h1节点

完成渲染

reactfiber怎么进行diff等操作,节点怎么挂载,我们先忽略,直接通过虚拟dom怎么生成元素

创建节点
function createElement (  
  type: string,
  props: Object,
  rootContainerElement: Element | Document,
  parentNamespace: string) {
    // ....
    // type指的是h1节点
    domElement = ownerDocument.createElement(type);
    // ....
    return domElement;
}


添加节点属性

function setInitialDOMProperties(
  domElement: Element,
  tag: string,
  rawProps: Object,
  rootContainerElement: Element | Document) {
  // ...
  // 设置dom属性
  setInitialDOMProperties(    
    tag,
    domElement,
    rootContainerElement,
    props,
    isCustomComponentTag)
  // ...
  }

渲染成dom树

function appendChildToContainer( 
  container: Container,
  child: Instance | TextInstance,) {
    if (container.nodeType === COMMENT_NODE) {
    parentNode = (container.parentNode: any);
    parentNode.insertBefore(child, container);
  } else {
    parentNode = container;
    parentNode.appendChild(child);
  }
}

执行后 生成最终的dom