React JSX
React 提供了稱作 JSX 的語法,作為 JavaScript extension syntax,JSX 是一種跟 HTML/XML 很相似的語法,用來寫所謂的 React elements (React 元素)。
JSX 的語法,舉例像是這個簡單的 JavaScript 變數宣告:
const myReactEle = <h1>Hello, world!</h1>;
<h1>Hello, world!</h1>
這一段 code 就是所謂的 JSX,注意它不是字串喔 (前後沒有引號包著),而 myReactEle
這個變數的值則是一個 React element。
JSX 是 React 的樣板語言 (template language),你可以把 JSX 想像成是 JavaScript 的 HTML DOM literal 表示法,這樣你就會更好理解 JSX 了!
傳統來說,我們常會將 JavaScript 中會用到的 HTML template 散佈在各處,可能是放在 <script>
tag 裡面,或放在幾個 template 文件檔案中,然後再用 JavaScript 將這些 template string 取出來 parse 使用,嚴格的執行邏輯 (JavaScript)、樣式 (CSS)、和內容 (HTML) 分離 (separation)。
但 React 的想法則是相反,React 以元件 (components) 為中心思考,認為一個元件的顯示邏輯 (HTML) 和 UI 運作邏輯 (JavaScript) 應該是緊密相關,應該綁在一起的才對,所以有了 JSX。
JSX 第一眼看你可能會覺得怪,但當你開始習慣 React 後,你會覺得 HTML 和 JavaScript 放在一起其實沒什麼問題,反而有好處,讓你一眼看你的 component code 就能夠有完整的資訊,可以知道這個 component 的長相、結構和邏輯是怎樣子的,code 也好維護。
JSX 的背後其實就只是 JavaScript 而已,因為 JSX 不是瀏覽器認識的語法,所以我們還會需要透過 webpack 或 Babel 等工具來將 JSX 轉成單純的 JavaScript 程式碼 - 轉成 React library 的 React.createElement()
function calls。
例如以這個例子 JSX:
const element = <h1>Hello, world!</h1>;
透過 Babel 最後會轉成像這樣子的 JavaScript code:
var element = React.createElement(
"h1",
null,
"Hello, world!"
);
看到這邊有沒有更感受到 JSX 帶來的好處?除了可以很直觀的用類 HTML 的語法來描述介面,同時你也不用寫一堆雜亂又冗長的 React.createElement( React.createElement( React.createElement( ... ) ) )
code。
JSX 語法 (Syntax)
先簡單說,JSX 就是 HTML/XML + JavaScript。
除了 HTML,你可以在 JSX 中使用任何 JavaScript expression - 例如 2 + 2
, user.firstName
, formatName(user)
- 在兩個大括號 { }
之間。
例如:
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
其中 {formatName(user)}
的地方就會被取代成 formatName function 的返回值。
( )
不是必要的,只是個好習慣,避免意外錯誤發生。JSX 可以是巢狀的元素 (nested elements):
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
留意一件事,在 React 16 以前 (React 15),你需要把 children elements 包在一個 container element 下面喔,像上例中最外層的 <div></div>
。
而從 React 16 起,新增了 Fragment 的功能,讓你不需要在 DOM 中增加這個額外節點了:
const element = (
<>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</>
);
就像 XML,如果遇到一個 empty tag,你需要主動 close />
:
// 正確
const element = <img src={user.avatarUrl} />;
// 錯誤
const element = <img src={user.avatarUrl}>;
標籤名稱大小寫的差別 (Tags Naming Convention)
用 JSX 寫 React elements DOM 結構時,如果是一般的 HTML tags 則用小寫 (lowercase names)。
例如:
const element = <div />;
但如果是 React Component,則 tag 名稱首字用大寫 (capital letter)。
例如:
const element = <Welcome name="Sara" />;
JSX 是 JavaScript expression
JSX 本質是一個 JavaScript expression,所以你可以用在 if 條件、for 迴圈、指定給變數、當作函數參數或返回值等。
例如:
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
在 JSX 中使用 if
條件式
因為 { }
中只能放 JavaScript expression,所以在 JSX 你是無法使用 if (if 是一個 JavaScript statement) 的,但是你可以用 JavaScript 三元運算子。
例如:
class MyComponent extends React.Component {
render() {
return <p>Hello {this.props.someVar ? 'World' : 'Mike'}</p>;
}
}
JSX 的屬性 (attributes)
跟 HTML 一樣 JSX 的 tag 可以有屬性:
const element = <div tabIndex="0"></div>;
在 React 15,如果是自定義的屬性 (custom DOM attribute),你需要使用 data-*
:
const element = <div data-myattribute="somevalue" />;
而從 React 16 開始允許你可以用任意的自定義屬性:
const element = <div myattribute="somevalue" />;
當使用 " "
包住的屬性值,它的 type 是字串型態 (string)。不過你可以用 { }
來給不同的 type 或動態變數的屬性值:
const element = <img src={user.avatarUrl} />;
在 JSX 中,如果屬性沒設定值,預設值為 true
:
const element = <input type="button" disabled />;
// 意思等同於
const element = <input type="button" disabled={true} />;
而如果沒設定某個屬性,預設值則為 false
:
const element = <input type="button" />;
// 意思等同於
const element = <input type="button" disabled={false} />;
class
/ className
; for
/ htmlFor
因為 JSX 底層其實就只是 JavaScript,同時 React 的 API 遵循著 JavaScript DOM API,所以屬性名稱也是按照 JavaScript 原生的語法。
依此原則,舉例如 HTML 中常用到的 class attribute 在 JSX 中則要使用 className;而 for 則是要用 htmlFor。
例如:
const element = <h1 className="title">Hello, world!</h1>;
Spread Attributes
你可以用 ES6 的 Spread Operator 來方便設定屬性。
例如:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
此外,同一個 tag 後面的屬性值會蓋掉前面相同名稱的屬性值,而這特性可以用來做 default values:
var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;
// 會顯示 override
console.log(component.props.foo);
Inline Styles (CSS 樣式)
JSX 的 inline style 樣式語法跟你用 JavaScript DOM API 是一樣的 (camelCased properties)。
例子:
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
}
但 JSX 不會自動幫你處理跨瀏覽器相容問題喔,你需要自己處理:
const divStyle = {
WebkitTransition: 'all',
msTransition: 'all'
};
function ComponentWithTransition() {
return <div style={divStyle}>This should work cross-browser</div>;
}
如果值是數字,JSX 會幫你加上 "px" 單位,若你不想用 px 你就得明確指定單位:
// 值同 10px
<div style={{ height: 10 }}>
Hello World!
</div>
// 明確指定單位 %
<div style={{ height: '10%' }}>
Hello World!
</div>
注意有兩個括號 {{ }}
,第一個 {}
是 JSX 語法的 {}
,而第二個 {}
表示屬性值是一個 JavaScript object。
JSX 註解 (Comments)
你可以用 /* */
和 //
在 { }
中作為註解。
各種註解寫法的例子:
const element = (
<p>
{/* 單行註解 */}
{// 多行註解
// 最後的 } 不能和 // 在同一行
// ...
}
{/* 多行註解
....
...
*/
}
{/* 多行註解
最後的 } 可以和 */ 在同一行
...
*/}
</p>
);
dangerouslySetInnerHTML
基於安全的考量,React 對於插入 {}
中的字串都會自動幫你做 HTML escape 來避免 XSS (cross-site-scripting) 安全攻擊。
例如下方的例子對於 XSS 攻擊是安全的,因為 React 會幫 title 的值做 HTML escape:
const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;
如果你想要像使用 innerHTML 一樣可以直接塞入任意 HTML,你可以用 React 提供的 dangerouslySetInnerHTML 這個屬性,它接受一個帶有 __html key 的物件。
像是這樣子使用:
function createMarkup() {
return {__html: 'First · Second'};
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}