React源码系列-render()方法

119 阅读2分钟

前言

    上一篇文章讲了怎样在react中创建出虚拟dom,这一篇讲的是将虚拟dom转换为真实dom,
并在页面中渲染出来

怎样实现

    说白了,render方法本质上就是将vdom对象中的属性值通过js语法生成对应的html标签,
然后将标签通过appendChild方法挂载到页面已经声明好的元素上面

代码实现

   首先我们来看一下上一篇我们定义的createElement方法生成的vdom对象和render方法
    {
      $$typeof: Symbol(react.element),
      key: null,
      props: {
        children: {type: Symbol("react.TEXT"), content: 'zhangsan'},
        className: "title",
        style: {color: '#fff'},
      },
      type: "h3",
      ref: null,
    }
    
    ReactDOM.render(
      element,  // vdom
      document.getElementById('root')
    )
接下来,我们就来用代码来实现他,先来定义一个方法
    function render (vdom, container) {
      // 1. 将 vdom 转换为 真实dom
      let newDOM = createDOM(vdom)
      // 2. 将真实dom放到对应的位置
      container.appendChild(newDOM)
    }
第一步很好理解,我们接下来主要实现的就是createDOM 这个函数
    function createDOM (vdom) {
      // vdom => 真实dom
      let { type, props } = vdom
      let dom;
      // 判断 type 
      /* 
         这一个判断是 我们在标签中props.children的时候会重新调用createDOM方法
         这时vdom为{type: Symbol("react.TEXT"), content: 'zhangsan'}这样的结构
      */
      if (type === Symbol("react.TEXT")) {  // 文本
        dom = document.createTextNode(vdom.content)
      } else {   // 元素
        // 第一次type为标签 div,直接创建出来
        dom = document.createElement(type)
      }
      
      // 最后将我们生成的真实dom返回出来 
      return dom
    }
通过现在的代码我们可以生成一个标签,下面我们只需要在标签内填充内容即可
    function createDOM (vdom) {
      // vdom => 真实dom
      let { type, props } = vdom
      let dom;
      // 1. 判断 type 
      if (type === Symbol("react.TEXT")) { 
        dom = document.createTextNode(vdom.content)
      } else {  
        dom = document.createElement(type)
      }
      
      // 2. 处理属性
      if (props) {
        // 这个函数用来为标签添加 style key等属性
        updateProps(dom, props) 
        
        // 3. 添加 children
        let children = props.children
        if (children) {
          // 这个函数用来填充文本内容
          changeChildren(children, dom)
        }
      }
      
      // 最后将我们生成的真实dom返回出来 
      return dom
    }
首先来实现 updateProps 函数
// 更新属性
function updateProps (dom, newProps) {
  if (newProps) {
    for (let key in newProps) {
      // children属性 我们这里不需要处理,跳过
      if (key === 'children') {
        continue
      } else if (key === 'style') {  // 为标签添加样式
        let styleObject = newProps[key]
        for (let styKey in styleObject) {
          // 为dom添加样式
          dom.style[styKey] = styleObject[styKey]
        }
      } else {  // 其他属性
        dom[key] = newProps[key]
      }
    }
  }
  
}
接下来就是 changeChildren 方法
    // 处理children
    function changeChildren (children, dom) {
      // 一个children {}
      if (typeof children === 'object' && children.type) {
        // 这里传入的children 结构为{type: Symbol("react.TEXT"), content: 'zhangsan'}
        // 这里再重新调用createDOM方法,就会进行文本的渲染
        createDOM(children, dom)
      } else if (Array.isArray(children)) {  // 多个为数组 [{}, {}]
        children.forEach(item => createDOM(item, dom))
      }
    }

至此,我们的 render 方法就定义好啦,去调用一下

    render(
      React.createElement("h3", { style: { color: 'blue' } }, "hello"),
      document.getElementById('root')
    )

这里就是今天的内容了,感谢观看