深入 JSX
JSX 是一種在 Javascript 中使用的 XML 語法,目的是用來轉換成原生的 Javascript。React 官方推薦使用。
是為了簡化類似 OOP 實例化多層架構物件。舉個例子像下面的虛擬碼
List l = new List();
Item i1 = new Item();
Item i2 = new Item();
l.Add(i1);
i.Add(i2);
這樣如果情況更複雜的時候會很難維護,換個方式如果是用 XML 的方式
<List>
<Item name='i1' />
<Item name='i2' />
</List>
感覺會比較好維護。 JSX 就是把下面的 XML 語法轉換成 Javascript 的一種工具。
注意: 不要忘記在程式碼開頭的 /** @jsx React.DOM */ 他不是一般的註解,這是告訴 JSX 編譯器要編譯這個檔案給 React 使用。
如果你沒有加入這段編譯指示,程式將不會被編譯。因此您可以安心的使用 JSX transformer,因為它並不會隨便編譯其他的 Javascript 檔案。
透過觀察下面這段簡單的程式碼,我們試著把 /** @jsx React.DOM */ 移除。會發現整段 <script type='text/jsx'> 都不會執行。其他功能都不會被影響。所以在已有的專案裡面使用 React ,就算他出錯了基本上也不會導致其他功能異常。
<!-- ex-1.html -->
<title>Hello React</title>
<script src="http://fb.me/react-0.8.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.8.0.js"></script>
<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
<div id='content'></div>
<h1>Static H1</h1>
<script>
$(function () {
$('h1').click(function () {
console.log('h1 click');
});
});
</script>
<script type='text/jsx'>
/** @jsx React.DOM */
console.log('jsx');
var HelloWorld = React.createClass({
render: function () {
return (
<div>Hello World</div>
)
}
});
React.renderComponent(<HelloWorld />, document.getElementById('content'));
</script>
為什麼使用 JSX ?
JSX 並不是強迫使用的,在不使用 JSX 的情況下,我們就得使用 React.DOM 提供的函式來建立註記標簽。上一篇我們提到 React 除非必要,平常不會直接操作 DOM 元素。它採用在內部模擬 DOM 來進行比較,計算如何有效率的操作 DOM 的方式在運作。使用 JSX 的最終目的是要幫助你用 XML 的編排方式轉成 React.DOM 的語法。
例如我們想建立一個超連結
var link = React.DOM.a({href: 'http://facebook.github.io/react'}, 'React');
如果情況在複雜一點可以比較一下下面的程式碼:
/* JSX*/
React.createClass({
render: function () {
return (
<div>
<h1>Title</h1>
<a href='http://andyyou.logdown.com/'>AndyYou Blog</a>
</div>
)
}
})
/* Origin */
React.createClass({
render: function () {
return (
React.DOM.div(null,
React.DOM.h1(null, "Title"),
React.DOM.a( {href:"http://andyyou.logdown.com/"}, "AndyYou Blog")
)
)
}
})
官方推薦使用 JSX 的理由如下:
* 可以輕易的檢視整個 DOM 的結構,就跟你使用 HTML 一樣。
* 方便維護修改。
* 概念上非常相似于 MXML 和 XAML。
關於轉換
JSX 的目的是從類似 XML 的語法轉換成原生的 Javascript。XML 標簽元素和屬性會被轉換成 function 和物件。看看下面的虛擬碼
var Nav;
// 編譯前 JSX
var app = <Nav color='blue' />
// 編譯後 JS
var app = Nav({color:'blue'})
注意: 為了能正確的使用 <Nav />,Nav 變數必須在 scope 裡。
JSX 也可以在元素中嵌入子元素。
var Nav, Profile;
// Input (JSX):
var app = <Nav color="blue"><Profile>click</Profile></Nav>;
// Output (JS):
var app = Nav({color:"blue"}, Profile(null, "click"));
我們根據上面的說明實際撰寫一段可以運作的程式碼如下:
/** @jsx React.DOM */
var Profile = React.createClass({
render: function () {
return (
<a href='http://andyyou.logdown.com/'>AndyYou</a>
);
}
});
var Nav = React.createClass({
render: function () {
return (
<nav>
<li><Profile /></li>
<li>Home</li>
<li>About</li>
</nav>
);
}
});
React.renderComponent(<Nav />, document.getElementById('nav'));
使用 JSX 編譯工具 就可以看到 JSX 是如何被轉換的。或者你也可以使用我們提供的線上工具。
查閱第一篇教學有關於如何使用編譯工具。
注意: 關於上面的虛擬碼是協助你理解關於 JSX 是如何轉換的,你不能夠直接使用虛擬碼的部分。
上面範例中加入了一段實作程式碼以方便學習實際演練。
React 和 JSX
React 和 JSX 是兩種獨立的技術,但 JSX 是 React 衍伸的一個重要的觀念。 JSX 正確來說被用在兩個地方:
* 構建 React DOM 元件(React.DOM.*)。
* 在使用 React.createClass() 設計元件結構。
React DOM 元件
如果要建立一個 <div> 的標記物件,實際上是使用 React.DOM.div。
var div = React.DOM.div;
var app = <div className='appClass'>Hello, React!</div>
React 組合元件
如果你想建立一個組合元件,你應該先透過 React.createClass({/*....*/}) 建立一個類別,然後才能實例化。
var MyComponent = React.createClass({/*...*/});
var app = <MyComponent someProperty={true} />;
JSX 會自動根據變數名稱或 displayName 推斷元件的名稱,而 displayName 一般會在 debug 時使用。
查閱複合式元件學習到更多關於多個元件組成的介紹。
注意:由於 JSX 是一種 Javascript,所以不能夠在標簽裡面直接使用 class 和 for 當作屬性名稱,替代的方式是 React 會用 className 和 htmlFor 取代。
易於使用的 DOM / 透過 JSX 使程式更加簡潔
如果每一個元素都要自行定義,事情肯定是非常單調乏味(舉例來說: div, span, h1, h2, ...)。JSX 提供一個便利的處理方式就是在 @jsx 註解的區塊指定一個變數,JSX 將會用這個指定的領域裡面搜尋 DOM 元件。
/**
* @jsx React.DOM
*/
// 有了上面的 @jsx React.DOM 就可以使用一般的 DOM 元素(div、span、h1、ul、li、table 等)
// 如果想用其他非 HTML 標簽才需要定義,如下面的Nav
var Nav;
// Input (JSX):
var tree = <Nav><span /></Nav>;
// Output (JS):
var tree = Nav(null, React.DOM.span(null));
提醒:JSX 只會單純的把元素轉成函式來呼叫,例如: React.DOM.div(),而且並不是表示 DOM 。在註解裡面指定的參數只是為了解決常用的標簽元素。一般來說 JSX 是不俱有 DOM 概念的。
Javascript 表達式
屬性表達式
為了使用 Javascript 來取得屬性的值,JSX 使用 {...} 大括號而不是 "..." 雙引號。
// Input (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// Output (JS):
var person = Person({name: window.isLoggedIn ? window.name : ''});
子元素表達式
同樣的,Javascript 也可以拿來放入子元素:
// Input (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// Output (JS):
var content = Container(null, window.isLoggedIn ? Nav(null) : Login(null));
註解
在 JSX 加入註解跟 Javascript 一樣。
var content = <Container>{/* this is a comment */}<Nav /></Container>;
注意:我們已經知道 JSX 會被轉成 Javascript,上面提到的註解,事實上並不能隨處亂加,舉個例子:
var Simple = React.createClass({
render: function () {
return (
<a href='http://andyyou.logdown.com/'>AndyYou</a>
);
}
});
var Container = React.createClass({
render: function () {
return (
<div>
<li><Simple />{ /* 亂加的註解 */}</li>
</div>
);
}
});
React.renderComponent(<Container />, document.getElementById('content'));
觀察被編譯過的檔案我們可以看到錯誤
var Simple = React.createClass({displayName: 'Simple',
render: function () {
return (
React.DOM.a( {href:"http://andyyou.logdown.com/"}, "AndyYou")
);
}
});
var Container = React.createClass({displayName: 'Container',
render: function () {
return (
React.DOM.div(null,
React.DOM.li(null, Simple(null ), /* 亂加的註解 */)
)
);
}
});
React.renderComponent(Container(null ), document.getElementById('content'));
重點
JSX 類似于其他嵌入 Javascript XML 式的語言或專案。JSX致力于下列的方向:
* JSX 專注于語法解析轉換。
* JSX 不相依於其他外部的函式庫。
* JSX 不會影響既有的 Javascript 語法。
JSX 類似於 HTML 但是並不是完全跟其相同,詳見 [JSX的陷阱]](http://facebook.github.io/react/docs/jsx-gotchas.html) 可以知道一些關鍵的不同。