React 拾遺:React.createElement 與 JSX

如何使用代碼

安裝項(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'));
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末椰苟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子树叽,更是在濱河造成了極大的恐慌,老刑警劉巖题诵,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異赠潦,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)她奥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門瓮增,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)哩俭,“玉大人绷跑,你說(shuō)我怎么就攤上這事凡资。” “怎么了隙赁?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伞访。 經(jīng)常有香客問我,道長(zhǎng)咐扭,這世上最難降的妖魔是什么滑废? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮蠕趁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俺陋。我一直安慰自己,他們只是感情好腊状,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缴挖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪映屋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天棚点,我揣著相機(jī)與錄音,去河邊找鬼瘫析。 笑死默责,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咸包。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼葡缰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了泛释?” 一聲冷哼從身側(cè)響起温算,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎注竿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巩割,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年宣谈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闻丑。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖勋锤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叁执,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布徒恋,位于F島的核電站,受9級(jí)特大地震影響欢伏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜硝拧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一葛假、第九天 我趴在偏房一處隱蔽的房頂上張望滋恬。 院中可真熱鬧聊训,春花似錦带斑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)挂滓。三九已至,卻和暖如春啸胧,著一層夾襖步出監(jiān)牢的瞬間赶站,已是汗流浹背贝椿。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工柠辞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留主胧,地道東北人叭首。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓焙格,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親眷唉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359