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
分析
- 注意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
分析
- 看到转化后代码的代码后,我们发现和Function组件是一样的,但是在虚拟DOM中,我们发现
type
是一个class
,我们只需要对这个class
进行实例化即可 - 我们使用typeof对class进行类型检测时发现还是function【这个是因为class本身就只是构造函数的语法糖而已】,所以我们还是在
createDOM
的function分支
中区分即可 - 我们看到编写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组件一样的处理方式
到此,我们的组件渲染就算完成了