一、元素渲染
1.1 React元素
元素是组成React应用的最小单位,与浏览器的DOM元素不同,React元素是通过解析JSX生成的普通对象(virtual dom),被用来模拟真实DOM。而React可以以极低的开销通过元素来创建真实的DOM,其中ReactDOM就是用来负责渲染和更新DOM的。
1.2 元素的渲染原理
元素的渲染原理就是将
JSX转换成vdom,再经过一系列的操作生成真实DOM,再将真实DOM插入容器中。

值得注意的是
React元素是不可变对象,也就代表着一旦创建就无法更改元素。像动画的单独一帧,只能代表一个特定的时间点。(React17之前这只是官方约定此对象不可变,在React17之后,源码中在开发环境中直接将整个React元素给Object.freeze()了)
二、源码实现
2.1 createElement
createElement是用来将JSX转换成vdom的方法
children的处理
- 传入多个儿子,将
props.children处理成数组 - 传入一个儿子,将
props.children处理成对象 - 没有传入儿子,将
props.children就是undefined
function createElement(type,config,children){
let props = {...config};
if(argument.length > 3){
children = Array.prototype.slice.call(arguments,2).map(warpToVdom)
}else{
if(typeof children !== 'undefined'){
props.children = warpToVdom(children);
}
}
return {
type,
props
}
}
// React的源码对于文本和数字有做特殊,这里方便理解简单,统一将它包装成vdom(源码没有这一步,只是方便写伪代码)
export const REACT_TEXT = Symbol('REACT_TEXT');
function warpToVdom(element){
if(typeof element === 'string' || typeof element === 'number'){
return {
type: REACT_TEXT,
props:{
content:element
}
}
}else{
return element;
}
}
复制代码
2.2 ReactDOM
vdom渲染的方法统一放在
ReactDOM中
2.2.1 render
render函数很好理解,就是将vdom创建成真实DOM,再将真实DOM插入容器中
function render(vdom,container){
let newDOM = createDOM(vdom);
container.appendChild(newDOM);
}
复制代码
2.2.2 createDOM 创建真实DOM
- 根据
vdom的 type 创建真实元素 - 再根据
props更新真实元素的属性 - 最后通过
props.children来判断是否递归执行render
function createDOM(vdom){
let {type,props} = vdom;
let dom;
// 1. 创建DOM
if(type === REACT_TEXT){
dom = document.createTextNode(props.content);
}else{
dom = document.createElement(props.content);
}
if(props){
// 2. 更新属性
updateProps(dom,{},props);
// 3. 递归渲染
if(typeof props.children == 'object' && props.children.type){
render(props.children,dom)
}else if(Array.isArray(props.children)){
reconcileChildren(props.children,dom)
}
}
vdom.dom = dom
return dom
}
复制代码
2.2.3 updateProps 更新属性
function updateProps(dom,oldProps,newProps){
for(let key in newProps){
if(key === 'children'){
continue;
}
if(key === 'style'){
let styleObj = newProps[style];
for(let attr in styleObj){
dom.style[attr] = styleObj[sttr]
}
}else{
dom[key] = newProps[key]
}
}
}
复制代码
2.2.4 reconcileChildren
当
props.children是对象时直接render,是数组时通过reconcileChildren遍历出 child 再render
function reconcileChildren(childrenVdom,parentDOM){
for(let i=0;i<childrenVdom.length;i++){
let childVdom = childrenVdom[i];
render(childVdom,parentDOM)
}
}
复制代码
2.2.5 导出
最后将render方法导出即可
const ReactDOM = {
render
}
export default ReactDOM;
复制代码
三、结语
这套思路是借鉴了
React15的源码来实现的,和现在的最新版的代码有一些出入,是由于16中新增的Fiber会使virtual DOM进行增量式渲染,直接手写的话会劝退大部分萌新玩家,之所以选择React15是由于这个渲染的过程更易于理解,当然后续也是会更新Fiber相关的内容的。对Fiber感兴趣的话可以看看这个–react-fiber-architecture。最后点个赞再走吧~~
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END





















![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)