自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官:說說React Jsx轉換成真實DOM過程?

開發(fā) 前端
react通過將組件編寫的JSX映射到屏幕,以及組件中的狀態(tài)發(fā)生了變化之后 React會將這些「變化」更新到屏幕上。

[[414295]]

本文轉載自微信公眾號「JS每日一題」,作者灰灰。轉載本文請聯系JS每日一題公眾號。

一、是什么

react通過將組件編寫的JSX映射到屏幕,以及組件中的狀態(tài)發(fā)生了變化之后 React會將這些「變化」更新到屏幕上

在前面文章了解中,JSX通過babel最終轉化成React.createElement這種形式,例如:

  1. <div> 
  2.   <img src="avatar.png" className="profile" /> 
  3.   <Hello /> 
  4. </div> 

 

 

會被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 中組件的首字母:

  • 當首字母為小寫時,其被認定為原生 DOM 標簽,createElement 的第一個變量被編譯為字符串
  • 當首字母為大寫時,其被認定為自定義組件,createElement 的第一個變量被編譯為對象

最終都會通過RenderDOM.render(...)方法進行掛載,如下:

  1. ReactDOM.render(<App />,  document.getElementById("root")); 

二、過程

在react中,節(jié)點大致可以分成四個類別:

  • 原生標簽節(jié)點
  • 文本節(jié)點
  • 函數組件
  • 類組件

如下所示:

  1. class ClassComponent extends Component { 
  2.   static defaultProps = { 
  3.     color: "pink" 
  4.   }; 
  5.   render() { 
  6.     return ( 
  7.       <div className="border"
  8.         <h3>ClassComponent</h3> 
  9.         <p className={this.props.color}>{this.props.name}</p> 
  10.       </div> 
  11.     ); 
  12.   } 
  13.  
  14. function FunctionComponent(props) { 
  15.   return ( 
  16.     <div className="border"
  17.       FunctionComponent 
  18.       <p>{props.name}</p> 
  19.     </div> 
  20.   ); 
  21.  
  22. const jsx = ( 
  23.   <div className="border"
  24.     <p>xx</p> 
  25.     <a href="https://www.xxx.com/">xxx</a> 
  26.     <FunctionComponent name="函數組件" /> 
  27.     <ClassComponent name="類組件" color="red" /> 
  28.   </div> 
  29. ); 

這些類別最終都會被轉化成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會根據傳入的節(jié)點信息進行一個判斷:

  • 如果是原生標簽節(jié)點, type 是字符串,如div、span
  • 如果是文本節(jié)點, type就沒有,這里是 TEXT
  • 如果是函數組件,type 是函數名
  • 如果是類組件,type 是類名

虛擬DOM會通過ReactDOM.render進行渲染成真實DOM,使用方法如下:

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

當首次調用時,容器節(jié)點里的所有 DOM 元素都會被替換,后續(xù)的調用則會使用 React 的 diff算法進行高效的更新

如果提供了可選的回調函數callback,該回調將在組件被渲染或更新之后被執(zhí)行

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. // 創(chuàng)建真實DOM節(jié)點 
  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節(jié)點,再插入父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節(jié)點 
  51. // 執(zhí)行函數 
  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節(jié)點 
  59. // 先實例化,再執(zhí)行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整體流程如下圖所示:

其渲染流程如下所示:

  • 使用React.createElement或JSX編寫React組件,實際上所有的 JSX 代碼最后都會轉換成React.createElement(...) ,Babel幫助我們完成了這個轉換的過程。
  • createElement函數對key和ref等特殊的props進行處理,并獲取defaultProps對默認props進行賦值,并且對傳入的孩子節(jié)點進行處理,最終構造成一個虛擬DOM對象
  • ReactDOM.render將生成好的虛擬DOM渲染到指定容器上,其中采用了批處理、事務等機制并且對特定瀏覽器進行了性能優(yōu)化,最終轉換為真實DOM

參考文獻

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

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

 

https://segmentfault.com/a/1190000018891454

 

責任編輯:武曉燕 來源: JS每日一題
點贊
收藏

51CTO技術棧公眾號