JSX語法詳解


一、基礎
1、JSX是什么
JSX是一種像下面這樣的語法:

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

它是一種JavaScript語法擴展,在React中可以方便地用來描述UI。

本質上,JSX為我們提供了創建React元素方法(React.createElement(component, props, ...children))的語法糖(syntactic sugar)。上面的代碼實質上等價於:

var element = React.createElement(
"h1",
null,
"Hello, world!"
);

2、JSX代表JS對象
JSX本身也是一個表達式,在編譯后,JSX表達式會變成普通的JavaScript對象。

你可以在if語句或for循環中使用JSX,你可以將它賦值給變量,你可以將它作為參數接收,你也可以在函數中返回JSX。

例如下面的代碼:

function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;

上面的代碼在if語句中使用JSX,並將JSX作為函數返回值。實際上,這些JSX經過編譯后都會變成JavaScript對象。

經過babel會變成下面的js代碼:

function test(user) {
if (user) {
return React.createElement(
"h1",
null,
"Hello, ",
formatStr(user),
"!"
);
}
return React.createElement(
"h1",
null,
"Hello, Stranger."
);
}

3、在JSX中使用JavaScript表達式
在JSX中插入JavaScript表達式十分簡單,直接在JSX中將JS表達式用大括號括起來即可。例如:

function formatName(user) {
return user.firstName + ' ' + user.lastName;
}

const user = {
firstName: 'Harper',
lastName: 'Perez'
};

const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);

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

上面的代碼中用到了函數調用表達式fromatName(user)。

在JavaScript中,表達式就是一個短語,Javascript解釋器會將其計算出一個結果,常量就是最簡單的一類表達式。常用的表達式有:

變量名;
函數定義表達式;
屬性訪問表達式;
函數調用表達式;
算數表達式;
關系表達式;
邏輯表達式;
需要注意的是,if語句以及for循環不是JavaScript表達式,不能直接作為表達式寫在{}中,但可以先將其賦值給一個變量(變量是一個JavaScript表達式):

function NumberDescriber(props) {
let description;
if (props.number % 2 == 0) {
description = <strong>even</strong>;
} else {
description = <i>odd</i>;
}
return <div>{props.number} is an {description} number</div>;
}

4、JSX屬性值
你可以使用引號將字符串字面量指定為屬性值:

const element = <div tabIndex="0"></div>;

注意這里的”0”是一個字符串字面量。

或者你可以將一個JavaScript表達式嵌在一個大括號中作為屬性值:

const element = <img src={user.avatarUrl}></img>;

這里用到的是JavaScript屬性訪問表達式,上面的代碼將編譯為:

const element = React.createElement("img", { src: user.avatarUrl });

5、JSX的Children
首先JSX可以是一個不包含Children的empty tag。如:

const element = <img src={user.avatarUrl} />;
1
JSX也可以像HTML標簽一樣包含Children:

const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);

這種寫法在生成React元素的時候給我們帶來了很大的便利,而且能夠更加直觀地描述UI。不然我們需要像下面這樣創建和上面代碼等價的React元素:

const element = React.createElement(
"div",
null,
React.createElement(
"h1",
null,
"Hello!"
),
React.createElement(
"h2",
null,
"Good to see you here."
)
);

tip: React DOM結點使用駱駝拼寫法給屬性命名

例如:class在JSX中應寫作className,tabindex應寫作tabIndex。

另外關於JSX的children需要注意的是:

React自定義組件的chilren是不會像固有的HTML標簽的子元素那樣自動render的,我們看下面的例子:

代碼1
class Test extends React.Component {
render() {
return (
<div>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
)
}
};
ReactDOM.render(
<Test />,
document.getElementById('test')
);

以上代碼定義的組件中都是build-in組件,類似div、p、ul、li等。它們中的子元素會直接render出來,像下面這樣:

 

但是如果你使用用戶定義組件,比如:

class Test extends React.Component {
render() {
return (
<Em>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</Em>
)
}
};

class Em extends React.Component {
render() {
return (<div></div>);
}
}

ReactDOM.render(
<Test />,
document.getElementById('test')
);

並不能得到跟上面代碼1一樣的結果,我們得到的只是一個空的div標簽:

 

