我们上一节了解了 react
的虚拟 dom
的格式,如何把虚拟 dom
转为真实 dom
进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调用函数返回虚拟 dom
,一个是调用实例的 render
方法,返回虚拟 dom
,进而转换为真实 dom
,本小节我们了解一下具体的实现原理。
函数式组件
特点
- 函数组件接受一个单一的
props
对象并返回一个react
元素 - 组件以大写字母开头(内部判断是原生还是自定义)
- 组件必须在使用时定义或引用
- 组件返回值只能有一个根元素(便于树遍历)
React
元素不但可以使dom标签,也可以是用户自定义的组件- 当
react
元素为用户自定义组件时,他会将jsx
接收的属性转换为单个对象换递给组件,即props
(babel 处理的)
使用
代码语言:txt复制// index.js
function FunctionComponent(props) {
let jsx = (
<h1 className="title" style={{ color: "red", backgroundColor: "pink" }}>
hello
<span>111</span>
</h1>
);
return jsx;
}
const el = <FunctionComponent msg='world' />;
console.log(el);
ReactDOM.render(el, document.getElementById("root"));
我们可以看到,函数组件返回的虚拟 dom
类型是函数类型,我们要修改我们的 createDOM
方法,实现函数式
实现
修改 createDOM
方法
// react-dom.js
...
if (typeof type === 'function') { // 上面截图我们看到函数组件的类型是个函数
return mountFunctionComponent(vdom)
}
...
function mountFunctionComponent(vdom) {
// 这里是 babel 编译时把我们写的属性转为了 props 对象形式
const {type, props} = vdom
const renderVdom = type(props)
return createDOM(renderVdom)
}
入口文件改为自己的文件,打印如下:
类组件
react hooks
出现之前,想实现组件内容变化做不到,定义状态并改变状态只能使用类组件的方式。
使用
代码语言:txt复制// index.js
class ClassComponent extends React.Component {
render() {
// 返回react 元素
let jsx = (
<h1 className="title" style={{ color: "red", backgroundColor: "pink" }}>
hello
<span>{this.props.msg}</span>
</h1>
);
return jsx;
}
}
const el = <ClassComponent msg="world" />;
console.log(el);
ReactDOM.render(el, document.getElementById("root"));
通过打印我们可以看到,类组件的类型是一个类。而且需要继承 React
对象的 Component
属性
实现
我们知道 javascript
本身是没有类的,es6 的类转化之后也会变成函数,这就会跟函数式组件的类型判断冲突,所以我们需要给类加上标识符
// react.js
// 我们逆推
const React = {
createElement,
Component
}
因为可以被继承,所以 Component
也是一个类
// src/Component.js
export class Component {
static isReactComponent = REACT_COMPONENT; // 区分标识
constructor(props) {
this.props = props;
}
}
// constants.js
export const REACT_COMPONENT = Symbol("react.component"); // Symbol 独一无二的 无法覆盖 无法伪造
我们改造一下 createDOM
方法
if (typeof type === "function") {
// 类也是个函数
if (type.isReactComponent === REACT_COMPONENT) {
return mountClassComponent(vdom);
}
// 函数组件
return mountFunctionComponent(vdom);
}
// 实现 mountClassComponent
function mountClassComponent(vdom) {
const {type, props} = vdom
const classInstance = new type(props) // 从打印中我们知道 type 是个类,可以被实例化
const renderVdom = classInstance.render()
return createDOM(renderVdom) // 虚拟dom转为真实dom
}
我们可以看到,无论是函数式组件还是类组件,本质都是返回处理的虚拟 dom
,也就是基于上一小节的知识。这两个实现还不算复杂,跟着小编简单写写就可以掌握。下一小节我们学习下组件的更新机制,如果有不对,欢迎指正!