手写MiniReact(2)—— 实现渲染类式组件和函数式组件

97 阅读2分钟

上一篇中,已经可以渲染原生的组件到页面,本节实现类式组件和函数式组件的渲染。首先使用官方的React框架,分别打印出类式组件和函数式组件的虚拟DOM。

class Demo1 extends React.Component{       //类式组件的Demo
  render(){
    return (
      <div style={{color:"green"}} className="title" ref={c=>this.root =c} >
        <h2>this is class component</h2>
      </div>
    )
  }
}

function Demo2(){          //函数是组件的Demo
  return (
    <div style={{color:"red"}}>
      <h2>this is function component</h2>
    </div>
  )
}

下面是类式组件的虚拟DOM, 可以看到type属性保存的就是Demo1这个类本身 image.png

继续把type属性展开,可以看到,render()函数的返回值,也就是返回的JSX的部分,被babel编译了,调用了createElement方法

image.png

因此对于类式组件,需要创建类的实例对象,然后调用render方法,该方法的返回值才是真正的虚拟DOM

同理,下面是函数式组件的虚拟DOM,可以看到type属性保存的就是Demo2这个类本身

image.png

查看type属性

image.png

对于函数式组件,该函数的返回值就是JSX表达式,才是真正的虚拟DOM

react-dom 文件中的写法

function render(VDOM, container){
    let DOM = createDOM(VDOM)
    container.appendChild(DOM)
}


function createDOM(VDOM){

    if(Object(VDOM) !== VDOM){
        return document.createTextNode(VDOM)
    }

    let {type, props} = VDOM

    let DOM =null
    
    if(typeof type === "function"){          //新增部分
        if(type.isClassComponent){          //判断是类式组件还是函数式组件,分别处理
            return handleClassComponent(VDOM)
        }
        return handleFunctionComponent(VDOM)
    }else{
        DOM = document.createElement(type)
    } 
    
    updateProps(DOM, null, props)

    let {children} = props
    if(children){
        handleChildren(DOM, children)
    }

    return DOM
}

function handleClassComponent(VDOM){     //对于类式组件
    let {type, props} = VDOM
    let classInstance = new type(props)
    let realVDOM = classInstance.render()    //render()的返回值才是JSX,也就是虚拟DOM
    return createDOM(realVDOM)
}


function handleFunctionComponent(VDOM){
    let {type, props} = VDOM
    let realVDOM = type(props)       //函数执行的返回值就是虚拟DOM
    return createDOM(realVDOM)
}


function updateProps(DOM, oldProps, newProps){     //props:{chilren, className, style, onXXX}
    if(newProps){
        for(let key in newProps){
            if(key === "children"){
                continue
            }else if(key === "style"){
                let styleObject = newProps[key]
                for(let item in styleObject){
                    DOM.style[item] = styleObject[item]
                }
    
            }else if(key.startsWith("on")){
                DOM[key.toLocaleLowerCase()] = newProps[key]
            }else{
                DOM[key] = newProps[key]
            }
        }
    }
    

    if(oldProps){
        for(let key in oldProps){
            if(!newProps[key]) DOM[key] = null
        }
    }
}

function handleChildren(DOM, children){
    if(children instanceof Array){
        children.forEach(child => render(child, DOM) )
    }else{
        render(children, DOM)
    }
}


const ReactDOM = {
    render
}

export default ReactDOM

创建Component.js文件,用来编写Component这个父类

class Component {
    static isClassComponent = true    //静态属性,表示这个是类式组件
    
    constructor(props){
        this.props = props
    }
}

export default Component

在react.js文件中,引入Component

import { REACT_ELEMENT } from "./constant"
import Component from "./component"

function createElement(type, config, children){           // 标签类别  config   标签内容
    let key = null, ref = null

    if(config){
        key = config.key
        ref = config.ref
        delete config.key
        delete config.ref
    }

    let props = {...config}

    if(arguments.length >= 4){
        props.children = Array.from(arguments).slice(2)
    }else if(arguments.length == 3){
        props.children = children
    }


    return {
        $$typeof: REACT_ELEMENT,
        type,
        ref,
        key,
        props,
    }
}

const React = {
    createElement,
    Component       //class Xyyy extends React.Component
}

export default React

下图表示目前的文件结构

image.png

这样就可以渲染类式组件和函数式组件了