為什么要引入 JSX 這種語法
傳統(tǒng)的 MVC 是將模板放在其他地方,比如 <script> 標(biāo)簽或者模板文件,再在 JS 中通過某種手段引用模板。按照這種思路,想想多少次我們面對(duì)四處分散的模板片段不知所措?糾結(jié)模板引擎,糾結(jié)模板存放位置,糾結(jié)如何引用模板……下面是一段 React 官方的看法:
We strongly believe that components are the right way to separate concerns rather than "templates" and "display logic." We think that markup and the code that generates it are intimately tied together. Additionally, display logic is often very complex and using template languages to express it becomes cumbersome.
簡(jiǎn)單來說,React認(rèn)為組件才是王道,而組件是和模板緊密關(guān)聯(lián)的,組件模板和組件邏輯分離讓問題復(fù)雜化了。顯而易見的道理,關(guān)鍵是怎么做?
所以就有了 JSX 這種語法,就是為了把 HTML 模板直接嵌入到 JS代碼里面,這樣就做到了模板和組件關(guān)聯(lián),但是 JS 不支持這種包含 HTML 的語法,所以需要通過工具將 JSX 編譯輸出成 JS 代碼才能使用。
JSX 是可選的
因?yàn)?JSX 最終是輸出成 JS 代碼來表達(dá)的,所以我們可以直接用 React 提供的這些 DOM 構(gòu)建方法來寫模板,比如一個(gè) JSX 寫的一個(gè)鏈接:
<a >Hello!</a>
用 JS 代碼來寫就成這樣了:
React.createElement('a', {href: 'http://facebook.github.io/react/'}, 'Hello!')
你可以通過 React.createElement 來構(gòu)造組件的 DOM 樹。第一個(gè)參數(shù)是標(biāo)簽名,第二個(gè)參數(shù)是屬性對(duì)象,第三個(gè)參數(shù)是子元素。
一個(gè)包含子元素的例子:
var child = React.createElement('li', null, 'Text Content');
var root = React.createElement('ul', { className: 'my-list' }, child);
React.render(root, document.body);
對(duì)于常見的 HTML 標(biāo)簽,React 已經(jīng)內(nèi)置了工廠方法:
var root = React.DOM.ul({ className: 'my-list' },
React.DOM.li(null, 'Text Content')
);
所以 JSX 和 JS 之間的轉(zhuǎn)換也很簡(jiǎn)單直觀,用 JSX 的好處就是它基本上就是HTML(后面會(huì)講到有一些小差異),對(duì)于構(gòu)造 DOM 來說我們更熟悉,更具可讀性。
關(guān)于 JSX 映射成 JS 對(duì)象,也就是 Virtual DOM 的內(nèi)部描述,參見Virtual DOMTerminology,如果你不想使用JSX,直接使用 JS 就是用這里面提到的接口方法。
使用 JSX
利用 JSX 編寫 DOM 結(jié)構(gòu),可以用原生的 HTML 標(biāo)簽,也可以直接像普通標(biāo)簽一樣引用 React組件。這兩者約定通過大小寫來區(qū)分,小寫的字符串是 HTML 標(biāo)簽,大寫開頭的變量是 React 組件。
使用 HTML 標(biāo)簽:
import React from 'react';
import { render } from 'react-dom';
var myDivElement = <div className="foo" />;
render(myDivElement, document.getElementById('mountNode'));
HTML 里的 class 在 JSX 里要寫成 className,因?yàn)?class 在 JS 里是保留關(guān)鍵字。同理某些屬性比如 for 要寫成 htmlFor。
使用組件:
import React from 'react';
import { render } from 'react-dom';
import MyComponent from './MyComponet';
var myElement = <MyComponent someProperty={true} />;
render(myElement, document.body);
使用 JavaScript 表達(dá)式
屬性值使用表達(dá)式,只要用 {} 替換 "":
// Input (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// Output (JS):
var person = React.createElement(
Person,
{name: window.isLoggedIn ? window.name : ''}
);
子組件也可以作為表達(dá)式使用:
// Input (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// Output (JS):
var content = React.createElement(
Container,
null,
window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);
注釋
在 JSX 里使用注釋也很簡(jiǎn)單,就是沿用 JavaScript,唯一要注意的是在一個(gè)組件的子元素位置使用注釋要用 {} 包起來。
var content = (
<Nav>
{/* child comment, put {} around */}
<Person
/* multi
line
comment */
name={window.isLoggedIn ? window.name : ''} // end of line comment
/>
</Nav>
);
HTML 轉(zhuǎn)義
React 會(huì)將所有要顯示到 DOM 的字符串轉(zhuǎn)義,防止 XSS。所以如果 JSX 中含有轉(zhuǎn)義后的實(shí)體字符比如 ? (?) 最后顯示到 DOM 中不會(huì)正確顯示,因?yàn)?React 自動(dòng)把 ? 中的特殊字符轉(zhuǎn)義了。有幾種解決辦法:
- 直接使用 UTF-8 字符 ?
- 使用對(duì)應(yīng)字符的 Unicode編碼,查詢編碼
- 使用數(shù)組組裝 <div>{['cc ', <span>?</span>, ' 2015']}</div>
- 直接插入原始的 HTML
<div dangerouslySetInnerHTML={{__html: 'cc © 2015'}} />
自定義 HTML 屬性
如果在 JSX 中使用的屬性不存在于 HTML的規(guī)范中,這個(gè)屬性會(huì)被忽略。如果要使用自定義屬性,可以用data-前綴。
可訪問性屬性的前綴aria-也是支持的。
支持的標(biāo)簽和屬性
如果你要使用的某些標(biāo)簽或?qū)傩圆辉谶@些支持列表里面就可能被 React 忽略,必須要使用的話可以提 issue,或者用前面提到的 dangerouslySetInnerHTML。
屬性擴(kuò)散
有時(shí)候你需要給組件設(shè)置多個(gè)屬性,你不想一個(gè)個(gè)寫下這些屬性,或者有時(shí)候你甚至不知道這些屬性的名稱,這時(shí)候 spreadattributes 的功能就很有用了。
比如:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
props 對(duì)象的屬性會(huì)被設(shè)置成 Component 的屬性。
屬性也可以被覆蓋:
var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.foo); // 'override'
寫在后面的屬性值會(huì)覆蓋前面的屬性。
關(guān)于 ... 操作符
The...operator (or spread operator) is already supported for arrays in ES6.There is also an ES7 proposal for Object Rest and SpreadProperties.
JSX 與 HTML 的差異
除了前面提到的 class 要寫成 className ,比較典型的還有:
- style 屬性接受由 CSS 屬性構(gòu)成的 JS 對(duì)象
- onChange 事件表現(xiàn)更接近我們的直覺(不需要 onBlur 去觸發(fā))
- 表單的表現(xiàn)差異比較大,要單獨(dú)再講
更多異同,可以參見 DOMDifferences