React.createElement 與 JSX


DOM 向JSX的演進

網頁由 DOM 元素構成。React DOM 並不是瀏覽器的 DOM,而React DOM 只是用來告訴瀏覽器如何創建 DOM 的方法。通常情況下,我們並不需要 React 就能創建出一個 DOM 元素,但是 React 創建與管理 DOM 的方式有組件化、虛擬 DOM 等更高層次的抽象,由此帶來的優勢是更快的渲染速度,以及更好的維護性,因此值得去嘗試。

下面分別用 JavaScript 原生方法,React.createElement 方法,以及 JSX 方法來創建一個h1元素,class設置為main,最后掛載在 id 為 root 的 div 元素下面。

html結構應該如下:

<div id="root">
  <h1 id="main">Hello React</h1>
</div>

1 document.createElement

JavaScript原生方法,沒有過多需要解釋的部分。

// 方法1:document.createElement
const title = document.createElement('h1');
title.innerText='Hello React (method 1)';
title.className='main';
document.getElementById('root').appendChild(title);

2 React.creaetElement

第二種方法是用 React 的 createElement 來創建 React DOM。

// 方法2:React.createElement
import React from 'react';
import ReactDOM from 'react-dom';

const title = React.createElement("h1", {className: "main"}, "Hello React (method 2)");
ReactDOM.render(title, document.getElementById('root'));

其中 createElement(a, b, c)

  • 第一個參數 a:表示元素的類型,比如:h1, div 等。
  • 第二個參數 b:表示該元素上的屬性,使用 JavaScript 對象方式表示。
  • 第三個參數 c:表示該元素內部的內容(子元素),可以是文字,可以繼續嵌套另外一個 React.createElement(a, b, c)

這種方法其實在實際 React 開發中幾乎不會使用,因為可以直接 JSX 方法。

3 JSX

JSX 是一種 JavaScript 的語法糖。Facebook 開發JSX出來,主要用於 React 中。雖然 JSX 的內容會長得像 html,但還是 JavaScript。

用 JSX 方法來創建 React DOM 的代碼如下:

// 方法3:JSX
import React from 'react';
import ReactDOM from 'react-dom';

const title = (
  <h1>Hello React (method 3)</h1>
);

ReactDOM.render(title, document.getElementById('root'));

3.1 代碼逐行解讀:

第1行: import React from 'react';

有 JSX 的地方,在文件開頭就需要引入 React,因為實際上 JSX 是使用了 React.createElement,JSX 只是一個JS 的語法糖,所以需要引入 React 包,否則會報錯。

第2行: import ReactDOM from 'react-dom';

 react-dom 是一個把React 代碼渲染到網頁端的包。如果在移動端渲染,就需要使用 React Native 的相關包。 目前(截至2017年12月3日),React 與 ReactDOM 都更新到了 16.0.0,所以在 package.json 中可以看到這兩個版本都是最新的版本。

第4-6行:

const title = (
  <h1>Hello React (method 3)</h1>
);

這就是一段 jsx 代碼,實際是 React.createElement 創建一個 React DOM 對象,完全等同於下面這行代碼。

const title = React.createElement("h1", {className: "main"}, "Hello React (method 3)");

JSX 更加直觀,符合我們對 html 結構的認知,如果都用 React.createElement 去創建 React DOM,會非常的繁瑣,且容易出錯。

第8行:

ReactDOM.render(title, document.getElementById('root'));

把上面創造出來的 React DOM 對象(即虛擬DOM),渲染到網頁 id 為 root 的元素中。

3.2 JSX的限制:標簽的包裹

但是 JSX 有一個限制,就是在 JSX 中 html 代碼第一層只能寫一個元素。如果有多個標簽(元素)並列,形成所謂的相鄰JSX元素(adjacement jsx elements),就會報語法錯誤。通常這種多元素並列的情況,就用在它們外面包裹一層 div。

(1) 錯誤的代碼

舉例來看,如果 index.js 寫成如下代碼:

// 沒有 div 包裹會報錯
import React from 'react';
import ReactDOM from 'react-dom';

