深刻理解JSX

本文來自 React技術揭祕html

JSX做爲描述組件內容的數據結構,爲JS賦予了更多視覺表現力。在React中咱們大量使用他。在深刻源碼以前,有些疑問咱們須要先解決:react

  • JSX和虛擬DOM是同一個東西麼?
  • React ComponentReact Element是同一個東西麼,他們和 JSX有什麼關係?

帶着這些疑問,讓咱們開始這一節的學習。git

JSX簡介

相信做爲React的使用者,你已經接觸過JSX。若是你還不瞭解他,能夠看下官網對其的描述github

JSX在編譯時會被Babel編譯爲React.createElement方法。web

JSX編譯 Demo 外網Demo 內網Demobabel

這也是爲何在每一個使用JSX的JS文件中,你必須顯式的聲明數據結構

import React from 'react';
複製代碼

不然在運行時該模塊內就會報未定義變量 React的錯誤。編輯器

你能夠經過@babel/plugin-transform-react-jsx插件告訴Babel編譯時須要將JSX編譯爲何函數的調用(默認爲React.createElement)。函數

好比在preact這個類React庫中,JSX會被編譯爲一個名爲h的函數調用。post

// 編譯前
<p>KaSong</p> // 編譯後 h("p", null, "KaSong"); 複製代碼

React.createElement

既然JSX會被編譯爲React.createElement,讓咱們看看他作了什麼:

export function createElement(type, config, children) {
 let propName;   const props = {};   let key = null;  let ref = null;  let self = null;  let source = null;   if (config != null) {  // 將 config 處理後賦值給 props  }   const childrenLength = arguments.length - 2;  // 處理 children,會被賦值給props.children   // 處理 defaultProps   return ReactElement(  type,  key,  ref,  self,  source,  ReactCurrentOwner.current,  props,  ); }  const ReactElement = function(type, key, ref, self, source, owner, props) {  const element = {  // 標記這是個 React Element  $typeof: REACT_ELEMENT_TYPE,   type: type,  key: key,  ref: ref,  props: props,  _owner: owner,  };   return element; }; 複製代碼

咱們能夠看到,React.createElement最終會調用ReactElement方法返回一個包含組件數據的對象,該對象有個參數$$typeof: REACT_ELEMENT_TYPE標記了該對象是個React Element

因此調用React.createElement返回的對象就是React Element麼?

React提供了驗證合法React Element的全局API React.isValidElement,咱們看下他的實現:

export function isValidElement(object) {
 return (  typeof object === 'object' &&  object !== null &&  object.$typeof === REACT_ELEMENT_TYPE  ); } 複製代碼

能夠看到,$$typeof === REACT_ELEMENT_TYPE的非null對象就是一個合法的React Element。換言之,在React中,全部JSX在運行時的返回結果(即React.createElement()的返回值)都是React Element

那麼JSXReact Component的關係呢。

React Component

React中,咱們常使用ClassComponentFunctionComponent構建組件。

class AppClass extends React.Component {
 render() {  return <p>KaSong</p>  } } console.log('這是ClassComponent:', AppClass); console.log('這是Element:', <AppClass/>);   function AppFunc() {  return <p>KaSong</p>; } console.log('這是FunctionComponent:', AppFunc); console.log('這是Element:', <AppFunc/>); 複製代碼

React Component 分類 Demo

咱們能夠從Demo控制檯打印的對象看出,ClassComponent對應的Elementtype字段爲AppClass自身。

FunctionComponent對應的Elementtype字段爲AppFunc自身,以下所示:

{
 $typeof: Symbol(react.element),  key: null,  props: {},  ref: null,  type: ƒ AppFunc(),  _owner: null,  _store: {validated: false},  _self: null,  _source: null } 複製代碼

值得注意的一點,因爲

AppClass instanceof Function === true;
AppFunc instanceof Function === true; 複製代碼

因此沒法經過引用類型區分ClassComponentFunctionComponentReact經過ClassComponent實例原型上的isReactComponent變量判斷是不是ClassComponent

ClassComponent.prototype.isReactComponent = {};
複製代碼

JSX與虛擬DOM

從上面的內容咱們能夠發現,JSX是一種描述當前組件內容的數據結構,他並不能描述組件schedulereconcilerender相關的信息。好比以下信息就不包括在JSX中:

  • 組件在更新中的優先級
  • 組件的 state
  • 組件被打上的用於 Renderer的標記

這些內容都是包含在虛擬DOM中的。

因此,在組件mount時,Reconciler根據JSX描述的組件內容生成組件對應的虛擬DOM。在update時,ReconcilerJSX與虛擬DOM保存的數據對比,爲對比後狀態有變化的虛擬DOM打上標記。

參考資料

經過這篇文章在運行時修改React.createElement達到消除頁面全部div元素的效果

如何幹掉知乎的所有DIV

相關文章
相關標籤/搜索