如果你想得到和代碼1一樣的結果,需要顯示地指定props.children,像下面這樣:

class Test extends React.Component {
render() {
return (
<Em>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</Em>
)
}
};

class Em extends React.Component {
render() {
return (<div>{this.props.children}</div>);
}
}

ReactDOM.render(
<Test />,
document.getElementById('test')
);

得到下面的結果:

 

6、JSX可自動防范注入攻擊
在JSX中嵌入接收到的內容是安全的,比如:

const danger = response.potentialDanger;

cosnt ele = <h1>{title}</h1>

在默認情況下,React DOM會將所有嵌入JSX的值進行編碼。這樣可以有效避免xss攻擊。

我們將以下代碼編譯后引入html:

class Test extends React.Component {
render() {
let v = "<script><\/script>";
return (
<div>
<h1>{v}</h1>
</div>
)
}
};

ReactDOM.render(
<Test />,
document.getElementById('test')
);

得到結果是:

 

可以看到通過JSX插入的文本自動進行了HTML轉義,所以這里插入的是一段文本,而不是<script>標簽。這有點類似於Js中的document.createTextNode("...")(實際上我們可以利用document.createTextNode進行HTML轉義)。

作為對比,換作使用DOM元素的innerHTML屬性:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="test"></div>
<script type="text/javascript">
document.getElementById("test").innerHTML="<h1><script><\/script><\/h1>";
</script>
</body>
</html>

得到結果如下:

 

注意文本的顏色,此時插入的是一個<script>標簽。

如果你很清楚自己在做什么,希望直接將字符串不經轉義編碼直接插入到HTML文檔流,可以使用dangerouslySetInnerHTML屬性,這是一個React版的innerHTML,該屬性接收一個key為__html的對象,修改代碼如下:

class Test extends React.Component {
render() {
let v = {
__html: "<script><\/script>"
};
return (
<div>
<h1 dangerouslySetInnerHTML={v}/>
</div>
)
}
};

這次我們插入了<script>標簽:

 

二、進階
1、JSX中的props
指定JSX中的props有以下幾種方式:

(1)使用JavaScript表達式
任何有效的JavaScript表達式都可以作為prop的值,使用的時候將該表達式放在一對大括號中即可:

<MyComponent foo={1 + 2 + 3 + 4} />

<YourComponent clickTodo={(id) => this.props.handleClick(id)} />

(2)使用字符串字面量
字符串字面量可以作為prop值,下面的代碼是等價的:

<MyComponent message="hello world" />

<MyComponent message={'hello world'} />

(3)使用擴展運算符
如果你想將一個prop對象傳入JSX,你可以使用擴展運算符...直接將整個prop對象傳入。下面的2個組件是等價的:

function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}

擴展運算符是一個es6特性。是一種傳遞屬性的十分便利的方式。但請注意不要濫用該運算符,注意不要將一大堆毫不相關的prop一股腦全部傳入下面的組件中。

2、JSX中的Children
React組件中有一個特殊的prop–props.children。它指代了JSX表達式中開閉標簽中包含的內容。

下面討論的是幾種指定JSX的children的方法:

(1)使用字符串字面量
你可以直接在JSX的開閉標簽中寫入字符串字面量,組件得到的props.children就是該字符串值。

以下面的代碼為例:

<MyComponent>Hello world!</MyComponent>
1
MyComponent的props.chilren將獲得”Hello World!”字符串。通過該方式傳入的字符串是未經HTML轉義的。實際上你只需要像在HTML標簽中寫入文本那樣就可以了。例如你想在一對<p>標簽中寫入文本”<script></script>”,HTML和JSX寫法是一樣的,就像下面這樣:

<p>&#60;script&#62;&#60;/script&#62;</p>
1
另外需要注意的是:

JXS會自動刪除一行中開頭和結尾處的空白符;JSX會自動刪除空行;JSX會刪除緊鄰標簽的換行;JSX會刪除字符串中的換行;字符串中的換行會被轉換成一個空格。

舉例來說,下面的JSX代碼都是等價的:

<div>Hello World</div>

<div> Hello World </div>

<div>
Hello World
</div>

<div>
Hello
World
</div>

<div>

