讀者進入以下網址:https://codesandbox.io/s/new,就能進到由 CodeSandbox 提供的線上開發環境以及使用 create-react-app 指令建立好的 React 專案來進行練習
基本的元件(component)是由一個或多個元素所組成,我們可以將重複出現或是負責某個功能的一組元素包裝成元件,所有元件會形成一棵元件樹,元件樹由一個根元件往下長出其他的元件,而一個元件內可能又包含其他一個或多個元件,形成階層關係,外層的元件稱為父元件,內層的元件稱為子元件,基本上每個元件是互相獨立的(但父元件可以透過 props 來傳遞資料給子元件,這一觀念會在下一篇文章介紹),下面我們透過範例來講解。
首先進入 https://codesandbox.io/s/new,將 index.js 這支檔案裡面的 App 函式裡面的內容改為:
function App() {
return (
<div>
<h1>React</h1>
<div>
<p>React 具有以下特性:</p>
<ul>
<li>Virtual DOM 的機制</li>
<li>元件化的開發</li>
</ul>
</div>
<div>
<p>三大前端框架:</p>
<ul>
<li>React</li>
<li>Vue</li>
<li>Angular</li>
</ul>
</div>
</div>
);
}
我們可以觀察到其中以下這兩個部分的結構很類似,都同樣具有一個文字敘述和一組項目清單:
<div>
<p>React 具有以下特性:</p>
<ul>
<li>Virtual DOM 的機制</li>
<li>元件化的開發</li>
</ul>
</div>
<div>
<p>三大前端框架:</p>
<ul>
<li>React</li>
<li>Vue</li>
<li>Angular</li>
</ul>
</div>
因此我們傾向於將它們包裝建立成元件,這邊用樹狀圖來幫助讀者們理解:
元件可以透過宣告函式的方式來建立,因此又可稱為函式元件,函式名稱就是元件的名稱,而函式的返回值就是被包裝的元素,我們的目標是將上面第一個部分包裝成 Block1 元件,第二個部分則包裝成 Block2 元件。
首先在 App 函式上方宣告一個名稱為 Block1 的函式,函式返回值就是第一個部分的元素:
function Block1() {
return (
<div>
<p>React 具有以下特性:</p>
<ul>
<li>Virtual DOM 的機制</li>
<li>元件化的開發</li>
</ul>
</div>
)
}
接著再宣告一個名稱為 Block2 的函式,函式返回值就是第二個部分的元素:
function Block2() {
return (
<div>
<p>三大前端框架:</p>
<ul>
<li>React</li>
<li>Vue</li>
<li>Angular</li>
</ul>
</div>
)
}
建立好的元件在 JSX 語法中可以透過類似標籤的方式來使用,方式如下:
<元件名稱></元件名稱>
而在之前介紹 JSX的文章中有提到:
沒有內容的 JSX 元素可以自閉
同樣的規則也適用於元件,因此也可以這樣寫:
<元件名稱 />
接續前一段,我們已經建立好 Block1 和 Block2 元件,現在可以用來取代掉 App 函式中的部分元素:
function App() {
return (
<div>
<h1>React</h1>
<Block1 />
<Block2 />
</div>
);
}
是不是簡潔許多了呢?
// 沒有遵守大駝峰式命名法(upper camel case)
function button() {
return ...
}
// 使用該元件時,被當作是一般的按鈕元素
<button />
若我們進一步觀察 Block1 和 Block2 裡面的元素結構,會再發現以下這兩個部分的結構也很類似:
<ul>
<li>Virtual DOM 的機制</li>
<li>元件化的開發</li>
</ul>
<ul>
<li>React</li>
<li>Vue</li>
<li>Angular</li>
</ul>
讀者們可以嘗試再將它們元件化為 List1 和 List2 當作練習,在此就不贅述元件化的過程,附上 index.js
檔案完整的程式碼:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function List1() {
return (
<ul>
<li>Virtual DOM 的機制</li>
<li>元件化的開發</li>
</ul>
);
}
function List2() {
return (
<ul>
<li>React</li>
<li>Vue</li>
<li>Angular</li>
</ul>
);
}
function Block1() {
return (
<div>
<p>React 具有以下特性:</p>
<List1 />
</div>
);
}
function Block2() {
return (
<div>
<p>三大前端框架</p>
<List2 />
</div>
);
}
function App() {
return (
<div>
<h1>React</h1>
<Block1 />
<Block2 />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
以及樹狀示意圖:
在上述範例中,我們自行建立了 Block1、Block2、List1、List2 四個元件,其中 List1 和 List2 分別為 Block1 和 Block2 的子元件,反過來說,Block1 和 Block2 則分別為List1 和 List2 的父元件,那 Block1 和 Block2 的父元件又是誰呢?我們從程式碼中可以看到,Block1 和 Block2 都被使用在 App 函式中,因此我們可以推斷 Block1 和 Block2 的父元件就是 App 元件。
那 App 元件的父元件又是誰呢?答案是:App 沒有父元件,因為 App 並沒有被使用在其他的元件中,代表它的外層已經沒有其他元件,因此 App 就是整個元件樹的根元件。
那在之前介紹 JSX的文章中有提到:
JSX 語法會先經過 babel 轉譯成 JavaScript 程式碼,再經過瀏覽器執行,由 React 轉換為存在記憶體中的 virtual DOM,並且產生建立實際的 DOM
整棵元件樹便是由 JSX 語法寫成的,因此這些元件會由 React 轉換為存在記憶體中的 virtual DOM,那又是如何產生建立實際的 DOM 呢?程式碼中的這兩行扮演關鍵的腳色:
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
第一行程式碼中,先取得 id 為 root 的 div 元素,然後在第二行程式碼中,呼叫 ReactDOM.render 將 App 根元件掛載到該 div 元素,如此便會從根元件開始一層一層的將所有元件或元素「渲染」(render)到畫面上,而所謂「渲染」其實就是將 virtual DOM 轉換為實際的 DOM 的過程,至於 id 為 root 的 div 元素在哪?我們可以到專案中 public 資料夾底下 index.html 檔案的內容中找到。
希望讀者們看完本篇文章,能夠對元件有基本的認識並且學會如何建立元件,下一篇文章我們將更進一步探討元件進階用法以及如何使用 props 來發揮元件的重複使用性。
參考資料:
- ReactDOM – React - https://reactjs.org/docs/react-dom.html#render