JSX —— JavaScript? HTML? 傻傻分不清楚?

black screen with code

本篇文章將簡述 React.js 特殊的語法 —— JSX 的基本概念

相信在學 React.js 的各位,一定都會看到諸如下列的 「JSX」 語法,一個看起來像 HTML 又潛藏在 JavaScript 中(然後還可以你中有我我中有你)的怪東西。

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

今天,我們就來好好剖析一下 JSX 的語法概念 ——

這篇文章先假定各位大致知道 HTML 和 JavaScript 的基本運作語法
不過稍後也會稍微提點一下這方面的概念
需要詳細介紹歡迎各位搜尋 iT 邦幫忙MDN Web Docs

或者期待一下,反應若不錯的話日後將有機會較為詳細的講述 Web Programming 相關的各項技術內容~

回顧 HTML

HTML,全名 HyperText Markup Language,中文為「超文言」
主旨就是運用標籤(tag)鑲嵌在文字中,對文字內容進行特殊的標記,來呈現更彈性多元的網頁內容。

舉例如下

<p>
  I <b>love</b> <a href="https://weikaiwei.com/">City Grandma</a>!
</p>

上面程式碼的 <p> </p> <b> </b> <a ...> </a> 就是標籤(tag),一般有開始標籤(<p>)和結束標籤(</p>)兩種,開始到結束中間的這段文字表達網頁的內容,而標籤本身則用來標記這段文字內容要以什麼形式類別呈現,例如 p 中間的是一段段落(paragraph)、b 之間的文字要粗體(bold)等等,而像 a 則是表達一個超連結(hyperlink,英文取自 anchor,指可以被點選連到其他網站的內容)。

在這邊要注意的是,像 <a> 這種連結由於通常會對應到一串目的地的網址,因此 <a> tag 通常不會只有 <a>一些文字</a> 這樣,而是會在開始標籤上加上 href="https://example.com" 這串目標網址,因此 HTML 的 tag 我們通常可以描述為

<標籤種類 標籤屬性1="屬性內容1..." 標籤屬性2>標籤內容文字</標籤種類>

這樣的形式。「標籤種類」是指示這段文字在網頁上的角色,而這段文字除了內容本身如果有其他資料,會用一串「屬性、內容」來呈現——這在 JSX 語法中扮演了很重要的角色,稍後各位會看到。

回顧 JavaScript

在這裡要提及的是 JavaScript 的函數類別兩個概念。

Function

最一般的 JavaScript 函數一般寫作

function add(x, y) {
    return x + y;
}

其中 add 是函數名,xy 是變數名,而大括號內部會寫明要對這些傳進來的變數做什麼一系列的操作,最後再由 return 後面寫的東西將結果回傳。同樣的寫法可以用「箭頭函數」寫成

const add = (x, y) => x + y;

Class

實際上 JavaScript 是弱型別語言,class 是 function 的語法糖,有關 class、function 和 this 的各種愛恨情仇留待日後詳述⋯⋯

一個 JavaScript class 的定義寫作

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greeting() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

class Student extends Person {
  constructor(name, age, school) {
    super(name, age);
    this.school = school;
  }
  
  student_info() {
    console.log(`Hello, I am from ${this.school}.`);
  }
}

let jeff = new Student("Jeff", 25, "NTU");
jeff.greeting();  // print out "Hello, my name is Jeff."
jeff.student_info();  // print out "Hello, I am from NTU."

Wrap it up

總之,從上面我們可以看到,function 可以定義一個傳入一些原料/資料並製作出產品/結果的過程,而 class 可以定義一項「物件(object)」並將其視為一個劇本中的角色並指派一些屬性與動作。

好了,說了這麼多,回來看 JSX 吧~

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

看上面這段 Create React App 自動生成的預設程式碼,各位可以看到最外層是一個 JS function,但在 return 後面接的那個「要被回傳的結果」卻是一段 HTML。

換言之,這是一個會回傳 HTML 的 JS function——各位大致這樣想就行了。
一般 JS 作為程式語言,JS function 回傳的東西當然是 JS 的變數(資料、物件),但在傳統純 JS 的寫法中我們經常得

var hyperlink = document.createElement("a");
// display <a></a>
hyperlink.setAttribute("href",  "https://weikaiwei.com/");
hyperlink.innerText = "Click me";
// display <a href="https://weikaiwei.com/">Click me</a>

這樣進行一番操作,用 JS 的語法去慢慢把 HTML 的 tag 給搭建出來。但有了 JSX 這樣「在 JS 中直接寫 HTML」的語法,我們可以很簡單的直接

let hyperlink = <a href="https://weikaiwei.com/">Click me</a>;

把這件事完成。我們可以把 tag 之間圍起來的部分當成某一個 object(實際上也是),這樣想之後其他外面的部分就只是普通的 JS 了。

至於這個 tag 內部也很有趣,如果我們希望 tag 內部的任何內容(包含標籤屬性的內容與文字內容)可以是某個 JS 的變數(variable)或表達式(expression,指可以被算成某個值、能被存成某個變數的程式碼),可以在內嵌的 HTML 中用大括號「 { } 」將 JS 程式碼包起來,例如

let hyperlink = <a href="https://weikaiwei.com/" + {1 + 2}>Click me {add(3, 4)} times!</a>;
// You will get 
//   <a href="https://weikaiwei.com/3">Click me 7 times!</a>

簡單說就是

js js js ... js <html html html {js js js ... js} html html>html html</html> js js js ...

也就是「用 tag 在 JS 內寫 HTML」,再「用 {} 在 HTML 內寫 JS」,因此以下操作是被允許的

js js js ... js <html html html {js js 
  js js js <html html ... html>html {js js ... js}</html> js
  js ... js} html html>html html</html> js js js ...

不過慣例上,HTML tag 如果比較長,通常外圍會用小括號 ( ) 包起來來讓程式碼比較好閱讀

回到最前面的例子

最後來看一下最一開始的例子

import logo from './logo.svg';  // 這邊是 JS,把 SVG 圖檔輸入進來
import './App.css';  // 這邊是 JS,把 CSS 樣式表輸入進來

function App() { // JS,定義了一個 function
  return ( // 要回傳一個東西,是一個 HTML tag。因為比較長所以用 () 包起來
    <div className="App">  
      <!-- 前一行第一個 “<” 出現了,程式從這邊開始變 HTML -->
      <header className="App-header">
        <!-- 這邊的 `logo` 是一個 JS 變數,代表圖片,
             因此要用 {} 告訴編譯器這裡是 JS 而不是單純的 "logo" 文字
             剩下的還是 HTML -->
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    <!-- 進到最後一個 HTML tag 的 closed tag 了,之後回到 JS -->
    </div>
  );  // 這邊後面都是 JS
}

export default App;

實際上 React 為了讓 HTML 可以跟 JS 「共同演出」因此有做了一些調整,例如 class 為了避免跟 JS 的 class 關鍵字衝突,因此會改寫成 className,在轉換成 HTML 時會變回 class 來使瀏覽器能夠正常運作;最後,有了這個 App 函數,我們要怎麼讓網頁裡面出現這些 div 等等的東西呢?

注意了!不是 App() ,而是 <App></App> (或通常寫成 <App /> )!

待續⋯⋯

今天先到這邊,讓各位大致看得懂 JSX 語法,至於 React 整套運作流程與用法(還有 functional / class component 、props、states 等等一堆)⋯⋯坑先挖了,希望有機會可以儘早回來填上,有什麼想討論的也歡迎各位留言,更期望大家可以多多分享,期待能幫助到各位。就先這樣囉~

1 Comment

留言討論區