ReactDOM.render的渲染原理
在react项目中,之所以可以在函数/组件中直接写模板结构,是因为最后babel都会帮我们把这些模板转译成 React.createElemen(config) 的形式,这也就是为什么我们在每一个组件中明明没有主动调用React,但是却要引入react的原因。
// 举例
import React from 'react';
import ReactDOM from 'react-dom';
let h = React.createElement(
'div',{className:'box',style:{fontSize:'30px', color:'green'}},'第一个儿子',
React.createElement('h2',{style:{color:'red',textAlign: 'center'}},'第二个儿子')
);
let p = <div className='box' style={{fontSize:'30px', color:'green'}}>
第一个儿子
<h2 style={{color:'red',textAlign: 'center'}}>第二个儿子</h2>
</div>
ReactDOM.render(<>
{h}
{p}
</>,
document.getElementById('root'));
// p和h渲染的页面是一样的
渲染结果:
简单实现 React.createElement 和 ReactDOM.render 的功能
let React = {
createElement(type, attr, ...children) {
// type 是对应的标签名
// attr 是标签内的一些行内属性
// children 是type标签元素的后代元素
let el = document.createElement(type);
return new Element(type, attr, children);
}
}
let ReactDOM = {
render(ele, container) {
container.appendChild(ele.render());
}
}
function Element(el, attr, children) {
this.el = el;
this.attr = attr;
this.children = children;
}
Element.prototype = {
constructor: Element,
render() {
// 负责把虚拟DOM变成真实的DOM
let el = document.createElement(this.el);
// 把行内属性放到el中
let keys = Object.keys(this.attr);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
// 不能直接放,需要对特殊的行内属性进行处理
switch (key) {
case 'className':
el.setAttribute('class', this.attr[key]);
break;
case 'htmlFor':
el.setAttribute('for', this.attr[key]);
break;
case 'style':
let str = '';
Object.keys(this.attr[key]).forEach(item=>{
str += `${this.changeStyle(item)}: ${this.attr[key][item]};`
})
el.setAttribute('style', str);
break;
default:
el.setAttribute(key, this.attr[key]);
}
}
this.children.forEach(item => {
// item 可能是一个字符串,也有可能是一个虚拟DOM
// 如果是字符串,则之间插入文本节点;如果是虚拟DOM,则需要先转为真实DOM再插入到el中
let text;
if (typeof item === 'string') {
text = document.createTextNode(item);
} else {
text = item.render();
}
el.appendChild(text);
});
return el;
},
changeStyle(str) {
// 正则匹配:把属性中的驼峰命名转变成串式命名:例如 fontSize -> font-size
return str.replace(/[A-Z]/g, b => {
return '-' + b.toLowerCase();
}).replace(/^-/, '');
}
}
let h2 = React.createElement('div', {
className: 'h1box',
style: {
fontSize: '30px',
}
},
'呵呵呵', // 和Vue不同,react的这里是如果有多个子元素,就直接往后接着写(Vue是多个子元素就用数组包起来)
React.createElement('h2', {
className: 'h2',
style: {
color: 'red'
}
}, '哈哈哈')
)
ReactDOM.render(h2, document.getElementById('root'))