感謝 iT 邦幫忙與博碩文化,本系列文章已出版成書「從 Hooks 開始,讓你的網頁 React 起來」,首刷版稅將全額贊助 iT 邦幫忙鐵人賽,歡迎前往購書,鼓勵筆者撰寫更多優質文章。
在 React 18 以後已經棄用
ReactDOM.render()
,改用ReactDOM.createRoot()
,內文中的圖片並未一併修改,煩請讀者留意。
本日關鍵字:ReactDOM.createRoot()
、JSX
昨天我們完成了一個簡單的計時器頁面,感覺用原生的 JavaScript 來寫計數器並不會太過複雜,我們只需要在特定的 HTML 元素上使用 addEventListener
去監聽事件,再透過 textContent
去修改元素內的文字內容就可以了。這麼說起來,我們為什麼需要透過 JSX 去把 HTML 寫在 JavaScript 內呢?
以前 Nokia 的有一句經典的話,叫做「科技始終來自於人性」。
這句話同樣適合用在前端技術的演進上,多數新技術的推出都是因為在當時有新的需求或需要解決的問題,事後學習這些技術的我們,可能不會知道當時的人為什麼要用這種寫法或使用這類工具,所以如果你在學習的過程中,對這些新技術或工具感到困惑的話,不妨可以試著去查查這些工具或方法當時想要解決的是什麼問題,在學習上比較不會那麼納悶為什麼有這麼多看起來有得沒的東西。
很多東西回過頭看,都是時代的眼淚啊!
雖然昨天透過 JavaScript 來修改網頁內容的做法看起來並不會太複雜,但這是因為現在我們只需要改變網頁上的一個內容(即,計數器的數字),但假設現在網頁上有非常多的內容是需要透過 JavaScript 來替換的話,這個動作會變得非常繁瑣。舉例來說,在 iThome 的個人資料裡有許多不同的欄位,包括「帳號名稱」、「累積瀏覽數」、「追蹤數」、「發問次數」、「文章發布數」、....。
假設這些資料不是一開始就透過網頁伺服器產生好,而是透過 AJAX 向後端拉取資料後才要放進去網頁時,要把這些欄位一個一個換掉就會變得相當麻煩,需要先一個又一個選到該 HTML 元素,再一個又一個把它們換掉。
假設資料欄位很多的情況下,直接使用 JavaScript 來修改網頁內容會變得非常繁瑣。
透過 JSX 等於我們可以直接把 HTML 放到 JavaScript 中去操作,不再需要先用 querySelector
去選到該元素後才能換掉,而是可以在 HTML 中直接帶入 JavaScript 的變數。
除此之外,如果現在希望某個使用者的「最佳解答」超過一定數目就在個人資料中額外出現一個獎勵標章時,透過 JSX 這種把 HTML 寫在 JavaScript 中的方式,就可以直接使用 if
判斷式。它的好處還不只這些,後面我們會再逐一提到,讓我們先來看看如何用 JSX 寫一個超簡單的 Hello World 吧!
簡單的說,在 JSX 的加持之下,讓開發者可以把 JavaScript 內的用法與程式邏輯,直接套用到 HTML 的元素上,就是一個「強化版 HTML 」的概念!
再把原本的計數器改成 JSX 的寫法之前,我們先來看一個非常簡單的 JSX 範例。請你打開這個 CodePen 連結,這是 React 官方提供的 Hello World 樣板 :
內容很精簡,在 HTML 的部分就是單純的一個 <div>
而已:
<!-- HTML -->
<div id="root"></div>
在 JavaScript 的地方則是使用了 ReactDOM.createRoot
這個語法,在 ReactDOM.createRoot
中說明要把 React JSX 的內容放到原本 HTML 中的哪個元素上,這裡就是放到 #root
,並且可以取得一個變數,這裡取名做 root
。
接著透過 root.render()
在參數中放的就是 JSX 的內容,這裡看起來其實就和直接放入 HTML 一樣:
// JavaScript
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<h1>Hello, world!</h1>);
所以最後出來的畫面就會是:
可以在 #root
元素中看到寫在 JavaScript 中 JSX 的內容。
許多人~~(其實是我)~~一開始聽到學 React 之前要先學 JSX 就望之卻步,默默想說那還是以後再來看好了...。但現在回頭來看什麼是 JSX,以上面的例子來說,這個部分就是 JSX:
你可能好奇,阿這不就只是 HTML 嗎?
沒錯,JSX 簡單來說,就是把你已經知道的 HTML 放到 JavaScript 內,但同時因為它被放在 JavaScript 中,所以可以使用 JavaScript 中提供的各種語法,例如之後我們會在 JSX 這裡面放入 JavaScript 的變數、執行 if...else
判斷式、進行迴圈等等。你可以把 JSX 看成是一個增強版的 HTML,讓我們可以用更簡便的方法對 HTML 進行的修改和操作。
簡單的說,在 JSX 的加持之下,讓開發者可以把 JavaScript 內的用法與程式邏輯,直接套用到 HTML 的元素上,就是一個「強化版 HTML 」的概念!
這裡你可能會好奇,為什麼可以直接在 JavaScript 中使用 ReactDOM.render
這個方法,而且可以直接在裡面就開始撰寫 JSX 呢?這是因為,在剛剛 React 提供的這個 HelloWorld 樣板 中,已經幫我們在 Codepen 中載入所需的套件了,你可以點選 JavaScript 側邊欄中的齒輪後:
會看到有使用了名為 Babel
的 JavaScript 前處理器(Preprocessor),因為 JavaScript 這幾年更新的非常快速,有些最新的語法部分瀏覽器可能尚未支援,而 Babel 就是用來讓 JavaScript 可以正常運作在不同版本的瀏覽器上。
此外,也可以看到這個 HelloWorld 樣板中,已經預先載入兩個 JavaScript 套件,分別是 React 和 ReactDOM 這兩個套件。其中 React 這個套件就可去解讀 JSX 的內容,而 ReactDOM 則可以讓所撰寫的內容,放置到特定 HTML DOM 中的元素上。
如果你之後有想要添加其他的套件,也可以從這裡加入:
現在既然我們已經可以把 HTML 的內容寫在 JavaScript 中,自然就可以把 JavaScript 的變數直接帶入 JSX 中,在 JSX 中帶入 JavaScript 變數。要把 JavaScript 變數帶入 JSX 只需使用 { }
大括號即可。
以下面的例子來說, word
被 {}
包起來,所以這裡的 word
就是 JavaScript 的變數:
<h1>Hello, {word}</h1>
如果在原本的 Hello World Template 想要帶入 JavaScript 變數的話,就可以這樣做:
const word = 'React';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<h1>Hello, {word}!</h1>);
就可以把 word
這個變數帶入 HTML 內了,是不是蠻容易的呢!
除此之外,在 JSX 中的 {}
內還可以放入其他 JavaScript 表達式(expression),「表達式」簡單來說就是當你輸入一段程式碼後,JavaScript 會直接回傳一個值給你的這種。你可以在瀏覽器的 console
視窗中測試看看,例如當直接輸入字串、 1 + 3
、 a = 3
或 3 > 2
時,console 都會直接有回應的這種情況,就是表達式:
參考:進一步談 JavaScript 中函式的建立─ function statements and function expressions。
像是這類的表達式同樣可以直接放入 {}
內,像是這樣:
/* JavaScript */
const deviceName = 'Galaxy Note';
const currentPrice = 31900;
const discount = 0.85;
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// JSX 中可以在 {} 內放入表達式(expression)
<h1>現在 {deviceName} 的售價是 {currentPrice * discount}</h1>
);
如果有需要看完成的程式碼,可以參考這裡 Day 3 - Use Variables in JSX。
還記得在 Day 03 中我們用 HTML 和 CSS 完成了一個簡單的計數器嗎?明天將會從 React Hello World Template 開始,把計數器改用 JSX 的方式來撰寫。
現在你可以先自己試試看,把在 Day 03 實作的 Counter Started Template,其中 HTML 的部分搬到 JSX 中,讓畫面可以呈現出來就好,還不需要加入 JavaScript 這類事件監聽的功能。不管最後有沒有完成,花個 5 分鐘嘗試看看,我們會在明天繼續完成它。
在之後的內容中,我們常會先提供一個樣板給大家當做開始,如此可以不用再去煩惱 CSS 樣式或套件載入的部分。在練習的時候,記得先從提供的樣版 "Fork" 一份出來再自己修改與存檔,Fork 就是「複製」一份的意思: