為什么要引入 JSX 這種語法
傳統(tǒng)的 MVC 是將模板放在其他地方型型,比如 <script> 標簽或者模板文件段审,再在 JS 中通過某種手段引用模板。按照這種思路闹蒜,想想多少次我們面對四處分散的模板片段不知所措寺枉?糾結模板引擎,糾結模板存放位置绷落,糾結如何引用模板……下面是一段 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.
簡單來說姥闪,React認為組件才是王道,而組件是和模板緊密關聯(lián)的砌烁,組件模板和組件邏輯分離讓問題復雜化了筐喳。顯而易見的道理催式,關鍵是怎么做?
所以就有了 JSX 這種語法避归,就是為了把 HTML 模板直接嵌入到 JS代碼里面荣月,這樣就做到了模板和組件關聯(lián),但是 JS 不支持這種包含 HTML 的語法梳毙,所以需要通過工具將 JSX 編譯輸出成 JS 代碼才能使用哺窄。
JSX 是可選的
因為 JSX 最終是輸出成 JS 代碼來表達的,所以我們可以直接用 React 提供的這些 DOM 構建方法來寫模板账锹,比如一個 JSX 寫的一個鏈接:
<a >Hello!</a>
用 JS 代碼來寫就成這樣了:
React.createElement('a', {href: 'http://facebook.github.io/react/'}, 'Hello!')
你可以通過 React.createElement 來構造組件的 DOM 樹萌业。第一個參數(shù)是標簽名,第二個參數(shù)是屬性對象牌废,第三個參數(shù)是子元素。
一個包含子元素的例子:
var child = React.createElement('li', null, 'Text Content');
var root = React.createElement('ul', { className: 'my-list' }, child);
React.render(root, document.body);
對于常見的 HTML 標簽啤握,React 已經(jīng)內(nèi)置了工廠方法:
var root = React.DOM.ul({ className: 'my-list' },
React.DOM.li(null, 'Text Content')
);
所以 JSX 和 JS 之間的轉換也很簡單直觀鸟缕,用 JSX 的好處就是它基本上就是HTML(后面會講到有一些小差異),對于構造 DOM 來說我們更熟悉排抬,更具可讀性懂从。
關于 JSX 映射成 JS 對象,也就是 Virtual DOM 的內(nèi)部描述蹲蒲,參見Virtual DOMTerminology番甩,如果你不想使用JSX,直接使用 JS 就是用這里面提到的接口方法届搁。
使用 JSX
利用 JSX 編寫 DOM 結構缘薛,可以用原生的 HTML 標簽,也可以直接像普通標簽一樣引用 React組件卡睦。這兩者約定通過大小寫來區(qū)分宴胧,小寫的字符串是 HTML 標簽,大寫開頭的變量是 React 組件表锻。
使用 HTML 標簽:
import React from 'react';
import { render } from 'react-dom';
var myDivElement = <div className="foo" />;
render(myDivElement, document.getElementById('mountNode'));
HTML 里的 class 在 JSX 里要寫成 className恕齐,因為 class 在 JS 里是保留關鍵字。同理某些屬性比如 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 表達式
屬性值使用表達式显歧,只要用 {} 替換 "":
// Input (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// Output (JS):
var person = React.createElement(
Person,
{name: window.isLoggedIn ? window.name : ''}
);
子組件也可以作為表達式使用:
// 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 里使用注釋也很簡單,就是沿用 JavaScript确镊,唯一要注意的是在一個組件的子元素位置使用注釋要用 {} 包起來士骤。
var content = (
<Nav>
{/* child comment, put {} around */}
<Person
/* multi
line
comment */
name={window.isLoggedIn ? window.name : ''} // end of line comment
/>
</Nav>
);
HTML 轉義
React 會將所有要顯示到 DOM 的字符串轉義,防止 XSS蕾域。所以如果 JSX 中含有轉義后的實體字符比如 ? (?) 最后顯示到 DOM 中不會正確顯示敦间,因為 React 自動把 ? 中的特殊字符轉義了。有幾種解決辦法:
- 直接使用 UTF-8 字符 ?
- 使用對應字符的 Unicode編碼,查詢編碼
- 使用數(shù)組組裝 <div>{['cc ', <span>?</span>, ' 2015']}</div>
- 直接插入原始的 HTML
<div dangerouslySetInnerHTML={{__html: 'cc © 2015'}} />
自定義 HTML 屬性
如果在 JSX 中使用的屬性不存在于 HTML的規(guī)范中廓块,這個屬性會被忽略厢绝。如果要使用自定義屬性,可以用data-前綴带猴。
可訪問性屬性的前綴aria-也是支持的昔汉。
支持的標簽和屬性
如果你要使用的某些標簽或屬性不在這些支持列表里面就可能被 React 忽略,必須要使用的話可以提 issue拴清,或者用前面提到的 dangerouslySetInnerHTML靶病。
屬性擴散
有時候你需要給組件設置多個屬性,你不想一個個寫下這些屬性口予,或者有時候你甚至不知道這些屬性的名稱娄周,這時候 spreadattributes 的功能就很有用了。
比如:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
props 對象的屬性會被設置成 Component 的屬性沪停。
屬性也可以被覆蓋:
var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.foo); // 'override'
寫在后面的屬性值會覆蓋前面的屬性煤辨。
關于 ... 操作符
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 屬性構成的 JS 對象
- onChange 事件表現(xiàn)更接近我們的直覺(不需要 onBlur 去觸發(fā))
- 表單的表現(xiàn)差異比較大木张,要單獨再講
更多異同众辨,可以參見 DOMDifferences