考慮下面這個變數宣告:
1 | const element = <h1>Hello Tainan,台南!</h1>; |
這個標籤語法不是一個字串也不是 HTML。
這個語法叫做 JSX。JSX 結合了 JavaScript 的 JS 和 XML 的 X,是一個 JavaScript 的語法擴充。允許我們直接在 Javascript 源代碼中使用基於標籤的語法來定義 React 元素。有時 JSX 會與 HTML 混淆,因為它們看起來很相似。JSX 也可能會讓你想到一些樣板語言(template language),但不一樣的地方是 JSX 允許你使用 JavaScript 所有的功能。
JSX 是創建 React 元素的另一種方式,執行 JSX 會產生 React 元素,因此您不必費力地在復雜的 React.createElement 調用中尋找缺少的逗號。
建議你在寫 React 的時候透過 JSX 語法來描述使用者介面的外觀。
將 React.createElement 改用 JSX:
const hello = React.createElement("h1", { id: "hello" }, "Hello Scott!"); |
1 | <div id="react-root"></div> |
JavaScript 不認識 JSX 語法,必須先經過 Babel 的預處理(compiling)。
但我們通常不會使用變數的方式,而會使用函式組件的方式:
1 | <script type="text/babel"> |
這個模式是 React JSX 的基本使用方式: 函式組件 (function component)。組成的組件樹(component tree)構造整個 React 應用程序。
JSX Tips
JSX 可能看起來很熟悉,並且大多數規則導致的語法類似於 HTML。但是,使用 JSX 時應注意一些事項。
Nested components
JSX 允許我們將組件添加為其他組件的子代。 例如,在 UsersList 內部,我們可以多次渲染另一個名為 User 的組件。
1 | <UsersList> |
className
由於 class 是 JavaScript 中的保留字,因此使用 className 來定義 class 屬性。
1 | <h1 className="fancy">Hello Scott!</h1> |
JavaScript expressions
JavaScript 表達式用大括號 { } 括起來,指示變數將在何處被求值並返回其結果值。例如,如果要在元素中顯示 user 屬性的值,則可以使用 JavaScript 表達式插入該值。該變數 user 將被求值並返回其值。
1 | <h1>{user}</h1> |
除字符串以外的其他類型的值,也應顯示為 JavaScript 表達式:
1 | <input type="checkbox" defaultChecked={false} /> |
Evaluation
大括號之間添加的 JavaScript 將被求值。 這意味著,可以執行字串串聯或加法之類的操作。這也意味著, JavaScript 將會調用在表達式中找到的函數:
1 | <h1>{"Hello " + user}</h1> |
Mapping Arrays with JSX
JSX 是 JavaScript,因此您可以將 JSX 直接合併到 JavaScript 函數中。例如,您可以將陣列映射到 JSX 元素:
1 | <ul> |
可以比對一下使用 React.createElement 與 使用 JSX 的差別:
const hello = React.createElement( |
JSX 看起來很乾淨而且有較佳的可讀性,但是無法直接在瀏覽器解譯執行。所有 JSX 必須轉換為 React.createElement 調用。幸運的是,有一個出色的工具可以執行此任務:Babel。
整段由使用 React.createElement 改用 JSX 改寫。
- 使用 React.createElement
1 | <script type="text/babel"> |
- 使用 JSX
1 | <script type="text/babel"> |
- 第 8 行是一個函式組件,這裡改用 JSX。
- 第 17 行我們使用 JSX 語法調用 UsersList 函式組件,透過 users 屬性傳遞函式引數。這裡的所有屬性將被包在一個物件 { users: “[…]”, others: “…” } 傳遞給函式組件。
有了基礎概念,現在來做一個比較實際的例子。
美味食譜
我們需要一些美味食譜的資料,我們將它放在 APEX Page Properties 的 JavaScript > Function and Global Variable Declaration 中。
1 | const recipesData = [ |
Region Properties 的 Source > Text 的程式碼:
1 | <div id="react-root"></div> |
這裡有 5 個 React 組件,組成一個組件樹。Menu 組件是根組件(root component),所有的資料從根組件的 recipes 屬性注入(第 46 行) ,往整個組件樹流動。此外還有一個 title 屬性。這兩個屬性組成了 Menu 函式所需的引數。Menu 參數用解構賦值的語法取得傳入的值。
第 39 ~ 41 行用 JavaScript 陣列的 map 方法加入多個 Recipe 組件,這裡使用了展開運算子 { …recipe } 賦予 recipe 的所有屬性 name、ingredients 與 steps。
這些組件是基於將應用程序的數據作為屬性傳遞給 Menu 組件而構造的。 如果我們更改 recipes 陣列並重新渲染 Menu 組件,整個樹狀組件也將引起連鎖反應,而 React 將盡可能有效地更改此 DOM。
數據使我們的 React 組件活起來。沒有 recipes 資料,我們構建的 recipes 的用戶界面是沒有用的。食譜和配料以及清晰的烹飪步驟說明使這樣的應用程序有價值。
我們構建了一個組件樹:數據能夠作為屬性流過組件樹的層次結構。 屬性(properties)是整體構圖的一半。另一半則是狀態(state)。 React 應用程序的狀態由具有更改能力的數據驅動。數據變動,驅動整個 React 應用程序的狀態的變動。
目前資料是死的,我們要讓資料活起來,帶動 React 應用程序的互動性。React State Management