当前位置: 首页 > 科技观察

面试官:说说ReactJsx转为真实DOM的过程?

时间:2023-03-22 00:53:18 科技观察

本文转载自微信公众号《JS日报》,作者慧慧。转载本文请联系JS每日一问公众号。1.什么是反应?通过将组件编写的JSX映射到屏幕上,当组件中的状态发生变化后,React会将这些“变化”更新到屏幕上。上一篇文章中,JSX最终是通过babel转化为React.createElement这种形式,例如:

会被转化通过bebel如下:React.createElement("div",null,React.createElement("img",{src:"avatar.png",className:"profile"}),React.createElement(Hello,null));在转换过程中,babel在编译Letter时会判断JSX中的第一个组件:当首字母为小写时,识别为原生DOM标签,createElement的第一个变量编译成字符串;当首字母大写时,识别为自定义组件,createElement的第一个变量被编译成对象,最终会通过RenderDOM.render(...)方法挂载,如下:ReactDOM.render(,document.getElementById("root"));2.流程在react中,节点大致可以分为四类:native标签节点文本节点功能组件类组件如下:classClassComponenttextendsComponent{staticdefaultProps={color:"pink"};render(){return(

ClassComponent

{this.props.name}

);}}functionFunctionComponent(props){return(FunctionComponent

{props.name}

);}constjsx=(

xx

xxx
);这些类别最终会转化为React.createElement的形式。当调用React.createElement时,它会传递标签类型type、标签属性props和几个子元素子元素。函数是生成一个虚拟Dom对象,如下图:functioncreateElement(type,config,...children){if(config){deleteconfig.__self;deleteconfig.__source;}//!源码中已经做了详细的处理,比如过滤掉key、ref等constprops={...config,children:children.map(child=>typeofchild==="object"?child:createTextNode(child))};return{type,props};}functioncreateTextNode(text){return{type:TEXT,props:{children:[],nodeValue:text}};}exportdefault{createElement};createElement会根据传入的节点信息:如果是原生标签节点,type为字符串,如div、span如果是文本节点,没有type,这里是TEXT如果是函数组件,type是函数名如果是类组件,type是类名虚拟DOM会通过渲染成真实DOMReactDOM.render,使用方法如下:ReactDOM.render(element,container[,callback])第一次调用时,会替换容器节点中的所有DOM元素,后续调用会使用React的diff算法进行有效的更新。如果提供了可选的回调函数callback,则该回调将在组件渲染或更新后执行。render的一般实现方法如下:node);}//创建一个真实的DOM节点functioncreateNode(vnode,parentNode){letnode=null;const{type,props}=vnode;if(type===TEXT){node=document.createTextNode("");}elseif(typeoftype==="string"){node=document.createElement(type);}elseif(typeoftype==="function"){node=type.isReactComponent?updateClassComponent(vnode,parentNode):updateFunctionComponent(vnode,parentNode);}else{node=document.createDocumentFragment();}reconcileChildren(props.children,node);updateNode(node,props);returnnode;}//遍历子vnode,然后放入子vnode->真实DOM节点,然后插入父节点functionreconcileChildren(children,node){for(leti=0;ik!=="children").forEach(k=>{if(k.slice(0,2)==="on"){leteventName=k.slice(2).toLocaleLowerCase();node.addEventListener(eventName,nextVal[k]);}else{node[k]=nextVal[k];}});}//返回真正的dom节点//执行函数functionupdateFunctionComponent(vnode,parentNode){const{type,props}=vnode;letvvnode=type(props);constnode=createNode(vvnode,parentNode);returnnode;}//返回真正的dom节点//先实例化,再执行render函数functionupdateClassComponent(vnode,parentNode){const{type,props}=vnode;letcmp=新类型(道具);constvvnode=cmp.render();constnode=createNode(vvnode,parentNode);返回节点;如图:渲染过程如下:使用React.createElement或JSX编写React组件,实际上最后会转换所有JSX代码变成React.createElement(...),Babel帮我们完成了转换过程。createElement函数处理key、ref等特殊props,获取defaultProps给defaultprops赋值,处理传入的子节点,最后构造一个虚拟DOM对象ReactDOM.render,将生成的虚拟DOM渲染到指定的容器中,采用批处理、事务等机制,针对特定浏览器进行性能优化,最终转化为真实的DOM,参考https://bbs.huaweicloud.com/blogs/265503)https://huang-qing.github.io/react/2019/05/29/React-VirDom/https://segmentfault.com/a/1190000018891454