面试官:说说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. function createTextNode(text) { 
    18.     return { 
    19.         type: TEXT, 
    20.         props: { 
    21.             children: [], 
    22.             nodeValue: text 
    23.         } 
    24.     }; 
    25. export default { 
    26.     createElement 
    27. }; 

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

    三、总结

    在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过程?
    分享网址:http://www.shufengxianlan.com/qtweb/news45/253495.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

    猜你还喜欢下面的内容

    App开发知识

    行业网站建设