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'));
轉載https://www.jianshu.com/p/42a3ec621e94