Hello World
</div>

(2)JSX元素作為children
我們同樣可以使用JSX元素作為JSX的children,由此生成嵌套組件:

<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>

我們也可以混合使用字符串字面量和JSX作為children:

<El>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</El>

El的props.children將得到一個數組:

 

可以看到數組的第一個元素就是字符串“Here is a list:”,第二個元素是一個對象(JSX代表JavaScript對象)。

(3)JavaScript表達式
和prop一樣,你也可以將任何有效的JavaScript表達式作為children傳入,將它放在{}中就可以了。像下面這樣:

<MyComponent>{'foo'}</MyComponent>

這里傳入了一個常量表達式。

下面使用一個函數調用表達式來生成一個list作為children:

function Item(props) {
return <li>{props.message}</li>;
}

function TodoList() {
const todos = ['finish doc', 'submit pr', 'nag dan to review'];
return (
<ul>
{todos.map((message) => <Item key={message} message={message} />)}
</ul>
);
}

當然你也可以在一個字符串children中插入一個JavaScript表達式來生成一個“模板”:

function Hello(props) {
return <div>Hello {props.username}!</div>;
}

(4)函數children
首先說明,這不是一種常見的用法。

實際上,傳入自定義組件的children並沒有嚴格的限制,只要在React需要render的時候能將它們轉換成可以render的東西就行了。

下面是一個函數children的例子:

function ListOfTenThings() {
return (
<Repeat numTimes={10}>
{(index) => <div key={index}>This is item {index} in the list</div>}
</Repeat>
);
}

// Calls the children callback numTimes to produce a repeated component
function Repeat(props) {
let items = [];
for (let i = 0; i < props.numTimes; i++) {
items.push(props.children(i));
}
return <div>{items}</div>;
}


實際上,我們更通常的情況下是將(index) => <div key={index}>This is item {index} in the list</div>作為一個prop傳入子組件。這個例子只是作為一種理解上的擴展。

(5)有關布爾值、Null以及Undefined
布爾值,Null以及Undefined可以作為有效的children,但他們不會被render,下面的JSX表達式都會render一個空的div標簽:

<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{true}</div>

關於此有一個有趣的應用,在條件render中,下面的<Header />只有在show為true時才會render:

<div>
{showHeader && <Header />}
<Content />

3、注意事項
(1)使用JSX時要引入React庫
前面已經解釋過了,JSX是React.createElement方法的語法糖,因此在使用JSX的作用域中必須引入React庫。

如果你使用了JS打包工具,你可以在文件的頭部作如下引用:

import React from 'react';
1
或者你不使用打包工具,也可以直接通過script標簽引入React,比如:

//本地
<script src="./react.js"></script>

//或者BootCDN
<script src="http://cdn.bootcss.com/react/15.4.0/react.js"></script>

此時React將作為一個全局變量被引入,變量名就是’React’。

(2)注意引入JSX中用到的自定義組件
JSX中用到的組件可能並不會在JavaScript中直接引用到,但自定義組件本質上就是一個JS對象,你在JSX中使用的時候,需要首先將該組件引入到當前作用域:

import MyComponent from './MyComponent.js'

...

<Outer>
<MyComponent />
</Outer>

(3)自定義組件首字母一定要大寫
JSX中小寫字母開頭的element代表HTML固有組件如div,span,p,ul等。用戶自定義組件首字母一定要大寫如<Header>、<Picker>。

(4)元素標簽名不能使用表達式
下面的代碼將產生錯誤:

const components = {
photo: PhotoStory,
video: VideoStory
};

function Story(props) {
// Wrong! JSX標簽名不能使用表達式
return <components[props.storyType] story={props.story} />;
}

如果你需要使用一個表達式來決定元素標簽,你應該先將該表達式的值賦給一個大寫字母開頭的變量:

const components = {
photo: PhotoStory,
video: VideoStory
};

function Story(props) {
// Correct! JSX type can be a capitalized variable.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}

(5)設置style屬性
在設置標簽style屬性的時候,要注意,我們是將一個描述style的對象以JavaScipt表達式的形式傳入。因此應該有2層大括號:

<div style={{color:'red', margin:'10px auto'}}></div>

 


免責聲明!

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



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