4.初识React之组件渲染

191 阅读3分钟

React的组件渲染

React组件总共有两种,一种是Function组件,一种是Class组件,两种渲染方式不同

Function组件

编写函数式组件
function FunctionComponent(props) {
  return <h1 className='title' style={{ color: "red" }}>hello<span>word</span>{props.title}</h1>;
}

<FunctionComponent title="测试"/>
转化后代码
function FunctionComponent(props) {
  return /*#__PURE__*/React.createElement("h1", {
    className: "title",
    style: {
      color: "red"
    }
  }, "hello", /*#__PURE__*/React.createElement("span", null, "word"), props.title);
}

/*#__PURE__*/
React.createElement(FunctionComponent, {
  title: "test"
});

虚拟DOM

4-VDOM.png

分析
  • 注意React.createElement第一个参数依然是**“标签名”,但是这个时候没有双引号包裹,说明是一个变量,但是从虚拟DOM上来看,我们知道其实是一个方法,会赋值到type字段中,所以我们在createDOM方法中加上一个判断type是否为function**即可
  • 我们需要执行该方法,就可以得到组件元素
  • 然后通过前面的createDOM方法就又能递归渲染DOM元素了

改造createDOM方法

function createDOM(vdom) {
    // ...省略声明和结构
    if (type === REACT_TEXT) {
        dom = document.createTextNode(props.content);
    } else if (typeof type === "function") { 
        dom = mountFunctionComponent(vdom); 
    } else {
        dom = document.createElement(type);
    }
    // ...省略对子代处理
    return dom;
}

我们单独添加了一个else if,并判断是否为function

我们还是提取出一个mountFunctionComponent方法进行专门挂载Function组件

以上代码省略了部分,可以查看上一篇3.初识React之VDOM渲染

编写mountFunctionComponent方法

/**
 * 函数式组件处理方法
 * @param {*} vdom 虚拟dom
 * @returns 
 */
function mountFunctionComponent(vdom) { 
    let { props, type } = vdom;
    let renderVdom = type(props); 
    // 记录一下老的虚拟dom,后面组件更新的时候使用
    vdom.oldRenderVdom = renderVdom;
    return createDOM(renderVdom);
}

这里其实我们只是将type函数执行了一下,然后把返回的结果放入到createDOM中去了

并且记录了一下当前的DOM为旧的虚拟DOM了,后面就方便比较更新了

Class组件

其实和Function组件差不多,只是多了组件实例化和执行实例中的render方法这两步

编写Class组件
class ClassCompontent extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return <h1 className='title' style={{ color: "red" }}>hello<span>word</span> <h3>{this.props.title}</h3></h1>
  }
}


<ClassCompontent title="这个是什么" />
转化后代码
class ClassCompontent extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return /*#__PURE__*/React.createElement("h1", {
      className: "title",
      style: {
        color: "red"
      }
    }, "hello", /*#__PURE__*/React.createElement("span", null, "word"), " ", /*#__PURE__*/React.createElement("h3", null, this.props.title));
  }

}

/*#__PURE__*/
React.createElement(ClassCompontent, {
  title: "test"
});
虚拟DOM

4-classVDOM.png

分析
  • 看到转化后代码的代码后,我们发现和Function组件是一样的,但是在虚拟DOM中,我们发现type是一个class,我们只需要对这个class进行实例化即可
  • 我们使用typeofclass进行类型检测时发现还是function【这个是因为class本身就只是构造函数的语法糖而已】,所以我们还是在createDOMfunction分支中区分即可
  • 我们看到编写class组件的时候,我们会继承React.Component这个类,所以我们需要在React导出这个类,那么我们是不是可以将class组件function组件的标识放在这个类里呢?

react.js添加Component类

// ...省略createElement

class Component {
    // 标识这个是class组件
    static isReactComponent = true;
    constructor(props) {
        this.props = props;
    }
}

const React = {
    createElement,
    Component
}

export default React;

我们使用一个静态变量isReactComponent标识这个是一个class组件

改造createDOM方法

function createDOM(vdom) {
    
    // ...省略声明和结构
    if (type === REACT_TEXT) {
        dom = document.createTextNode(props.content);
    } else if (typeof type === "function") { 
        // class组件
        if(type.isReactComponent){
            dom = mountClassComponent(vdom);
        }else{
            dom = mountFunctionComponent(vdom);
        } 
    } else {
        dom = document.createElement(type);
    }
    
    // ...省略对子代处理
    return dom;
}
  • 我们通过type的静态变量isReactComponent的值来判断是否为class组件
  • 我们还是提取出一个mountClassComponent方法进行专门挂载Function组件

编写mountFunctionComponent方法

/**
 * class组件处理方法
 * 基本与function组件差不多
 * 需要先进行实例化
 * 然后执行实例中的render方法
 * @param {*} vdom 虚拟dom
 * @returns 
 */
 function mountClassComponent(vdom) { 
    let { props, type } = vdom;
    const componentInstance = new type(props);
    let renderVdom = componentInstance.render();
    // 记录一下老的虚拟dom,后面组件更新的时候使用
    vdom.oldRenderVdom = renderVdom;
    return createDOM(renderVdom);
}

这里和Function组件区别不大

  • 先获取type,并使用new关键字进行实例化【带上props】
  • 然后执行实例的render方法
  • 然后同Function组件一样的处理方式

到此,我们的组件渲染就算完成了