JSX是React引入的一個技術。讓我們深入了解一下。

JSX簡介

JSX是由React引入的一項技術。

儘管React在不使用JSX的情況下也可以正常工作,但它是與組件一起工作的理想技術,因此React從JSX中獲益良多。

起初,您可能會認為使用JSX就像混合HTML和JavaScript(以及CSS)。

但事實並非如此,因為使用JSX語法時,實際上是用JavaScript編寫一個組件UI的聲明語法。

您使用的不是字符串描述UI,而是使用JavaScript,這使您能夠做許多好事。

JSX入門

以下是如何定義包含字符串的h1標籤的示例:

const element = <h1>Hello, world!</h1>

它看起來像JavaScript和HTML的奇怪混合,但實際上它是完全的JavaScript。

看起來像HTML,實際上是為了定義組件以及它們在標記中的定位而添加的語法糖。

在JSX表達式中,可以非常容易地插入屬性:

const myId = 'test'
const element = <h1 id={myId}>Hello, world!</h1>

只需注意,當屬性具有連字符(-)時,它會轉換為駝峰命名法。還有兩種特殊情況:

  • class變成className
  • for變成htmlFor

這是因為它們是JavaScript中的保留字。

以下是一個將兩個組件包裝到div標籤中的JSX片段的示例:

<div>
 <BlogPostsList />
 <Sidebar />
</div>

標籤始終需要關閉,因為這更像是XML而不是HTML(如果您還記得XHTML的日子,這將是熟悉的,但此後採用了HTML5鬆散語法)。在這種情況下,使用了自閉標籤。

請注意,我將2個組件包裝到div中。為什麼?因為**render()函數只能返回單個節點**,所以如果您要返回2個兄弟節點,只需添加一個父節點。它可以是任何標籤,不僅僅是div

轉譯JSX

瀏覽器無法執行包含JSX代碼的JavaScript文件。它們必須首先轉換為常規JS。

如何轉譯?通過進行所謂的轉譯過程。

我們已經說過JSX是可選的,因為對於每一個JSX行,都有一個對應的純JavaScript替代方案可用,這就是JSX轉譯的內容。

例如,以下兩個結構是等價的:

ReactDOM.render(
 React.createElement('div', { id: 'test' },
 React.createElement('h1', null, 'A title'),
 React.createElement('p', null, 'A paragraph')
 ),
 document.getElementById('myapp')
)

ReactDOM.render(
 <div id="test">
 <h1>A title</h1>
 <p>A paragraph</p>
 </div>,
 document.getElementById('myapp')
)

這個非常基本的例子只是起點,但您已經可以看到純JS語法與使用JSX相比有多麼複雜。

撰寫本文時,進行轉譯的最流行方法是使用Babel,這是在運行create-react-app時的默認選項,因此如果您使用它,則無需擔心,一切都在幕後自動進行。

如果您不使用create-react-app,則需要自行設置Babel。

JSX中的JS

JSX接受任何類型的JavaScript混合。

每當您需要添加一些JS時,只需將其放在大括號{}中即可。例如,這是如何使用在其他地方定義的常量值:

const paragraph = 'A paragraph'
ReactDOM.render(
 <div id="test">
 <h1>A title</h1>
 <p>{paragraph}</p>
 </div>,
 document.getElementById('myapp')
)

這是一個基本示例。大括號接受任何JS代碼:

const paragraph = 'A paragraph'
ReactDOM.render(
 <table>
 {rows.map((row, i) => {
 return <tr>{row.text}</tr>
 })}
 </table>,
 document.getElementById('myapp')
)

正如您所見,我們嵌套了JavaScript,該JavaScript定義在JSX內,而JSX定義在JavaScript中,您可以進行任意深度的嵌套。

JSX中的HTML

JSX在很大程度上與HTML類似,但實際上它是XML語法。

最終您要渲染HTML,所以您需要了解一些在HTML中如何定義某些內容以及如何在JSX中定義它們之間的差異。

您需要關閉所有標籤

就像在使用過XHTML的情況下一樣,您需要關閉所有標籤:不再是<br>,而是使用自閉標籤<br />(其他標籤也是如此)。

駝峰命名法是新的標準

在HTML中,您會找到沒有任何大小寫的屬性(例如onchange)。在JSX中,它們被重命名為駝峰命名法:

  • onchange => onChange
  • onclick => onClick
  • onsubmit => onSubmit

class變成className

由於JSX是JavaScript,而class是保留詞,因此您不能寫

<p class="description">

而是需要使用

<p className="description">

相同的適用於for,它被轉換為htmlFor

style屬性改變其語義

HTML中的style屬性允許指定內聯樣式。在JSX中,它不再接受字符串,您將在React中的CSS中看到為什麼這是一個非常方便的更改。

表單

表單字段的定義和事件在JSX中進行了更改,以提供更一致性和實用性。

有關表單的更多細節,請參閱JSX中的表單

React中的CSS

JSX提供了一種很酷的方式來定義CSS。

