Appearance
实现函数组件
本节目标:理解“实现函数组件”的核心思路,并能把它用于实际开发或面试表达。
src\index.js
import React from "./react";
import ReactDOM from "./react-dom";
function FunctionComponent(props) {
return (
<div
className="title"
style={{ color: 'red' }}
>
<span>{props.name}</span>
{props.children}
</div>
);
}
let element = <FunctionComponent name="hello">world</FunctionComponent>;
ReactDOM.render(element, document.getElementById("root"));由于函数组件是通过函数声明或表达式声明的,又加上名字为大写的约定,所以babel在分析jsx时,就会将type值设置为这个变量所代表函数,这时typeof这个变量肯定是函数; 修改
src\react-dom.js
import { REACT_TEXT } from "./constants";function render(vdom, container) {
mount(vdom, container);
}
export function mount(vdom, container) {
let newDOM = createDOM(vdom);
container.appendChild(newDOM);
}在createDOM的时候多加一个判断,即type为function时,进行特殊处理 /**
- 把虚拟DOM转成真实DOM */ export function createDOM(vdom) { if (!vdom) return null; let { type, props } = vdom; let dom;//真实DOM if (type === REACT_TEXT) {//如果这个元素是一个文本的话 dom = document.createTextNode(props.content); } ==else== ==if== ==(====typeof== ==type== ======= =='function'====) {====//====如果类型是一个函数的话== ==if== ==(====type====.====isReactComponent====) {====//====说明它是一个类组件== ==return== ==mountClassComponent====(====vdom====);== ==}== ==else== =={== ==return== ==mountFunctionComponent====(====vdom====);== ==}== ==}== else { dom = document.createElement(type);// div span p } //处理属性 if (props) { updateProps(dom, {}, props); if (props.children) { let children = props.children; if (typeof children === 'object' && children.type) {//说明这是一个React元素 mount(children, dom); } else if (Array.isArray(children)) { reconcileChildren(props.children, dom); } } } vdom.dom = dom;//让虚拟DOM的dom属性指向这个虚拟DOM对应的真实DOM return dom; }
主要是做以下事件:
- 调用函数,并传入
props值,这样取得函数的返回值; - 这个函数的返回值就是函数组件的
vdom; - 将
vdom以属性名oldRenderVdom挂靠到函数组件自身的vdom上; - 使用函数组件返回的
renderVdom创建真实dom,并将其返回; - 这个返回的
dom会被挂载到父dom下;
function mountFunctionComponent(vdom) {
let { type, props } = vdom;
let renderVdom = type(props);
vdom.oldRenderVdom = renderVdom;
return createDOM(renderVdom);
}
function updateProps(dom, oldProps, newProps) {
for (let key in newProps) {
if (key === "children") {
continue;
}
if (key === "style") {
let style = newProps[key];
for (let attr in style) {
dom.style[attr] = style[attr];
}
} else {
dom[key] = newProps[key];
}
}
}
function reconcileChildren(childrenVdom, parentDOM) {
for (let i = 0; i < childrenVdom.length; i) {
let childVdom = childrenVdom[i];
mount(childVdom, parentDOM);
}
}
const ReactDOM = {
render,
};
export default ReactDOM;