render方法的源码,将虚拟DOM转为真实DOM
函数式组件
特点
- 函数组件的名称首字母大写
- 函数式组件的返回值 => jsx
- jsx是一个父子组件
- 具有props属性
定义并使用函数式组件
function FunctionComponent (props){
return <div>{props.name}</div>
}
const element = <FunctionComponent name={'100'}></FunctionComponent
babel转译后的代码
"use strict";
function FunctionComponent(props) {
return /*#__PURE__*/React.createElement("div", null, props.name);
}
const element = /*#__PURE__*/React.createElement(FunctionComponent, {
name: '100'
});
打印element
$$typeof: Symbol(react.element)
key: null
props: {name: '100'}
ref: null
type: ƒ FunctionComponent(props)
_owner: null
_store: {validated: false}
_self: null
_source: null
发现
我们可以看到element
的type
不再是div
等标签,而是一个函数
获取在父组件下的完整虚拟dom
function mountFunctionComponent (vdom) {
// type为一个函数
let {type,props} = vdom
// 将子组件的的props传入到父组件中,得到一个完整的虚拟dom
let functionVdom = type(props)
return functionVdom
}
类组件
区别类组件和函数组件
vdom.type.prototype.isReactComponent
完整代码
/**
* children为文本 { type: 'div', props: { className: 'title', style: { color: '#f69' }, children: 'one node' }, };
* children为一个元素 { type: 'div', props: { children: { type: 'span', props: { children: 'one child node' }, }, }, }
*/
/**
* 初始化react元素
* @param {*} vdom 虚拟DOM
* @param {*} container 位置(容器)
*/
function render(vdom, container) {
// 本质就是挂载,因container本身存在于文档之中,所以挂载操作会触发渲染
mount(vdom, container)
}
// 将第一个参数变成真实DOM,插入到第二个参数挂载元素上
function mount(vdom, container) {
const DOM = createDOM(vdom);
container.append(DOM);
}
// 创建真实DOM
function createDOM(vdom) {
// 是否为 文本 或 无内容
if(typeof vdom === 'string' || typeof vdom === null){
return document.createTextNode(vdom || '')
} else if (typeof vdom.type === 'function'){
if(vdom.type.prototype.isReactComponent){
return mountClassComponent(vdom)
}
return mountFunctionComponent(vdom)
}else {
return createElementDOM(vdom)
}
function createElementDOM(vdom) {
const { type, props } = vdom;
let DOM = document.createElement(type); // 创建节点
// 处理props
if (props) {
updateProps(DOM, props);
const { children } = props;
children && updateChildren(DOM, children);
}
return DOM;
}
}
// 处理函数式组件
function mountFunctionComponent(vdom) {
// type为一个函数
let {type,props} = vdom
// 将子组件的的props传入到父组件中,得到一个完整的虚拟dom
let functionVdom = type(props)
return createDOM(functionVdom)
}
// 处理类组件
function mountClassComponent(vdom) {
let {type,props} = vdom
// 注意 type 是一个类
let classInstance = new type(props)
// 得到虚拟dom
let classVnode = classInstance.render()
return createDOM(classVnode)
}
// 将属性(除了children)放置标签(DOM)中
function updateProps(DOM, props) {
for (const key in props) {
if (key === 'children') continue;
if (key === 'style') {
// 添加样式
updateStyle(DOM, props[key]);
continue;
}
// 添加其他属性,如ref和自定义属性
DOM[key] = props[key];
}
function updateStyle(DOM, styleObj) {
for (const key in styleObj) {
DOM.style[key] = styleObj[key];
}
}
}
function updateChildren(DOM, children) {
// 单个节点,直接插入(挂载)到DOM上; 多个节点,遍历插入
const isOneChildren = !Array.isArray(children);
isOneChildren
? mount(children, DOM)
: children.forEach((child) => mount(child, DOM));
}