const title = (
  <h1>Parallel elements demo</h1>
  <p>Content</p>
);

ReactDOM.render(title, document.getElementById('root'));

命令行中會報語法錯誤:相鄰 JSX 元素必須用封閉的標簽包裹。

Module build failed: SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag (35:2)
(2) 正確的代碼

相鄰元素 Adjacent JSX elements,在這里其實就是並列的 h1 與 p 標簽。所以這里的解決方法就是用一個 div 標簽來包裹 h1 與 p 標簽。

// 正確寫法:用 div 包裹並列標簽
import React from 'react';
import ReactDOM from 'react-dom';

const title = (
  <div>
    <h1>Parallel elements demo</h1>
    <p>Content</p>
  </div>
);

ReactDOM.render(title, document.getElementById('root'));

3.3 突破JSX標簽包裹限制

注意:如果剛接觸 React,這部分內容可以跳過后面再來看。

對於 jsx 外層需要包裹一層 div,如果要突破這個限制,目前有兩種方法:

  • 返回數組
  • 使用高階組件做輔助
(1) 返回數組

如果的是數組,就沒有問題。

// 突破JSX標簽包裹限制1:返回數組
import React from 'react';
import ReactDOM from 'react-dom';

const arr = ['Adams', 'Bill', 'Charlie'];

const Arr = () => {
  return arr.map((item, index) => {
    return <p key={index}>{item}</p>
  })
}

ReactDOM.render(<Arr />, document.getElementById('root'));

這里是一個數組 arr,包含三個名字,然后用 map 方法得到一個包含三段 JSX 代碼的數組。注意這里需要寫成匿名函數,然后以 <Arr /> 自封閉標簽的格式放入 ReactDOM 的第一個參數位置去渲染。

當然,這段代碼還可以進行簡寫:

第一種簡寫 map 中的剪頭函數少了 return

// 簡寫1
import React from 'react';
import ReactDOM from 'react-dom';

const arr = ['Adams', 'Bill', 'Charlie'];

const Arr = () => {
  return arr.map((item, index) => <p key={index}>{item}</p>);
};

ReactDOM.render(<Arr />, document.getElementById('root'));

第二種簡寫是 Arr 這個匿名函數少了 return。

// 簡寫2
import React from 'react';
import ReactDOM from 'react-dom';

const arr = ['Adams', 'Bill', 'Charlie'];

const Arr = () => (arr.map((item, index) => <p key={index}>{item}</p>));

ReactDOM.render(<Arr />, document.getElementById('root'));
(2) 高階組件(High Order Component, hoc)

div 去包裹並列元素的痛點是,我們可能並不需要這個多余的 div 標簽,可能會破壞 html 結構,也許上層做了 flex,並不能有效的傳遞到這些並列標簽上。

所以這里引入了用於輔助的高階組件 hoc。雖然高階組件的名字聽起來很嚇人,然而做的事情很簡單,就是傳遞的作用。

// 突破JSX標簽包裹限制:方法2 高階組件
import React from 'react';
import ReactDOM from 'react-dom';

const Aux = props => props.children;

const title = (
  <Aux>
    <h1>Parallel elements demo</h1>
    <p>Content</p>
  </Aux>
);

ReactDOM.render(title, document.getElementById('root'));

在上面這段代碼中,const Aux = props => props.children; 就是高階組件。Aux 這個高階組件的作用是把標簽包括的內容進行傳遞和顯示(Aux 是英文中的 auxiliary輔助的)。查看最終 html 結構會發現 div 已經消失了,而且代碼沒有 div 也能正常。

高階組件不僅僅是這里的傳遞作用,在 Redux 中會大量使用,后面會講到。

另外,據稱 React 16.2 開始有一個所謂的 fragment 的做法,就是 React 自帶了 <Aux></Aux>,但是寫成 <></>。在 React 16.2,代碼可以寫成如下格式:

import React from 'react';
import ReactDOM from 'react-dom';

const title = (
  <>
    <h1>Parallel elements demo</h1>
    <p>Content</p>
  </>
);

ReactDOM.render(title, document.getElementById('root'));


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM