面试官:说说ReactJsx转换成真实DOM过程?

本文转载自微信公众号「JS每日一题」,作者灰灰。转载本文请联系JS每日一题公众号。

创新互联建站主要从事网站制作、网站设计、网页设计、企业做网站、公司建网站等业务。立足成都服务井冈山,10年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:028-86922220

一、是什么

react通过将组件编写的JSX映射到屏幕,以及组件中的状态发生了变化之后 React会将这些「变化」更新到屏幕上

在前面文章了解中,JSX通过babel最终转化成React.createElement这种形式,例如:

 
 
 
 
  1.  
  2.    
  3.    
 

会被bebel转化成如下:

 
 
 
 
  1. React.createElement( 
  2.   "div", 
  3.   null, 
  4.   React.createElement("img", { 
  5.     src: "avatar.png", 
  6.     className: "profile" 
  7.   }), 
  8.   React.createElement(Hello, null) 
  9. ); 

在转化过程中,babel在编译时会判断 JSX 中组件的首字母:

最终都会通过RenderDOM.render(...)方法进行挂载,如下:

 
 
 
 
  1. ReactDOM.render(,  document.getElementById("root")); 

二、过程

在react中,节点大致可以分成四个类别:

如下所示:

 
 
 
 
  1. class ClassComponent extends Component { 
  2.   static defaultProps = { 
  3.     color: "pink" 
  4.   }; 
  5.   render() { 
  6.     return ( 
  7.        
  8.         

    ClassComponent

     
  9.         {this.props.name}

     
  10.       
 
  •     ); 
  •   } 
  • } 
  •  
  • function FunctionComponent(props) { 
  •   return ( 
  •      
  •       FunctionComponent 
  •       

    {props.name}

     
  •     
  •  
  •   ); 
  • } 
  •  
  • const jsx = ( 
  •    
  •     

    xx

     
  •     xxx 
  •      
  •      
  •    
  • ); 
  • 这些类别最终都会被转化成React.createElement这种形式

    React.createElement其被调用时会传?标签类型type,标签属性props及若干子元素children,作用是生成一个虚拟Dom对象,如下所示:

     
     
     
     
    1. function createElement(type, config, ...children) { 
    2.     if (config) { 
    3.         delete config.__self; 
    4.         delete config.__source; 
    5.     } 
    6.     // ! 源码中做了详细处理,⽐如过滤掉key、ref等 
    7.     const props = { 
    8.         ...config, 
    9.         children: children.map(child => 
    10.    typeof child === "object" ? child : createTextNode(child) 
    11.   ) 
    12.     }; 
    13.     return { 
    14.         type, 
    15.         props 
    16.     }; 
    17. } 
    18. function createTextNode(text) { 
    19.     return { 
    20.         type: TEXT, 
    21.         props: { 
    22.             children: [], 
    23.             nodeValue: text 
    24.         } 
    25.     }; 
    26. } 
    27. export default { 
    28.     createElement 
    29. }; 

    createElement会根据传入的节点信息进行一个判断:

    虚拟DOM会通过ReactDOM.render进行渲染成真实DOM,使用方法如下:

     
     
     
     
    1. ReactDOM.render(element, container[, callback]) 

    当首次调用时,容器节点里的所有 DOM 元素都会被替换,后续的调用则会使用 React 的 diff算法进行高效的更新

    如果提供了可选的回调函数callback,该回调将在组件被渲染或更新之后被执行

    render大致实现方法如下:

     
     
     
     
    1. function render(vnode, container) { 
    2.     console.log("vnode", vnode); // 虚拟DOM对象 
    3.     // vnode _> node 
    4.     const node = createNode(vnode, container); 
    5.     container.appendChild(node); 
    6. } 
    7.  
    8. // 创建真实DOM节点 
    9. function createNode(vnode, parentNode) { 
    10.     let node = null; 
    11.     const {type, props} = vnode; 
    12.     if (type === TEXT) { 
    13.         node = document.createTextNode(""); 
    14.     } else if (typeof type === "string") { 
    15.         node = document.createElement(type); 
    16.     } else if (typeof type === "function") { 
    17.         node = type.isReactComponent 
    18.             ? updateClassComponent(vnode, parentNode) 
    19.         : updateFunctionComponent(vnode, parentNode); 
    20.     } else { 
    21.         node = document.createDocumentFragment(); 
    22.     } 
    23.     reconcileChildren(props.children, node); 
    24.     updateNode(node, props); 
    25.     return node; 
    26. } 
    27.  
    28. // 遍历下子vnode,然后把子vnode->真实DOM节点,再插入父node中 
    29. function reconcileChildren(children, node) { 
    30.     for (let i = 0; i < children.length; i++) { 
    31.         let child = children[i]; 
    32.         if (Array.isArray(child)) { 
    33.             for (let j = 0; j < child.length; j++) { 
    34.                 render(child[j], node); 
    35.             } 
    36.         } else { 
    37.             render(child, node); 
    38.         } 
    39.     } 
    40. } 
    41. function updateNode(node, nextVal) { 
    42.     Object.keys(nextVal) 
    43.         .filter(k => k !== "children") 
    44.         .forEach(k => { 
    45.         if (k.slice(0, 2) === "on") { 
    46.             let eventName = k.slice(2).toLocaleLowerCase(); 
    47.             node.addEventListener(eventName, nextVal[k]); 
    48.         } else { 
    49.             node[k] = nextVal[k]; 
    50.         } 
    51.     }); 
    52. } 
    53.  
    54. // 返回真实dom节点 
    55. // 执行函数 
    56. function updateFunctionComponent(vnode, parentNode) { 
    57.     const {type, props} = vnode; 
    58.     let vvnode = type(props); 
    59.     const node = createNode(vvnode, parentNode); 
    60.     return node; 
    61. } 
    62.  
    63. // 返回真实dom节点 
    64. // 先实例化,再执行render函数 
    65. function updateClassComponent(vnode, parentNode) { 
    66.     const {type, props} = vnode; 
    67.     let cmp = new type(props); 
    68.     const vvnode = cmp.render(); 
    69.     const node = createNode(vvnode, parentNode); 
    70.     return node; 
    71. } 
    72. export default { 
    73.     render 
    74. }; 

    三、总结

    在react源码中,虚拟Dom转化成真实Dom整体流程如下图所示:

    其渲染流程如下所示:

    参考文献

    https://bbs.huaweicloud.com/blogs/265503)

    https://huang-qing.github.io/react/2019/05/29/React-VirDom/

    https://segmentfault.com/a/1190000018891454


    标题名称:面试官:说说ReactJsx转换成真实DOM过程?
    当前URL:http://www.hdfswf.com/article/djejhed.html

    其他资讯