閱讀65 返回首頁    go 阿裏雲 go 技術社區[雲棲]


通過 JSX Control Statements 編寫 JSX

通過 JSX Control Statements 編寫 JSX

一、前言

一些開發者,特別是有使用過「模板技術」的開發者(例如 Handlebars),剛開始嚐試使用 React 編寫應用時,可能會驚訝的發現,JSX 竟然沒有內建支持類似其它模板引擎似的任何結構控製語句或指令。但這就是 JSX,它就是這麼設計的,JSX 不是傳統的模板,也不需要某個模板引擎去解析。大體上,可以將 JSX 看成普通 JavaScript 表達式的語法糖。

下邊的的代碼是一個「普通的 JavaScript 代碼示例」

render () {
  return (if(true){
    ...
  }else{
    ...
  });
}

如上代碼不合法的,同樣,下邊的 jsx 代碼也是不合法的

render () {
  return (<div>
  {if(true) <span>1</span> else <span>2</span> }
  </div>);
}

因為,JavaScript 不能在一個「表達式」中嵌入「控製語句」。

二、能做什麼?

JSX-Control-Statements 為 JSX 增加了基本的結構控製語句,比如條件和循環控製語句。通過將插件將「組件風格」的控製語句最終轉換為普通 JS 代碼,例如:

<If condition={condition()}>Hello World!</If>

將會轉換為

condition() ? 'Hello World!' : null

三、實現原理及說明

這看起來,似乎很容易實現,通過一係列表組件好像就能實現,但事實上這在 React 中這是不可行的,如果用「組件」的方式實現,類似如下代碼

<ul>
  <ForComponent each="item" index="index" of={list}>
     <li key={index}>{item.title}</li>
  </ForComponent>
</ul>

組件的方式實現上邊的代碼,將會拋出來一個錯誤 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-statements

還需要在 .babelrc 中配置插件,如下

{
  ...
  "plugins": ["jsx-control-statements"]
}

五、核心語法

If 標簽

用來表示最簡單的條件判斷邏輯

// 簡單示例
<If condition={ true }>
  <span>IfBlock</span>
</If>

// 包括多個子元素及表達式
<If condition={ true }>
  one { "two" }
  <span>three</span>
  <span>four</span>
</If>

<If>

if 的 body 部分隻有在 condition 為 true 才會渲染

屬性名 類型 必須
condition boolean

<Else /> (deprecated)

Eles 已廢棄,不推薦使用,它會破壞 JSX/XML 的語法,並且影響自動格式化。

轉換

If 標簽將會預編譯為「三元表達式」

// 轉換前
<If condition={ test }>
  <span>Truth</span>
</If>

// 轉換後
{ test ? <span>Truth</span> : null }

Choose 標簽

Choose 是比 If 更複雜分支結構寫法

<Choose>
  <When condition={ test1 }>
    <span>IfBlock</span>
  </When>
  <When condition={ test2 }>
    <span>ElseIfBlock</span>
    <span>Another ElseIfBlock</span>
    <span>...</span>
  </When>
  <Otherwise>
    <span>ElseBlock</span>
  </Otherwise>
</Choose>

// default block is optional; minimal example:
<Choose>
  <When condition={true}>
    <span>IfBlock</span>
  </When>
</Choose>

<Choose>

Choose 的子元素隻允許出現 WhenOtherwise,其中最少需要有一個 When,而 Otherwise 是可選的

<When>

<If> 類似

屬性名 類型 必須
condition boolean

<Otherwise>

沒有任何一個 When 滿足條件時,將會渲染 Otherwise

轉換

Choose 標簽同樣將會預編譯為「三元表達式」

// 轉換前
<Choose>
  <When condition={ test1 }>
    <span>IfBlock1</span>
  </When>
  <When condition={ test2 }>
    <span>IfBlock2</span>
  </When>
  <Otherwise>
    <span>ElseBlock</span>
  </Otherwise>
</Choose>

// 轉換後
{ test1 ? <span>IfBlock1</span> : test2 ? <span>IfBlock2</span> : <span>ElseBlock</span> }

For 標簽

For 的命名用方法,如下

  // 注意,需要指定 key 屬性
  <For each="item" of={ this.props.items }>
    <span key={ item.id }>{ item.title }</span>
  </For>

  <For each="item" index="idx" of={ [1,2,3] }>
    <span key={ idx }>{ item }</span>
    <span key={ idx + '_2' }>Static Text</span>
  </For>
屬性名 類型 必須 說明
of Array/collection 列表或包含 map 方法的集合
each string 循環「項」變量名
index string 循環「索引」變量名

注意,For 不能作為根元素

轉換

// 轉換前
<For each="item" index="index" of={ items )}>
  <span key={ item.id }>{ index }. { item.title }</span>
</For>

// 轉換前
{
  items.map( function(item, index) {
    <span key={ item.id }>{ index }. { item.title }</span>
  })
}

With 標簽

用於將值賦給局部變量

// 簡單用法
<With foo={ 47 } bar={ 'test' }>
  <span>{ foo }</span>
  <span>{ bar }</span>
</With>

// 嵌套使用
<With foo={ 47 }>
  <With bar={ 'test' }>
    <span>{ foo }</span>
    <span>{ bar }</span>
  </With>
</With>
屬性名 類型 必須 說明
any any 將值賦給指定名稱的局部變量

注意,定義的「變量」僅在 With 塊中可用。

轉換

<With> 將會轉換為一個「匿名的立即執行函數」

// 轉換前
<With foo={ 47 }>
  <span>{ foo }</span>
</With>


// 轉換後
{
  (function(foo) {
    return <span>{ foo }</span>
  }).call(this, 47)
}

六、對比普通 JS/JSX 寫法的對比

帶來的好處

  • 更直觀、對於習慣於模板語法的開發人員或設計人員,更接近於傳統模板寫法
  • 減少 JS/JSX 相互隔斷導致的代碼「支離破碎」
  • 更好的可讀性和整潔,不過這取決於你的個人喜好。

一點小問題

  • 多了一步編譯,會多用一點點構建時間
  • 依賴 Bable 6,並需要配置一下 .babelrc

七、如何進行語法檢查?

ESLint

所有結構控製標簽都是通過 Babel 插件進行轉義的, 不需要 require or import,所有在進行「語法檢查」時,將會出現「變量未定義」的警告或錯誤。

但是,有一個 ESlint 插件可處理這個問題
ESLint plugin for JSX-Control-Statements

-- end --

最後更新:2017-08-18 10:32:25

  上一篇:go  外貿仿牌抗投訴主機
  下一篇:go  深入淺出JS動畫