如何使用代碼
安裝項(xiàng)目前置依賴仅政,以及啟動(dòng)項(xiàng)目的方法盆驹,參看:React 拾遺:項(xiàng)目腳手架。
請(qǐng)根據(jù)文章內(nèi)容躯喇,把相應(yīng)部分的代碼注釋取消,即可運(yùn)行廉丽。
摘要
本文介紹
- document.createElement, React.createElement 與 JSX 三種方法創(chuàng)建 DOM 的比較
- JSX 的 div 標(biāo)簽包裹并列元素的限制
- 突破 JSX 的 div 包裹限制的兩種方法
項(xiàng)目代碼地址:React 拾遺:React.createElement 與 JSX
DOM 向JSX的演進(jìn)
網(wǎng)頁(yè)由 DOM 元素構(gòu)成。React DOM 并不是瀏覽器的 DOM正压,而是 React DOM 只是用來(lái)告訴瀏覽器如何創(chuàng)建 DOM 的方法。通常情況下焦履,我們并不需要 React 就能創(chuàng)建出一個(gè) DOM 元素,但是 React 創(chuàng)建與管理 DOM 的方式有組件化嘉裤、虛擬 DOM 等更高層次的抽象,由此帶來(lái)的優(yōu)勢(shì)是更快的渲染速度厢洞,以及更好的維護(hù)性,因此值得去嘗試犀变。
下面分別用 JavaScript 原生方法秋柄,React.createElement 方法,以及 JSX 方法來(lái)創(chuàng)建一個(gè)h1元素骇笔,class設(shè)置為main,最后掛載在 id 為 root 的 div 元素下面笨触。
html結(jié)構(gòu)應(yīng)該如下:
<div id="root">
<h1 id="main">Hello React</h1>
</div>
1 document.createElement
JavaScript原生方法,沒有過(guò)多需要解釋的部分芦劣。
// 方法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 來(lái)創(chuàng)建 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)
- 第一個(gè)參數(shù) a:表示元素的類型寸认,比如:h1, div 等。
- 第二個(gè)參數(shù) b:表示該元素上的屬性偏塞,使用 JavaScript 對(duì)象方式表示。
- 第三個(gè)參數(shù) c:表示該元素內(nèi)部的內(nèi)容灸叼,可以是文字,可以繼續(xù)嵌套另外一個(gè)
React.createElement(a, b, c)
古今。
這種方法其實(shí)在實(shí)際 React 開發(fā)中幾乎不會(huì)使用,因?yàn)榭梢灾苯?JSX 方法沧卢。
3 JSX
JSX 是一種 JavaScript 的語(yǔ)法糖。Facebook 開發(fā)JSX出來(lái)醉者,主要用于 React 中。雖然 JSX 的內(nèi)容會(huì)長(zhǎng)得像 html撬即,但還是 JavaScript。
用 JSX 方法來(lái)創(chuàng)建 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,因?yàn)閷?shí)際上 JSX 是使用了 React.createElement
,JSX 只是一個(gè)JS 的語(yǔ)法糖几于,所以需要引入 React 包沿后,否則會(huì)報(bào)錯(cuò)沿彭。
第2行: import ReactDOM from 'react-dom';
react-dom 是一個(gè)把React 代碼渲染到網(wǎng)頁(yè)端的包尖滚。如果在移動(dòng)端渲染,就需要使用 React Native 的相關(guān)包漆弄。
目前(截至2017年12月3日),React 與 ReactDOM 都更新到了 16.0.0撼唾,所以在 package.json 中可以看到這兩個(gè)版本都是最新的版本。
第4-6行:
const title = (
<h1>Hello React (method 3)</h1>
);
這就是一段 jsx 代碼倒谷,實(shí)際是 React.createElement
創(chuàng)建一個(gè) React DOM 對(duì)象,完全等同于下面這行代碼恨锚。
const title = React.createElement("h1", {className: "main"}, "Hello React (method 3)");
JSX 更加直觀,符合我們對(duì) html 結(jié)構(gòu)的認(rèn)知猴伶,如果都用 React.createElement 去創(chuàng)建 React DOM,會(huì)非常的繁瑣他挎,且容易出錯(cuò)。
第8行:
ReactDOM.render(title, document.getElementById('root'));
把上面創(chuàng)造出來(lái)的 React DOM 對(duì)象办桨,渲染到網(wǎng)頁(yè) id 為 root 的元素中。
3.2 JSX的限制:標(biāo)簽的包裹
但是 JSX 有一個(gè)限制呢撞,就是在 JSX 中 html 代碼第一層只能寫一個(gè)元素。如果有多個(gè)標(biāo)簽(元素)并列殊霞,形成所謂的相鄰JSX元素(adjacement jsx elements)摧阅,就會(huì)報(bào)語(yǔ)法錯(cuò)誤棒卷。通常這種多元素并列的情況顾孽,就用在它們外面包裹一層 div比规。
(1) 錯(cuò)誤的代碼
舉例來(lái)看,如果 index.js 寫成如下代碼:
// 沒有 div 包裹會(huì)報(bào)錯(cuò)
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'));
命令行中會(huì)報(bào)語(yǔ)法錯(cuò)誤:相鄰 JSX 元素必須用封閉的標(biāo)簽包裹测秸。
Module build failed: SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag (35:2)
(2) 正確的代碼
相鄰元素 Adjacent JSX elements,在這里其實(shí)就是并列的 h1 與 p 標(biāo)簽吃谣。所以這里的解決方法就是用一個(gè) div 標(biāo)簽來(lái)包裹 h1 與 p 標(biāo)簽做裙。
// 正確寫法:用 div 包裹并列標(biāo)簽
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標(biāo)簽包裹限制
注意:如果剛接觸 React,這部分內(nèi)容可以跳過(guò)后面再來(lái)看锚贱。
對(duì)于 jsx 外層需要包裹一層 div,如果要突破這個(gè)限制拧廊,目前有兩種方法:
- 返回?cái)?shù)組
- 使用高階組件做輔助
(1) 返回?cái)?shù)組
如果的是數(shù)組,就沒有問題吧碾。
// 突破JSX標(biāo)簽包裹限制1:返回?cái)?shù)組
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'));
這里是一個(gè)數(shù)組 arr,包含三個(gè)名字倦春,然后用 map 方法得到一個(gè)包含三段 JSX 代碼的數(shù)組。注意這里需要寫成匿名函數(shù)睁本,然后以 <Arr />
自封閉標(biāo)簽的格式放入 ReactDOM 的第一個(gè)參數(shù)位置去渲染。
當(dāng)然呢堰,這段代碼還可以進(jìn)行簡(jiǎn)寫:
第一種簡(jiǎn)寫 map 中的剪頭函數(shù)少了 return
// 簡(jiǎn)寫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'));
第二種簡(jiǎn)寫是 Arr 這個(gè)匿名函數(shù)少了 return。
// 簡(jiǎn)寫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 去包裹并列元素的痛點(diǎn)是,我們可能并不需要這個(gè)多余的 div 標(biāo)簽藻雌,可能會(huì)破壞 html 結(jié)構(gòu),也許上層做了 flex蜕提,并不能有效的傳遞到這些并列標(biāo)簽上。
所以這里引入了用于輔助的高階組件 hoc布轿。雖然高階組件的名字聽起來(lái)很嚇人,然而做的事情很簡(jiǎn)單稠肘,就是傳遞的作用。
// 突破JSX標(biāo)簽包裹限制:方法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 這個(gè)高階組件的作用是把標(biāo)簽包括的內(nèi)容進(jìn)行傳遞和顯示(Aux 是英文中的 auxiliary)环揽。查看最終 html 結(jié)構(gòu)會(huì)發(fā)現(xiàn) div 已經(jīng)消失了庵佣,而且代碼沒有 div 也能正常。
高階組件不僅僅是這里的傳遞作用巴粪,在 Redux 中會(huì)大量使用,后面會(huì)講到肛根。
另外辫塌,據(jù)稱 React 16.2 開始有一個(gè)所謂的 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'));