前提
React 17版本之前,都是利用createElement函数生成虚拟dom对象
jsx语法由babel解析后,然后通过createElement得到一个虚拟dom对象例如
babel解析是在webpack打包编译的时候解析的,然后将解析的代码在浏览器执行createElement函数的时候生成虚拟dom对象
<div className="test">zzzz<span>dddd</span></div>
---> babel解析 ----------------->
React.createElement("div", {
className: "test"
}, "zzzz", /*#__PURE__*/React.createElement("span", null, "dddd"));
----> React.createElement转为虚拟dom对象
const vdom = {
props: {
className: 'test',
children: "zzzzs",
// child可能是字符串也可能是数组
// children: ["zzzzs",{
// type: 'span',
// props: {
// 属性
// }
// }]
},
type: 'div'
}
React 17
实现react.CreateElement
function createElement (type, config, children) {
if(config) {
delete config.__self
delete config.__source
}
const props = {...config};
// 有多个子节点的时候
if (arguments.length > 3) {
// children 是一个数组
children = Array.prototype.slice.call(arguments, 2)
}
props.children = children;
return {
type,
props,
}
}
const React = {
createElement,
}
export default React;
react
react-dom
有一个render方法,将虚拟dom转为真实dom,然后挂载到容器上
render方法实现
根据虚拟dom创建真实dom
把虚拟dom的属性挂在到真实dom上
把虚拟dom的子节点也变成真实dom挂在到自己的dom上
将dom挂载到容器上
// 传入一个虚拟dom对象.和一个容器进行渲染
function render(vdom, container) {
// 将虚拟dom转为真实dom,进行渲染
const dom = createDom(vdom)
container.appendChild(dom)
}
// 将虚拟dom转为真实dom
function createDom(vdom) {
// 如果是数字或者字符串,直接返回一个真实到文本节点
if(typeof vdom === 'string' || typeof vdom === 'number') {
return document.createTextNode(vdom)
}
const { type, props } = vdom;
// 创建真实dom
const dom = document.createElement(type);
// 把虚拟dom属性更新到真实dom上,
updateProps(dom, props)
// 把虚拟dom的儿子也变成真实dom挂在到自己的dom上。
if (typeof props.children === 'string' || typeof props.children === 'number') {
dom.textContent = props.children
} else if (typeof props.children === 'object' && props.children.type) {
// 只是有一个子元素,并且儿子是虚拟dom,把儿子变成真实dom插入到dom上
render(props.children, dom)
} else if (Array.isArray(props.children)) {
// 如果children是一个数组的时候
updateArryChild(props.children, dom)
} else {
document.textContent = props.children ? props.children.toString() : ""
}
// 把真实dom作为一个dom属性放到虚拟dom上,为以后更新准备
// vdom.dom = dom;
return dom;
}
// 将虚拟dom的props属性挂在真实dom 上
/**
*
* @param {*} dom 真实dom
* @param {*} props 虚拟dom的属性
*/
function updateProps(dom, props) {
for (let key in props) {
if (key === 'children') continue;
if (key === 'style') {
let styleObj = props.style;
for (let attr in styleObj) {
dom.style[attr] = styleObj[attr]
}
} else {
dom[key] = props[key]
}
}
}
/**
*
* @param {*} childVdom 虚拟节点的儿子 child是一个数组的时候
* @param {*} parantdom 父节点
*/
function updateArryChild(childVdom, parantdom) {
for (let i = 0; i < childVdom.length; i++) {
let childVdoms = childVdom[i]
// 将虚拟节点挂在父节点上
render(childVdoms, parantdom)
}
}
const ReactDOM = {
render,
}
export default ReactDOM;