通過 JSX Control Statements 編寫 JSX
一、前言
一些開發者,特別是有使用過「模板技術」的開發者(例如 Handlebars),剛開始嘗試使用 React 編寫應用時,可能會驚訝的發現,JSX 竟然沒有內建支持類似其它模板引擎似的任何結構控制語句或指令。但這就是 JSX,它就是這麼設計的,JSX 不是傳統的模板,也不需要某個模板引擎去解析。大體上,可以將 JSX 看成普通 JavaScript 表達式的語法糖。
下邊的的代碼是一個「普通的 JavaScript 代碼示例」
render () {n return (if(true){n ...n }else{n ...n });n}n
如上代碼不合法的,同樣,下邊的 jsx 代碼也是不合法的
render () {n return (<div>n {if(true) <span>1</span> else <span>2</span> }n </div>);n}n
因為,JavaScript 不能在一個「表達式」中嵌入「控制語句」。
二、能做什麼?
JSX-Control-Statements 為 JSX 增加了基本的結構控制語句,比如條件和循環控制語句。通過將插件將「組件風格」的控制語句最終轉換為普通 JS 代碼,例如:
<If condition={condition()}>Hello World!</If>n
將會轉換為
condition() ? Hello World! : nulln
三、實現原理及說明
這看起來,似乎很容易實現,通過一系列表組件好像就能實現,但事實上這在 React 中這是不可行的,如果用「組件」的方式實現,類似如下代碼
<ul>n <ForComponent each="item" index="index" of={list}>n <li key={index}>{item.title}</li>n </ForComponent>n</ul>n
組件的方式實現上邊的代碼,將會拋出來一個錯誤 Cannot read property title of undefined
因為 React 在執行到 ForComponent 時就是會執行它的 children ,想達到目的,需要延後執行 FormComponent 的 children,那麼只能用一個 function 包裹,並在這個函數中返回 jsx 表達式,但這將比直接寫 map 方法還麻煩。
所以,JSX Control Statements 是一個 Babel 插件,採取了在「編譯階段」將「控制標籤」轉換為「普通的 JavaScript 表達式」的方案,最終編譯結果和原來的寫法沒有什麼不同。
四、安裝
需要先行安裝好 Babel,之後通過 npm 安裝 jsx-control-statements
npm install --save-dev jsx-control-statementsn
還需要在 .babelrc 中配置插件,如下
{n ...n "plugins": ["jsx-control-statements"]n}n
五、核心語法
If 標籤
用來表示最簡單的條件判斷邏輯
// 簡單示例n<If condition={ true }>n <span>IfBlock</span>n</If>nn// 包括多個子元素及表達式n<If condition={ true }>n one { "two" }n <span>three</span>n <span>four</span>n</If>n
<If>
if 的 body 部分只有在 condition 為 true 才會渲染
屬性名類型必須conditionboolean是
<Else /> (deprecated)
Eles 已廢棄,不推薦使用,它會破壞 JSX/XML 的語法,並且影響自動格式化。
轉換
If 標籤將會預編譯為「三元表達式」
// 轉換前n<If condition={ test }>n <span>Truth</span>n</If>nn// 轉換後n{ test ? <span>Truth</span> : null }n
Choose 標籤
Choose 是比 If 更複雜分支結構寫法
<Choose>n <When condition={ test1 }>n <span>IfBlock</span>n </When>n <When condition={ test2 }>n <span>ElseIfBlock</span>n <span>Another ElseIfBlock</span>n <span>...</span>n </When>n <Otherwise>n <span>ElseBlock</span>n </Otherwise>n</Choose>nn// default block is optional; minimal example:n<Choose>n <When condition={true}>n <span>IfBlock</span>n </When>n</Choose>n
<Choose>
Choose 的子元素只允許出現 When 和 Otherwise,其中最少需要有一個 When,而 Otherwise 是可選的
<When>
和 <If> 類似
屬性名類型必須conditionboolean是
<Otherwise>
沒有任何一個 When 滿足條件時,將會渲染 Otherwise
轉換
Choose 標籤同樣將會預編譯為「三元表達式」
// 轉換前n<Choose>n <When condition={ test1 }>n <span>IfBlock1</span>n </When>n <When condition={ test2 }>n <span>IfBlock2</span>n </When>n <Otherwise>n <span>ElseBlock</span>n </Otherwise>n</Choose>nn// 轉換後n{ test1 ? <span>IfBlock1</span> : test2 ? <span>IfBlock2</span> : <span>ElseBlock</span> }n
For 標籤
For 的命名用方法,如下
// 注意,需要指定 key 屬性n <For each="item" of={ this.props.items }>n <span key={ item.id }>{ item.title }</span>n </For>nn <For each="item" index="idx" of={ [1,2,3] }>n <span key={ idx }>{ item }</span>n <span key={ idx + _2 }>Static Text</span>n </For>n
屬性名類型必須說明ofArray/collection是列表或包含 map 方法的集合eachstring循環「項」變數名indexstring循環「索引」變數名
注意,For 不能作為根元素
轉換
// 轉換前n<For each="item" index="index" of={ items )}>n <span key={ item.id }>{ index }. { item.title }</span>n</For>nn// 轉換前n{n items.map( function(item, index) {n <span key={ item.id }>{ index }. { item.title }</span>n })n}n
With 標籤
用於將值賦給局部變數
// 簡單用法n<With foo={ 47 } bar={ test }>n <span>{ foo }</span>n <span>{ bar }</span>n</With>nn// 嵌套使用n<With foo={ 47 }>n <With bar={ test }>n <span>{ foo }</span>n <span>{ bar }</span>n </With>n</With>n
屬性名類型必須說明anyany將值賦給指定名稱的局部變數
注意,定義的「變數」僅在 With 塊中可用。
轉換
<With> 將會轉換為一個「匿名的立即執行函數」
// 轉換前n<With foo={ 47 }>n <span>{ foo }</span>n</With>nnn// 轉換後n{n (function(foo) {n return <span>{ foo }</span>n }).call(this, 47)n}n
六、對比普通 JS/JSX 寫法的對比
帶來的好處
- 更直觀、對於習慣於模板語法的開發人員或設計人員,更接近於傳統模板寫法
- 減少 JS/JSX 相互隔斷導致的代碼「支離破碎」
- 更好的可讀性和整潔,不過這取決於你的個人喜好。
一點小問題
- 多了一步編譯,會多用一點點構建時間
- 依賴 Bable 6,並需要配置一下 .babelrc
七、如何進行語法檢查?
ESLint
所有結構控制標籤都是通過 Babel 插件進行轉義的, 不需要 require or import,所有在進行「語法檢查」時,將會出現「變數未定義」的警告或錯誤。
但是,有一個 ESlint 插件可處理這個問題
ESLint plugin for JSX-Control-Statements-- end --
推薦閱讀:
※前端每周清單第 30 期:WebVR 指南,Vue 代碼分割範式,理想的 React 架構特性
※對React應用的一些思考