web前端高级React - React从入门到进阶之JSX虚拟DOM渲染为真实DOM的原理和步骤

系列文章目录

第一章:React从入门到进阶之初识React
第二章:React从入门到进阶之JSX简介
第三章:React从入门到进阶之元素渲染
第四章:React从入门到进阶之JSX虚拟DOM渲染为真实DOM的原理和步骤
第五章:React从入门到进阶之组件化开发及Props属性传值
第六章:React从入门到进阶之state及组件的生命周期
第七章:React从入门到进阶之React事件处理

一、分析与思考

前面我们已经学习了JSX的一些基本语法,通过前面的学习我们知道:在js文件中通过调用ReactDOM.render()方法,并传入一堆JSX代码(可以理解为虚拟DOM)。当程序运行时这些JSX代码就会被转换为真实的DOM元素然后呈现在页面上;那么这些JSX虚拟DOM是如何被转换为真实DOM的呢,原理又是怎样的呢?

当我们用react脚手架创建好一个项目后,会生成一个package.json文件,在这个文件的最下面有这样一段代码

"babel" :{
     
     
		presets:["react-app"] 
}

了解babel的人应该都知道,babel主要是用来将ES6或更高版本的js代码转换为ES5的代码,从而提高兼容性。上面这段代码的配置同样也是如此,它可以将JSX虚拟DOM转换成React可识别的React DOM对象,如下图:
在这里插入图片描述

二、JSX虚拟DOM渲染为真实DOM的原理和步骤

接下来我们就来分析一下JSX虚拟DOM渲染为真实DOM的原理和步骤

  • 基于babel-preset-react-app把JSX语法变为React.createElement的模式
    • 只要遇到元素标签(或组件)都要调用createElement
    • createElement的前两个参数是固定的:标签名(组件名)、属性,第三个及以后的参数是子元素
    • 如果传递了属性,第二个参数是一个对象(包含了各属性的信息),没有传递属性则第二个参数为null
  • 基于React.createElement方法执行创建出虚拟DOM对象(JSX对象)
    • 首先创建一个对象
    • type属性存储的是标签名或组件
    • props属性:如果没有传递任何属性,也没有任何子元素,则为空对象;把传递的createElement的属性,都赋值给props;如果有子元素则新增一个children属性,可能是一个值也可能是一个数组
  • 基于ReactDOM.render把创建的虚拟DOM对象渲染到页面指定的容器中
    • ReactDOM.render([jsxObj],[container],[callback]),render接收三个参数:jsx对象,页面指定的容器和回调函数(可不传)
    • callback渲染触发的回调函数,着这里可以获取到真实DOM

三、基于渲染原理重写createElement和render

React.createElement = function(type, props, ...children){
    
    
	let jsxObj = {
    
    
		tyupe,
		props:{
    
    },		
	}
	//传递了属性,把传递的属性都放在jsxObj的props中
	if(props !== null){
    
    
		//基于es6实现
		jsxObj.props = {
    
    ...props};
		//或者有es5的语法用for循环
	}
	//如果传递了子元素,还需要给jsxObj的props设置children属性
	if(children.length > 0){
    
    
		jsxObj.props.children = children;
		//如果传递的子元素只有一项,则直接把第一项的值赋值给jsxObj.props.children即可
		if(children.length === 1){
    
    
			jsxObj.props.children = children[0];
		}
	}
	return jsxObj;
}

ReactDOM.render = function render(jsxObj, container, callback){
    
    
	let {
    
    type, props} = jsxObj;
	//创建DOM元素
	if(typeof type === "string"){
    
    
		//创建真实DOM元素对象
		let element = document.createElement(type);
		//给创建的DOM设置属性
		for(let key in props){
    
    
			if(!props.hasOwnProperty(key)) break;
			//样式类和行内样式特殊处理
			if(key === "className"){
    
    
				element.setAttribute('class', props[key]);
				continue;
			}
			if(key === "style"){
    
    
				let styObj = props[key];
				for(let attr in styObj){
    
    
					if(!styObj.hasOwnProperty(attr)) break;
					element.style[attr] = styObj[attr];
				}
				continue;
			}
			//关于子元素处理
			if(key === "children"){
    
    
				let children = props[key];
				if(!Array.isArray(children)){
    
    
					 children = [children];
				}
				//循环子元素
				children.forEach(item=>{
    
    
					//如果是文本,则直接创建文本节点赋值给element,如果是新的虚拟DOM对象,则需要重复调用render方法,把新创建的DOM对象增加给element(递归)
					if(typeof item === "string"){
    
    
						element.appendChild(document.createTextNode(item));
						return;
					}
					render(item, element);
				});
			}
			
			element.setAttribute(key, props[key]);
		}
		container.appendChild(element);
		callback && callback();
	}
}

猜你喜欢

转载自blog.csdn.net/lixiaosenlin/article/details/111993181