如果您對HTML內聯樣式有一點經驗,第一眼看起來,您會感覺自己被推到10或15年前的世界,在那裡內聯CSS是完全正常的(如今它被當作“快速修復”解決方案)。

JSX樣式與內聯CSS並不相同:首先,JSX style屬性不像接受包含CSS屬性的字符串那樣,而僅接受一個對象。這意味著您在對象中定義屬性:

var divStyle = {
 color: 'white'
}

ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode)

或者

ReactDOM.render(<div style={{ color: 'white' }}>Hello World!</div>, mountNode)

您在JSX中編寫的CSS值與普通CSS稍有不同:

  • 屬性名稱的鍵以駝峰命名法命名
  • 值只是字符串
  • 您使用逗號分隔每組屬性

為什麼這比純CSS / SASS / LESS更受青睞?

CSS是一個未解決的問題。自從它問世以來,圍繞它的工具數不勝數。 CSS的主要問題在於它沒有作用域,並且很容易撰寫不受限制的CSS,因此“快速修復”可能會影響不應該觸及的元素。

JSX允許組件(例如在React中定義的組件)完全封裝其樣式。

這是首選解決方案嗎?

在JSX中的內聯樣式很好,直到您需要

  1. 編寫媒體查詢
  2. 規劃動畫
  3. 引用偽類(例如:hover
  4. 引用偽元素(例如::first-letter

總之,它們涵蓋了基本功能,但不是最終解決方案。

JSX中的表單

JSX對HTML表單工作進行了一些更改,目的是使開發人員更容易使用。

value屬性和defaultValue屬性

value屬性始終保存字段的當前值。

defaultValue屬性保存在創建字段時設置的默認值。

這有助於解決常規DOM交互的一些奇怪行為,例如檢查input.valueinput.getAttribute('value')返回一個返回當前值和一個返回原始默認值的情況。

這也適用於textarea字段,例如

<textarea>Some text</textarea>

而不是

<textarea defaultValue={'Some text'} />

對於select字段,不使用

<select>
 <option value="x" selected>
 ...
 </option>
</select>

而使用

<select defaultValue="x">
 <option value="x">...</option>
</select>

更一致的onChange事件

將函數傳遞給onChange屬性,您可以訂閱表單字段上的事件。

它在不同的字段上以一致的方式工作,即使是radioselectcheckbox輸入字段也會觸發onChange事件。

inputtextarea字段中輸入字符時,onChange事件也會觸發。

JSX自動轉義

為了減輕XSS攻擊的風險,JSX強制在表達式中自動轉義。

這意味著在字符串表達式中使用HTML實體時,可能會遇到問題。

您期望以下代碼將打印© 2020

<p>{'&copy; 2020'}</p>

但它實際上打印的是&copy; 2020,因為字符串已轉義。

要解決此問題,您可以將實體移出表達式:

<p>&copy; 2020</p>

或者通過使用打印HTML實體所對應的Unicode表示來使用常量:

<p>{'\u00A9 2020'}</p>

JSX中的空格

在JSX中添加空格有兩個規則:

水平空格被修剪為1個

如果在同一行上的元素之間有空格,則所有空格都被修剪為1個空格。

<p>Something becomes this</p>

變成

<p>Something becomes  this</p>

垂直空格被消除

<p>
 Something
 becomes
 this
</p>

變成

<p>Somethingbecomesthis</p>

要解決此問題,您需要明確添加空格,只需添加一個空格表達式,如下所示:

<p>
 Something
 {' '}becomes
 {' '}this
</p>

或者通過在空格表達式中嵌入字符串來解決此問題:

<p>
 Something
 {' becomes '}
 this
</p>

在JSX中添加註釋

您可以使用正常的JavaScript註釋在表達式中添加註釋:

<p>
 {/\* a comment \*/}
 {
 //another comment
 }
</p>

擴展屬性

在JSX中,常見的操作是為屬性分配值。

而不是手動執行,例如

<div>
 <BlogPost title={data.title} date={data.date} />
</div>

您可以使用

<div>
 <BlogPost {...data} />
</div>

並且data對像的屬性將自動用作屬性,這得益於ES6的展開運算符

如何在JSX中循環

如果您有一組元素需要進行循環以生成JSX片段,可以創建循環,然後將JSX添加到數組中:

const elements = [] //..一些數組

const items = []

for (const [index, value] of elements.entries()) {
 items.push(<Element key={index} />)
}

現在在渲染JSX時,您可以將items數組嵌入其中,方法是將其放在大括號中:

const elements = ['one', 'two', 'three'];

const items = []

for (const [index, value] of elements.entries()) {
 items.push(<li key={index}>{value}</li>)
}

return (
 <div>
 {items}
 </div>
)

您也可以直接在JSX中執行相同操作,使用map而不是for-of循環:

const elements = ['one', 'two', 'three'];
return (
 <ul>
 {elements.map((value, index) => {
 return <li key={index}>{value}</li>
 })}
 </ul>
)