手写render和ceateElement

javascript/jquery

浏览数:96

2020-7-5

jsx语法

首先介绍jsx语法 , 其实jsx= javascript + xml,看上去是html其实不是.

  let element = <h1 id='myid'>hello world</h1>

通过bable 转义后变成

 let element= React.createElement('h1',{id:'myid'},'hello world')
 //这里我们称React元素,这是React最小单位

render渲染

写完React元素,就可以render渲染,就可以在页面中呈现了.

ReactDom.render(element,document.getELementById('root'))

那么我们按照源码开始再手写一下render和createElement

createElement

  1. createElement函数的目标是把传进来的(‘h1′,{id:’myid’},’hello world’)参数变成一个React元素

如图这个就是我们要的react元素

  function createElement(type,config={},children){
      let props={};
      for(let propsName in config){
        props[propsName]=config[propsName]
      };
      let childrenLength = arguments.length-2;
      if(childrenLength==1){
        props.children=children
      }else if( childrenLength >1){
        props.children=Array.from(arguments).slice(2)
      }
      let element={type,props}
      return element
  }

思路:

  1. 三个参数,参数1是我们的type可直接返回,参数2和参数3我们需要把它们塞进一个props对象里面
  2. 首先创建一个props空对象,遍历config,依次添加进props对象里
  3. 截取children长度,判断长度若是1说明是文本节点可直接添加,大于1则是一个数组,我们返回数组即可

render

render函数是把React元素(虚拟dom)渲染成html

  function render(element,parentNode){
  if(typeof element == 'string' || typeof element =='number'){
    return parentNode.appendChild(document.createTextNode(element))
  }
  let {type,props}=element;
  let domElement = document.createElement(type);
  for(let propsName in props){
      switch(propsName){
        case propsName=='className':
          return domElement.className=props[propsName];
        case propsName=='style':
          let styobj=  props[propsName]
          for(let attr in styobj){
            domElement.style[attr]=styobj[attr]
          }
          return
         case  propsName=='children':
          let children = Array.isArray(props.children)?props.children:[props.children]
          children.forEach(element=>{
            render(element,domElement)
          })
          return
          default:
            domElement.setAttribute(propsName,props[propsName])
      }
  }
  parentNode.appendChild(domElement)
}

思路:

  1. render函数2个参数分别为react元素,根组件,我们先判断react元素是不是string和number因为可以直接渲染比如像这样

    ReactDom.render('abc',document.getElementById('root'))
  2. 当参数1 是个react元素的时间,我们就得去做判断,首先我们可以分别解构出type,props,然后我们用type创建出元素,遍历props让type元素依次添加属性.
  3. 需要考虑特性情况如className,style就得去分别去做处理在添加,其他的直接用setAttribute添加进去.
  4. 最后通过根元素把我们创建出来的appendChild添加进去.

当我们元素是函数组件的时候

  function Welcome(props){
    return React.createElement('h1',{id:'zhufeng'},props.name,props.age)
  }
  ReactDOM.render(element, document.getElementById('root'));

我们得在render里面去添加逻辑,只需要把这个函数执行后就可,因为它的返回值还是我们的react元素.

function render(element,parentNode){
  if(typeof element == 'string' || typeof element =='number'){
    return parentNode.appendChild(document.createTextNode(element))
  }
     let {type,props}=element
    + if(typeof type == 'function'){
    + let newElemet=type(props)
    + type=newElemet.type;
    + props=newElemet.props;
  }
  //以下逻辑相同
}

当我们元素是类组件的时候

  class Welcome1 extends React.Components{
      render(){
        return React.createElement('h1',{id:'classwelcome'},this.props.name,this.props.age)
      }
  }

同样的思路,我们先去创建一个Components组件让它去继承.

  class Components{
    static isReactComponents=true
    constructor(props){
      this.props=props
    }
  }

这个组件我们只需要去传递props让子组件去继承就可以,另外isReactComponents这个静态属性是用来标识是否是类组件.
接下来我们继续改render函数

function render(element,parentNode){
  if(typeof element == 'string' || typeof element =='number'){
    return parentNode.appendChild(document.createTextNode(element))
  }
     let {type,props}=element
     + if(type.isReactComponents){
     +    let newElemet=new type(props).render()
     +     type=newElemet.type;
     +     props=newElemet.props;
     + }else if(typeof type == 'function'){
     +    let newElemet=type(props)
     +     type=newElemet.type;
     +     props=newElemet.props;
     + }
  }
  //以下逻辑相同
}

那么我们就可以顺利跑通啦.

作者:千城