React
JSX
props
本篇為 [FE302] React 基礎 - hooks 版本 這門課程的學習筆記。如有錯誤歡迎指正!
學習目標:
P1 我知道 React 的目的以及原理
P1 我知道我們為什麼需要 React
P1 我知道使用 React 跟之前使用 jQuery 的區別
P1 我理解 state 跟 props 的不同
關於 React 發展歷史,其實起源於 2013 年由 Facebook 開源出來的一個 Library,直到 2015 年更新到 v15 版本後又為更多人使用。
而 React 16.8.0 則是第一個支援 Hook 的版本,這部分我們之後會再提到。到 2020 年 10 月出現 React v17,課程將以此版本為主,和 v16 基本上沒有太大的差異。
在開始之前,可以先複習這篇:React 中一定會用到的 JavaScript 語法,主要包括下列幾個重點:
可事先具備這些知識,或到時候再搭配 MDN 查詢用法,因為這些是之後實作 React 時一定會碰到的語法。
我們經常會聽到前端三大框架:React、Vue、Angular,但根據 React 官網定義:
React: A JavaScript library for building user interfaces
嚴格來說,React 其實不算是一個框架,而是一個 JavaScript Libaray。通常使用 React 時會搭配其他 Libaray 使用,整個生態系結合起來就會和框架差不多。
我們可以從「React 提供了哪些功能」的角度來思考這個問題。
之所以會有 React 的誕生,可從原有的 JavaScript MVC 架構談起:Facebook 認為 MVC 模式已無法滿足擴展需求,隨著應用規模增加,系統的複雜度會成級數成長,增加開發難度以及不易進行後續維護。
因此開源出 React 這個 JavaScript Library,負責處理 MVC 的 View(介面)部分,解決思路就是「當狀態改變時,直接重新渲染畫面」,引入 Virtual DOM 的概念,透過 DOM Diff 演算法算出實際需要更新的部分,有效減少渲染次數以提高效能。
瞭解到 React 的發展過程之後,再回到「為什麼我們需要 React?」這個問題,React 的核心概念如下:
由於 React 中,強調「模組化」的概念,如果是實作一個靜態網頁,或是專案規模較小、沒有模組化需求時,直接使用 HTML、CSS、JavaScript 等進行開發會更方便省時。
因此,並不是所有專案都適合用 React 進行開發,應該根據網站需求去決定合適的工具。
參考資料:
在開始前可先透過 CodeSandbox 進行練習,這是一個支援前端框架開發線上編輯器(IDE),類似複雜版的 Codepen。
點選 Create Sandbox,並選擇 React 來快速建立開發環境:
建立完成的初始畫面如下,藉由像這樣快速建立環境,也能用來幫助線上 debug:
簡單來說,JSX(JavaScript extension syntax)就是 HTML/XML + JavaScript。
React 提供的 JSX 語法,是透過底層的 Babel 機制,將 HTML 語法轉成 JavaScript Function 的形式,讓我們能用來建立 React elements:
const rootElement = document.getElementById("root");
ReactDOM.render(<h2>app</h2>, rootElement
);
以 mount Hello 這個 component 為例:
mount:意思是把 component 放到畫面上
function Hello() {
// 在 Hello() 中 return 什麼,就會在畫面 render 出什麼
return <h1>hello world!</h1>
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Hello />, rootElement
);
Hello() 這個 component 可以接收參數 props,寫在大括號中會被解讀 React 為 JavaScript 程式碼執行,例如 {JS code}
:
function Hello(props) {
return <h1>Hello, {props.name}!</h1>
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Hello name="Heidi"/>, rootElement
);
更常見的寫法,是把 props 參數改成 ES6 的解構寫法,也就是在大括號內傳入參數:
function Hello({name}) {
return <h1>Hello, {name}!</h1>
}
JSX 語法和 template engine 不同,在使用上沒有那麼自由,必須遵守幾點規則,以避免寫出不合法的 JSX :
那麼該如何解決這種情況呢?以下有兩種解決方式,以及範例程式碼:
{todo.isDone ? '已完成' : '未完成'}
{todo.isDone && '已完成'}
{!todo.isDone && '未完成'}
此外,根據官方文件,JSX 語法能夠預防 Injection Attacks,提供 escape 功能。
如果真的想要 render 出 innerHTML,則需透過 dangerouslysetinnerhtml 這個冗長的標籤,一般而言不會使用這個方法。
但需要注意,如果在 a 連結標間中,有使用者輸入的區塊,例如:
<a href={todo.content}>click me!</a>
這時若被惡意輸入 javascript:alert()
程式碼,點擊 a 連結就會執行該 JS 程式碼。這其實因為 React 沒有跳脫冒號,造成的 click based XSS:
防範方式有兩種:
<a href={window.encodeURIComponent(todo.content)}>click me!</a>
最後再以常見的 Counter component 為例:
function Counter() {
// useState() 會建立一個陣列 state, 傳入參數為初始值
const [value, setValue] = React.useState(1);
function handleClick() {
// 透過呼叫 setValue() 來更動 state
setValue(value + 1);
}
// 建立 onClick 監聽事件
return <button onClick={handleClick}>{value}</button>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<Counter />, rootElement
);
透過點擊 button 觸發事件來改變 state,React 再根據 state 內容來 render 出畫面:
React 最大的不同,在於多了 Component 和 State 這兩個概念。
這和以往的思考模式其實很不一樣,像是在切好的 UI 畫面上,將每個 element 新增各種功能;或是以 MVC 架構進行開發。
而在 React 中,一個元件(component)是 React 的最小單位,再透過 Props 來設定屬性或資料。由於所有東西或介面都是由元件所組成,強調的是 UI 元件的封裝性、共用性及擴展性。
在 React 是「透過資料的狀態,決定是否重新渲染畫面」,這和以往直接更改操作 DOM 元素不同;而是以類似間接的方式,透過 React 演算法比對 Virtual DOM 來決定是否更新真實 DOM。
也就是說,Component 是透過資料狀態,來決定是否更新 UI 畫面,而 Component 中有兩種資料來源:
每當 React 偵測到 Props 或 State 有改變時,就會自動重新渲染。
剛開始對這種以 Component 和 State 為核心概念的寫法很不習慣,但卻也有點熟悉的感覺,就像以前在實作前後端分離的留言板或 Todo List 時很像。可能也因為 function component,又和透過功能區分的模組化很類似,但需要考慮到該如何更改 State 來呈現畫面。
State 和 Props 都是 JavaScript 物件,我們在前面有提到,當這兩者之一有改變時,會觸發 React 重新渲染畫面。兩者差別在於:
在 React component 中,包在標籤中間的東西,稱為 children,children 也是一個 props。
以 Todoitem 為例,這裡的 children 指的就是 Watch a movie
:
function Todoitem({n, children}) {
return <h1>Todo {n}: {children}</h1>
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<Todoitem n={1}>
Watch a movie
</Todoitem>, rootElement
);
參考資料:
React 環境建置可分為兩種:
這裡會以現成的程式來做示範,詳細說明可參考 facebook/create-react-app。
依照下方指令安裝相關套件並啟動:
$ npx create-react-app my-app
$ cd my-app
$ npm start
成功運行後,就會在 localhost 開一個 server:
在開始專案之前,首先要閱讀 README.md
的說明,還有查看 package.json
確認安裝了哪些套件等訊息。
再來是 src\index.js 檔案,和我們在 CodeSandbox 看到的內容非常類似:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// 與資料收集、效能有關
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
React.StrictMode
:代表嚴格模式中,會像 ESLint 進行檢查與警告,但有時為了偵測,可能會呼叫兩次,造成和想像中不同的結果,因此建議不加上<App />
:render 出 App conponent,也就是 render 的畫面可在 src\App.js 檔案,修改要 render 的畫面:
function App() {
return (
<div className="App">
Hello World!
</div>
);
}
export default App;
重整頁面即可看到結果:
到這裡我們瞭解 React 重要的概念 Component,以及如何使用 JSX 語法來建立 React 元素。
此外,除了可以利用線上編輯器 CodeSandbox,也可以在本地端安裝官方提供的 create-react-app 套件,來快速建置 React 開發環境。