---
# System prepended metadata

title: 筆記｜第一堂：從函式拆解認識設計模式
tags: [學習筆記, JavaScript, React]

---

`#React` `#六角前端課程` `#學習筆記` `#骨力走傱`

## 什麼是關注點分離？
### 🔸 介紹
關注點分離（Separation of Concerns）是指程式開發的一種設計原則。當資料越龐大、功能越複雜時，就需要將資料、功能拆分清楚，使程式碼更容易管理與維護。

#### 未採用關注點分離
資料與畫面綁在一起，程式碼不易維護、無法重複使用。

:::spoiler 程式碼
```html=
<ul class="ticketCard-area">
  <li class="ticketCard">
    <div class="ticketCard-img">
      <a href="#">
        <img src="https://github.com/hexschool/2022-web-layout-training/blob/main/js_week5/travel_1.png?raw=true" alt="">
      </a>
      <div class="ticketCard-region">高雄</div>
      <div class="ticketCard-rank">10</div>
    </div>
    <div class="ticketCard-content">
      <div>
        <h3>
          <a href="#" class="ticketCard-name">綠島自由行套裝行程</a>
        </h3>
        <p class="ticketCard-description">
          嚴選超高CP值綠島自由行套裝行程，多種綠島套裝組合。
        </p>
      </div>
      <div class="ticketCard-info">
        <p class="ticketCard-num">
          <span><i class="fas fa-exclamation-circle"></i></span>
          剩下最後 <span id="ticketCard-num"> 87 </span> 組
        </p>
        <p class="ticketCard-price">
          TWD <span id="ticketCard-price">$1400</span>
        </p>
      </div>
    </div>
  </li>
  <li class="ticketCard">
    <div class="ticketCard-img">
      <a href="#">
        <img src="https://github.com/hexschool/2022-web-layout-training/blob/main/js_week5/travel_4.png?raw=true" alt="">
      </a>
      <div class="ticketCard-region">台北</div>
      <div class="ticketCard-rank">2</div>
    </div>
    <div class="ticketCard-content">
      <div>
        <h3>
          <a href="#" class="ticketCard-name">清境高空觀景步道</a>
        </h3>
        <p class="ticketCard-description">
          清境農場青青草原數十公頃碧草，這些景觀豐沛了清境觀景步道的風格，也涵養它無可取代的特色。
        </p>
      </div>
      <div class="ticketCard-info">
        <div class="ticketCard-num">
          <p>
            <span><i class="fas fa-exclamation-circle"></i></span>
            剩下最後 <span id="ticketCard-num"> 99 </span> 組
          </p>
        </div>
        <p class="ticketCard-price">
          TWD <span id="ticketCard-price">$240</span>
        </p>
      </div>
    </div>
  </li>
</ul>
```
:::

#### 採用關注點分離
資料與畫面分離，程式碼易維護、可重複使用。

:::spoiler 程式碼
```javascript=
// 資料
let data = [
  {
    id: 0,
    name: "肥宅心碎賞櫻3日",
    imgUrl:
      "https://images.unsplash.com/photo-1522383225653-ed111181a951?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1655&q=80",
    area: "高雄",
    description: "賞櫻花最佳去處。肥宅不得不去的超讚景點！",
    group: 87,
    price: 1400,
    rate: 10
  },
  {
    id: 1,
    name: "貓空纜車雙程票",
    imgUrl:
      "https://images.unsplash.com/photo-1501393152198-34b240415948?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1650&q=80",
    area: "台北",
    description:
      "乘坐以透明強化玻璃為地板的「貓纜之眼」水晶車廂，享受騰雲駕霧遨遊天際之感",
    group: 99,
    price: 240,
    rate: 2
  }
];
```

```javascript=
// 資料處理與渲染邏輯
function renderTicker(tickets) {
  let ticketList = "";
  tickets.forEach(function (ticket) {
    ticketList += `<li class="ticketCard">
                <div class="ticketCard-img">
                    <a href="#">
                        <img src="${ticket.imgUrl}"
                            alt="">
                    </a>
                    <div class="ticketCard-region">${ticket.area}</div>
                    <div class="ticketCard-rank">${ticket.rate}</div>
                </div>
                <div class="ticketCard-content">
                    <div>
                        <h3>
                            <a href="#" class="ticketCard-name">${ticket.name}</a>
                        </h3>
                        <p class="ticketCard-description">
                            ${ticket.description}
                        </p>
                    </div>
                    <div class="ticketCard-info">
                        <p class="ticketCard-num">
                            <span><i class="fas fa-exclamation-circle"></i></span>
                            剩下最後 <span id="ticketCard-num"> ${ticket.group} </span> 組
                        </p>
                        <p class="ticketCard-price">
                            TWD <span id="ticketCard-price">$${ticket.price}</span>
                        </p>
                    </div>
                </div>
            </li>`;
  });

  // 渲染
  ticketCardArea.innerHTML = ticketList;

  // 渲染文字
  searchResultText.textContent = `本次搜尋共 ${tickets.length} 筆資料`;
};

renderTicker(data);
```
:::

### 🔸 核心概念
![VS---17’23”](https://hackmd.io/_uploads/r1fC1n6Z-g.jpg)
1. 定義資料。
2. 資料處理與渲染畫面分開執行。
3. 著重在資料處理的能力。

#### 資料來源
* [關注點分離](https://ithelp.ithome.com.tw/articles/10238118)
* [關注點分離 Separation of Concerns (SOC)](https://hackmd.io/@Codeitaday/r1tyE6Luo)

## Vite 安裝與環境建置環境
### 🔸 為什麼要使用 Vite？
> Vite 讓前端開發從「繁瑣設定」變成「直接開發」。

#### 1. 快速建立開發環境
* 不需從零設定環境。
* 透過預設選項即可立即開始開發。

#### 2. 適合開發單頁式應用（SPA）
* 快速建立前端應用服務。
* 支援現代前端框架（如 React、Vue）。

#### 3. 整合性高的開發工具
* 將多種開發流程整合在同一工具中。
* 降低環境設定與維護成本。

#### 4. 套件管理一致化
* 由 CDN 改為 NPM 管理套件。
* 依賴關係與版本更可控。

### 🔸 環境建立
![螢幕擷取畫面 (1708)](https://hackmd.io/_uploads/HJ5GOFkHWl.png)
```
npm create vite@latest
```
* Project name: （自定義專案名稱）
* Select a framework: › React
* Select a variant: › JavaScript

### 🔸 安裝 NPM
```
npm install
```

### 🔸 運行方式
```
npm run dev
```
* 運行開發環境。
* `dev` 並不是 npm 的指令，是透過 package.json 檔案新增指令的。

### 🔸 專案環境結構說明
#### 1. `node_modules`
* 存放專案所使用的第三方套件。
* 由套件管理工具自動產生，不需手動修改。

#### 2. `public`
* 不會被編譯。
* 存放靜態資源（如圖片、favicon）。
* 內容會直接以原始檔案形式提供給瀏覽器。

#### 3. `src`
* 會被編譯。
* 主要開發目錄，所有應用程式邏輯都在此。
* `main.jsx` 為專案進入點（entry point）。
  * 所有元件與程式碼必須從此檔案被引入，才會納入編譯流程。

#### 4. `eslint.config`
* 程式碼品質與風格檢查工具設定檔。
* 用於檢查錯誤、潛在問題與一致的撰寫規範。

#### 5. `.gitignore`
* 設定 Git 要忽略的檔案與資料夾。
* 通常包含不需版控的檔案（如 `node_modules`、環境設定檔）。

#### 6. `package.json`、`package-lock.json`
* 專案與套件的描述檔。
* 記錄：
  * 安裝的套件。
  * 套件版本。
  * 專案指令（scripts）。
* `package-lock.json` 用於鎖定實際安裝的套件版本。

#### 7. `vite.config`
* Vite 的設定檔。
* 用來設定：
  * 專案啟動方式。
  * 編譯與打包行為。
  * 編譯後輸出的調整方式。

### 🔸 專案部署流程（GitHub Pages）
#### 1. 將專案放上 GitHub
* 建立 GitHub Repository。
* 專案需先完成 `commit` 並 `push`。

#### 2. 透過 Git 指令操作部署流程
* 本地端以 git 管理版本與上傳程式碼。

#### 3. 建立正式版檔案
```
npm run build
```
* 產生可部署的靜態檔案。
* Vite 預設輸出至 `dist` 資料夾。

#### 4. 安裝 `gh-pages` 套件
```
npm install gh-pages --save-dev
```
* 用途：將靜態檔案一鍵部署至 GitHub Pages。
* 屬於開發工具，因此通常放在 `devDependencies`。

#### 5. 設定部署指令
在 `package.json` 的 `"scripts"` 中加入：
```
"deploy": "gh-pages -d dist"
```
* `gh-pages`：執行部署套件。
* `-d dist`：指定要部署的資料夾。
  * `-d` = **directory（目錄）**。

#### 6. 執行部署
```
npm run deploy
```
* 若成功，終端機會顯示 `Published`。
* 代表已成功部署到 GitHub Pages。

#### 核心重點總結
* `build`：產生靜態檔案。
* `gh-pages`：負責把 `dist` 推到 GitHub Pages。
* `deploy`：實際執行部署的指令。

### 🔸 調整靜態檔案路徑（Vite）
> GitHub Pages 不是從根目錄部署時，必須設定 `base`，否則靜態資源會找不到。
#### 問題與說明
* Vite 預設使用**絕對路徑**載入靜態資源。
* 部署到 **GitHub Pages（非根目錄）** 時，資源路徑會錯誤。
* 必須調整為對應 Repository 的路徑。

#### `vite.config.js` 設定
```javascript
export default defineConfig({
  base: process.env.NODE_ENV === "production" ? "/Repository名稱/" : "/",
  plugins: [react()],
})
```

#### 1. `base`
* 設定專案的基礎路徑（所有靜態資源的前綴）。

#### 2. 環境判斷
```javascript
process.env.NODE_ENV === "production"
```

* 判斷目前是否為正式環境：

| 指令              | 環境   | 路徑               |
| --------------- | ---- | ---------------- |
| `npm run dev`   | 開發環境 | `/`              |
| `npm run build` | 正式環境 | `/Repository名稱/` |

#### 為什麼這樣做？
* 本地開發仍維持 `/`，避免路徑錯誤。
* 部署後資源能正確從 GitHub Pages 的子路徑載入。

### 🔸 載入外部套件
#### 1️⃣ Sass
**安裝**
```bash
npm add -D sass
```

**引入**
```scss
@import "./assets/all.scss";
```

**補充**
* 若使用 **Vue**，建議安裝：

  ```bash
  npm add -D sass-embedded
  ```
* 通常在 `main.jsx`（或 main.js）中引入全域樣式。

#### 2️⃣ Bootstrap
**安裝**
```bash
npm i bootstrap
```

**引入（使用 SCSS）**
```scss
@import "bootstrap/scss/bootstrap.scss";
```

**補充**
* 可指定版本：

  ```bash
  npm i bootstrap@5.3.8
  ```
* 未指定版本則安裝最新穩定版。

#### 3️⃣ Bootstrap 客製化變數（SCSS）
**目的**
* 覆寫 Bootstrap 預設樣式（顏色、字體、間距等）

**複製變數檔**
* 路徑：
  ```
  node_modules/bootstrap/scss/
  ```
* 複製以下檔案：
  * `_variables.scss`
  * `_variables-dark.scss`
* 貼到：
  ```
  src/assets/
  ```

**自定義變數**
* 在 SCSS 定義變數，再引入 Bootstrap：
```scss
$theme-colors: (
  "primary-100": #FBF7F5,
  "primary-300": #FEA375,
  "primary-500": #F2783C,
  "primary-700": #C26030,
  "primary-900": #613018,
  "primary-950": #30180C,
  "secondary-500": #433D39,
  "secondary-700": #252322,
  "secondary-900": #000000
);
```

**原因：**
使用覆蓋的方式，覆蓋 Bootstrap 的變數，以達修改目的，故 Bootstrap 必須最後引入。
```scss
@import "bootstrap/scss/bootstrap";
```

#### 4️⃣ axios

**安裝**
```bash
npm install axios
```

**在 App.jsx 引入**
```jsx
// 外部套件（第三方）放前面
import { useEffect, useState } from 'react'
import axios from 'axios'

// 內部資源放後面
import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg'
```

**慣例原則：**
* **外部套件 → 內部模組 → 靜態資源**
* 有助於可讀性與維護性。

#### 總結重點
* Sass：處理樣式與變數管理。
* Bootstrap：快速建立 UI。
* Bootstrap SCSS：可高度客製化設計系統。
* axios：負責 API 請求。
* 引入順序與結構，是專案可維護性的關鍵。

### 🔸 環境變數
> **環境變數的目的，是讓「設定跟著環境走，程式碼不需要動」。**

#### 1️⃣ 為什麼需要環境變數？
在實務開發中，同一份程式碼會跑在不同環境：

* **本地開發**：`localhost`
* **內部測試**：`dev.xxx`
* **正式上線**：`www.xxx`

如果每個環境都手動修改 API 路徑或設定：
* 容易出錯。
* 維護成本高。
* 不利於部署流程。

解法：
**將會變動的設定抽離成環境變數，由環境決定實際值。**

#### 2️⃣ Vite 的 `.env` 檔案規則

**官方文件：**

> [https://vite.dev/guide/env-and-mode#env-files](https://vite.dev/guide/env-and-mode#env-files)

Vite 會依照檔名與模式自動載入環境變數：

```bash
.env                 # 所有模式都會載入
.env.local           # 所有模式都會載入（Git 忽略）
.env.[mode]          # 僅指定模式載入（如 .env.development）
.env.[mode].local    # 指定模式載入（Git 忽略）
```

**常見模式：**
* `development`
* `production`

#### 3️⃣ 環境變數命名規則（Vite）
* **必須加上 `VITE_` 前綴**
* 否則前端程式無法讀取

**範例（`.env`）：**
```env
VITE_APP_PATH=https://randomuser.me/api/
```

#### 4️⃣ 在程式中使用環境變數
**基本引入方法**
```js
import.meta.env.VITE_APP_PATH
```

**解構寫法（推薦）**
```js
const { VITE_APP_PATH } = import.meta.env
```

**實務應用**
```js
axios.get(VITE_APP_PATH)
```

#### 5️⃣ 使用流程總結
1. 建立 `.env`（或 `.env.development`）。
2. 設定 `VITE_` 開頭的變數。
3. 透過 `import.meta.env` 讀取。
4. 取代寫死的路徑或設定值。
5. 切換環境時無需改程式碼。

### 🔸 資料來源
* [Day16 - Node.js 是什麼？為什麼大家都在使用？](https://ithelp.ithome.com.tw/articles/10331821)
* [Vite 是什麼? 為什麼要用 Vite? 它解決了哪些問題? 又是如何解決?](https://www.explainthis.io/zh-hant/swe/what-is-vite)
* [React 開發者前端建構工具之路](https://ithelp.ithome.com.tw/articles/10321762)
    
## 認識 React
### 🔸 學習流程
![VS---2’52”](https://hackmd.io/_uploads/r1NzVenG-e.jpg)

### 🔸 建構方法
* **HTML** → CDN（少見）
* **Vite** → 前端常用的方法、SPA（常見）
* **Next.js** → SSR 前端整合形式

### 🔸 五步驟載入 React
:::warning
以下是課程教學使用，實務上建議使用 Vite 或其他建構工具載入 React。
:::
#### 1. 載入資源
**[React](https://legacy.reactjs.org/docs/cdn-links.html#gatsby-focus-wrapper)**
```html=
// React 函式庫
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>

// React DOM 操作
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
```
* `development`：開發模式。
* `production`：正式機。

**[JSX](https://zh-hant.legacy.reactjs.org/docs/add-react-to-a-website.html#quickly-try-jsx)**
```html=
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
```
* JSX 是一種 JavaScript 語法擴充，可以在 JavaScript 裡直接寫「像 HTML 的結構」，以提升 React 程式碼的可讀性。
* Babel 是編譯器，負責把 JSX 轉成瀏覽器看得懂的 JavaScript。
* 若要使用 Babel，需要在 `<script>` 中加入 `type="text/babel"`，才能正確運行。

#### 2. 測試是否有正確引入
```jsx=
console.log(React, ReactDOM);
```
* 會回傳兩筆物件，一個是 React 函式庫，一個是 ReactDOM 物件。
* ReactDOM 的 DOM 必須是大寫。

#### 3. 加入 root 元素
```html=
<div id="root"></div>
```
* React 的元素都會生成在 root 裡。

#### 4. 建立 React 元件
```jsx=
function App() {
    return <h1>React 我來了</h1>
}
```
* React 的元件都是函式。
* 函式名稱必須大寫。
* 因為 JSX 的關係，不必加入反引號，就可以寫出「像 HTML 的結構」。

#### 5. 渲染元件至 root
```jsx=
const el = document.querySelector("#root");
const root = ReactDOM.createRoot(el);

root.render(<App />);
```
* `ReactDOM.createRoot()`：建立一個根元素的節點。
* `root.render(<App />)`：
    * 在根元素的節點中，渲染 App 元件產生的畫面。
    * 必須加上結尾標籤 `/`。

### 🔸 資料驅動 React：建立版型
```jsx=
  function App() {
    return <div className="card">
      <img
        src="https://images.unsplash.com/photo-1765600112366-6688f98dfa33?q=80&w=687&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
        className="card-img-top" alt="..." />
      <div className="card-body">
        <h5 className="card-title">Card title</h5>
        <p className="card-text">Some quick example text to build on the card title and make up the bulk of the card's
          content.</p>
        <a href="#" className="btn btn-primary">Go somewhere</a>
      </div>
    </div>
  }

  const el = document.getElementById('root');
  const root = ReactDOM.createRoot(el);
  root.render(<App />);

```
* 基於 React 的規則：
    * `<img>` 標籤必須加上結尾標籤 `< />`。
    * 屬性名稱 `class` 必須修改成 `className`。

### 🔸 資料驅動 React：資料抽離
```jsx=
// 資料抽離
// 觀察上一段程式碼 有哪些內容可以作為資料抽離
function App() {
const data = {
    imageUrl: "https://images.unsplash.com/photo-1765600112366-6688f98dfa33?q=80&w=687&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
    title: "Card title",
    content: "Lorem ipsum dolor sit amet consectetur.",
    link: "https://www.google.com/?hl=zh_TW"
}

return <div className="card">
  <img src={ data.imageUrl } className="card-img-top" alt="..." />
  <div className="card-body">
    <h5 className="card-title">{ data.title }</h5>
    <p className="card-text">{ data.content }</p>
    <a href={ data.link } className="btn btn-primary">Go somewhere</a>
  </div>
</div>
}

const el = document.getElementById('root');
const root = ReactDOM.createRoot(el);
root.render(<App />);
```
* 使用 `{}` 帶入物件中的資料。
* 其中有一項規則是只能放入表達式。

:::warning
`return` 之前的程式碼是為了決定資料狀態，而之後則是如何將資料渲染至畫面。
:::

### 🔸 什麼是 JavaScript 陳述式？表達式？
> [JavaScript 表達式觀念及運用](https://www.casper.tw/development/2020/09/17/js-expression/)
> 
在 JavaScript 中，語句分為陳述式與表達式，而在 React 中可內嵌表達式，因此使用 React 時，需要認識兩種語句的差異。
除此之外，學會區分陳述式與表達式，就知道「什麼是流程、什麼是結果」，是使得程式碼更加精簡的關鍵。

#### 陳述式的特徵
* 由多個單字或句子組成。
* 目的是執行某些操作，不會回傳值。
* `if...else`、`try...catch`、`var`、`let`、`const` ......
* 控制流程。

#### 表達式的特徵
* 1 ~ 5 個左右的單字。
* 可以賦值給變數，會回傳值。
* `10`、`1+1`、`'hello'`、`myFunction()` ......
* 回傳結果。

#### 比較
* `let a = 1;`（不會回傳值）
* `a = 2;`（會回傳 2）

#### 陳述式可以包表達式
```javascript=
if(true) {
    console.log(123);
};
```
* 雖然執行後會回傳 123，但這是因為 `console.log(123);` 的緣故，並非 `if` 的關係。
* `if` 為陳述式，`console.log` 為表達式。

### 🔸 表達式與 React 的關係
#### 使用三元運算子替換元件資料
```jsx=
function App() {
  const data = {
    name: "卡斯伯"
  }
    
  return <div className="card w-50">
    <img src={data.imageUrl} className="card-img-top" alt="..." />
    <div className="card-body">
      <h5 className="card-title">{data.name}</h5>
      {data.name ? data.name : "小花"}, {/* 使用三元運算子替換使用者名稱或其他資料 */}
    </div>
  </div>
}
```
* 若 `data.name` 無資料會回傳「小花」，若有，則是回傳 `data.name` 的值。

#### 可帶入函式表達式
```javascript=
// 宣告式
function fn() {}

// 表達式 --> 可放 JSX 裡的
const fn = function () {}
```
```jxs=
{(function () { return 2 * 2 })()}
```
* 執行函式方式有兩種：
    * `fn();`
    * `(function (){})();`

### 🔸 JS 型別與 JSX
#### JSX 不會呈現物件型別及特定的原始型別
* 在 `{}` 中帶入物件會報錯，例如 `{ data }`，會回傳「這不是有效的 React 子元素。」的錯誤訊息。
* `{ true }`、`{ false }`、`{ undefined }`、`{ null }` 不會呈現在畫面上，若要呈現，可使用三元運算子呈現。
    * `{ true ? "true" : "false" }`

#### 陣列中的值會直接呈現在畫面上
* `{ [1, 2, 3, <div>我是小花</div>] }`
* 可以在陣列中加入 html 的結構，會作為標籤結構呈現在畫面上，但需要加入 key 值（後續會提到）。

#### 將 HTML 結構賦值在變數
```jsx=
const list = [<li class="list-group-item">An item</li>,<li class="list-group-item">A second item</li>,<li class="list-group-item">A third item</li>];

<ul className="list-group list-group-flush">
    { list }
</ul>
```

* 綜合上述的特性，可以將 HTML 結構賦值在變數上，在元件中只要帶入該變數，便能帶入整組 html 結構。

### 🔸 JSX 是如何運作的
#### 簡介
| #     | 功能                                                                                             |
| ----- | ------------------------------------------------------------------------------------------------ |
| JSX   | JavaScript 語法擴充，可以在 JavaScript 裡直接寫「像 HTML 的結構」，以提升 React 程式碼的可讀性。 |
| Babel | 編譯器，負責把 JSX 轉成瀏覽器看得懂的 JavaScript。                                               | 

#### JS 原生寫法
```javascript=
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
function App() {
  return _jsxs("h1", {
    children: ["React \u6211\u4F86\u4E86 ", _jsx("small", {
      className: "text-danger",
      children: new Date().toLocaleDateString()
    })]
  });
}
const el = document.getElementById('root');
const root = ReactDOM.createRoot(el);
root.render(_jsx(App, {}));
```

#### JSX 寫法
```jsx=
function App() {
  return <h1>React 我來了 <small className="text-danger">{new Date().toLocaleDateString() }</small></h1>
}

const el = document.getElementById('root');
const root = ReactDOM.createRoot(el);
root.render(<App/>)
```

### 🔸 JSX 與 HTML 的標籤屬性
> 基於 React 的規則，需要將 HTML 標籤做一些修改，才能在 React 的環境中運行。

#### className
```html=
<div>
    <button type="button" className="btn btn-primary">Primary</button>
</div>
```
* 屬性 `class` 需改為 `className`。

#### checked 與結尾標籤
```html=
<div>
    <input type="text" defaultValue="小花" />
</div>
```
* 若要設定預設值，要將屬性 `value` 需改為 `defaultValue`。
* `input` 需要加上結尾標籤，或是以 `/` 結尾即可。

#### htmlFor
```html=
<div>
    <label htmlFor="email">請輸入 Email</label>
    <input type="email" id="email" />
</div>
```
* 屬性 `for` 需改為 `htmlFor`。

#### selected
```html=
<div>
    <select name="" id="" defaultValue="2">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
    </select>
</div>
```
在 HTML 中，若要指定預設值為 2 的話，語法會是 `<option value="2" selected="">2</option>`；而在 React 裡，則是寫在標籤 `select`，使用 `defaultValue` 設定預設值。

#### textarea
```html=
<div>
    <textarea name="" id="" cols="30" rows="5" defaultValue="這裡可以放多行文字" />
</div>
```
* 在標籤 `textarea` 加入屬性 `defaultValue` 設定預設值。
* 需要加上結尾標籤，或以 `/` 結尾。

#### dangerouslySetInnerHTML
```jsx=
function App() {
    const htmlTemplate = {
        __html: '<div>這裡有一段文字</div>'
    };

    return (
        <div dangerouslySetInnerHTML={ htmlTemplate }></div>
    )
}
```
* 基於安全問題，無法直接使用 `{htmlTemplate}`　渲染 HTML 結構，這麼做會以純字串呈現在畫面上。
* 若要渲染資料的話：
    1. 需要使用屬性 `dangerouslySetInnerHTML`。
    2. 將資料改為物件，並加上屬性名稱 `__html`。

#### onChange
```jsx=
<div>
    <input type="text" onChange={
        function(e){
            console.log(e.target.value);
        }
        } />
</div>
```
* `onChange`：綁監聽 + 監聽事件為 `Change`。
* 若要取得表格內的值，可在標籤 `input` 加入屬性 `onChange`，接著使用 `function` 取得表格內的值。

### 🔸 React 行內樣式
#### 縮寫渲染程式碼
```jsx=
// 縮寫前
// const el = document.querySelector("#root");
// const root = ReactDOM.createRoot(el);
// root.render(<App />)

// 縮寫後
// 1.
ReactDOM.createRoot(document.querySelector("#root")).render(<App />);

// 2.
ReactDOM
    .createRoot(document.querySelector("#root"))
    .render(<App />);
```
1. `document.querySelector("#root")` 移到 `ReactDOM.createRoot` 裡。
2. `ReactDOM.createRoot(document.querySelector("#root"));` 後方直接接 `render()`。

#### 屬性 style
```jsx=
return (
    <div className='card' style={{width: "18rem"}}>
    <div className='card-img-top' style={{
        height: "200px",
        backgroundImage: `url('https://images.unsplash.com/photo-1564564321837-a57b7070ac4f?ixlib=rb-1.2.1&amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;auto=format&amp;fit=crop&amp;w=2352&amp;q=80')`,
        backgroundSize: "cover",
        backgroundPosition: "center center"
    }}></div>
    </div>
);
```
* 使用屬性 `style` 加入行內樣式，`style={{}}` 的第一個括號表示可帶入表達式，第二個括號表示物件，因此需要此物件的格式帶入資料。
* 屬性名稱不可以有 `-`，因此使用小駝峰命名法。
* 遇到網址這類帶有特殊符號的值，使用反引號帶入資料。

### 🔸 JSX 開發中常見的錯誤
* 使用大寫定義元件，例如 `App()`。
* 多個元素外層需要使用標籤包覆，若希望不要產生多餘的標籤例如 `<div>`，可以使用 `React.Fragment` 或 `<></>` 包覆程式碼。
* 沒有結尾標籤。
* 未使用正確的屬性名稱，或引入方式，例如 `className`、`style={{}}`。
    * JSX 的本質是 JavaScript，會避免屬性名稱與 JavaScript 的關鍵字相同，例如 `class`、`for` ... 不建議使用。
* `return` 後方若無程式碼，會因**自動插入分號**的機制中斷執行；若需要繼續執行，可加入 `()`。
```jsx=
// 會執行 <div>
function App() {
    return <div> 123 </div>;
};

// 不會執行 <div>
function App() {
    return // 若後方無程式碼會中斷執行
    <div> 123 </div>;
};

// 加入 () 繼續執行
function App() {
    return ( // 需要與 return 同一行
    <div> 123 </div>);
};
```

## JavaScript 必學觀念
### 🔸 常見縮寫
#### 語法糖與新增語法
```javascript=
// 語法糖
const obj = {
  myName: '物件',
  fn: function() {
    return this.myName;
  }
};
console.log(obj.fn()); // 物件

// 箭頭函式
const obj = {
  myName: '物件',
  fn: () => {
    return this.myName;
  }
}
console.log(obj.fn()); // error 結果與上方不同 不是語法糖
```
* 語法糖的邏輯與當前的 JS 一致，只是提供更簡潔的寫法。
* 若會改變邏輯的話，表示當前的寫法是新增語法，而非語法糖，例如箭頭函式。

### 🔸 物件字面值
#### 物件內的函式
```javascript=
// 縮寫前
const obj3 = {
  myName: '物件',
  fn: function() { // 縮寫物件內的函式
    return this.myName;
  }
};

// 縮寫後
const obj3 = {
  myName: '物件',
  fn() {
    return this.myName;
  }
};
```
* `fn: function(){};` → `fn(){};`

#### 物件內的變數
```javascript=
// 縮寫前
const name = "小明";
function fn() {
  return this.name;
};
const person = {
  name: name, // 縮寫物件屬性名稱
  fn: fn
};
console.log(person,person.fn()); // {name: '小明', fn: ƒ}

// 縮寫後
const name = "小明";
function fn() {
  return this.name;
};
const person = {
  name,
  fn
};
console.log(person,person.fn()); // {name: '小明', fn: ƒ}
```
* 在物件字面值的概念中，若屬性名稱與傳入的變數名稱相同（或函式），留下一個名稱即可，稱為「物件屬性名稱縮寫」。
    * `name: name` → `name`

### 🔸 展開
#### 不同陣列合併
```javascript=
// 使用 concat();
const groupA = ['小明', '杰倫', '阿姨'];
const groupB = ['老媽', '老爸'];
const groupC = groupA.concat(groupB);
console.log(groupC); // ['小明', '杰倫', '阿姨', '老媽', '老爸']

// 使用展開運算符
const groupA = ['小明', '杰倫', '阿姨'];
const groupB = ['老媽', '老爸'];
const groupC = [...groupA, ...groupB];
console.log(groupC); // ['小明', '杰倫', '阿姨', '老媽', '老爸']
```
* 在 ES6 之前，會使用 `concat();` 方法合併不同陣列，但使用展開運算符 `...` 可以讓這個過程變得更加簡潔。

#### 物件擴展（淺拷貝）
```javascript=
// 原本的物件
const methods = {
  fn1() {
    console.log(1);
  },
  fn2() {
    console.log(1);
  },
}
console.log(methods); // {fn1: ƒ, fn2: ƒ}

// 新建立的物件
const newMethods = {
  ... methods,
  fn3() {
    console.log(1);
  }
}
console.log(newMethods); // {fn1: ƒ, fn2: ƒ, fn3: ƒ}
```
* 展開運算符具有淺拷貝的特性，新增的新屬性不會影響原物件。

#### 轉成純陣列
```javascript!
const doms = document.querySelectorAll('li');
console.log(doms); // 此時的 Prototype 為 NodeList 請轉為 Array

const newDoms = [...doms];
console.log(newDoms); // Prototype 為 Array
newDoms.map((e) => { // 可以使用了 !
  console.log(e)
});
```
* 展開 `doms` 會看到 Prototype 為 NodeList，能使用的方法比較少 例如無法使用 `map()`。
* 使用展開運算符轉為陣列後，Prototype 為 Array，就能使用 `map()` 了！

#### 參數預設值
```javascript=
// 未設定參數預設值
function sum(a, b) {
  return a + b;
}
console.log(sum(1)); // NaN

// 設定參數預設值
// 1. 只寫一個參數
function sum(a, b = 1) {
  return a + b;
}
console.log(sum(1)); // 2

// 2. 兩個參數都有寫
function sum(a, b = 1) {
  return a + b;
}
console.log(sum(1, 5)); // 6
```
* 有些情況，不一定所有參數都要填寫時，可設定參數預設值，避免錯誤。
* 參數預設值的寫法：`(a, b = 1)` 表示 b 的預設值為 1，若使用者為填寫參數時，會自動帶入 1；有填寫參數時，則會覆蓋預設值的設定。

### 🔸 解構
#### 物件取值
```javascript=
// 未使用解構取值
const name = person.name;
const age = person.age;
console.log(name, age); // 小明 16

// 使用解構取值
const {name, age} = person;
console.log(name, age); // 小明 16
```
* `const { 屬性名稱 } = 物件;`

#### 陣列取值
```javascript=
const array = ['小明', '杰倫', '漂亮阿姨'];
const [a, b, c] = array;
console.log(a, b, c); // 小明 杰倫 漂亮阿姨
```
* `const [ 變數名稱 ] = 陣列;`
* 陣列解構時，`[]` 內是變數宣告，值會依位置順序帶入變數。

#### 解構 + 展開
```javascript=
const people = {
  Ming: {
    name: '小明',
    age: 16,
    like: '鍋燒意麵'
  },
  Jay: {
    name: '杰倫',
    age: 18,
    like: '跑車'
  },
  Auntie: {
    name: '漂亮阿姨',
    age: 22,
    like: '名牌包'
  }
}

// 目標
// 1. Ming 為一筆資料 
// 2. Jay + Auntie 為一筆資料
const { Ming, ...other } = people;
console.log(Ming, other); // {name: '小明', age: 16, like: '鍋燒意麵'} {Jay: {…}, Auntie: {…}}

// 3. 解構小明的年齡與喜歡的東西
const {age, like} = Ming;
console.log(age, like); // 16 '鍋燒意麵'
```
* `const { Ming, ...屬性名稱 } = people;`

#### 重新命名
```javascript=
// 前面已使用 Ming 作為變數名稱
let Ming = {};

// 解構時 不能再用 Ming 作為名稱
const { Ming: newMing } = people;
console.log(newMing);
```
* `const { 原本的屬性名稱: 重新命名的屬性名稱 } = people;`
* 使用 `:` 重新命名。

#### 函式參數
```javascript=
const person = {
  name: '小明',
  age: 16,
  like: '鍋燒意麵'
}

// 解構前
function callName (params) {
  console.log(params.name, params.age)
};
callName(person); // 小明 16

// 解構後
function callName ({name, age}) {
  console.log(name, age)
};
callName(person); // 小明 16
```
* `()` 加入 `{}` 解構物件資料。

#### 解構在 React 的運用
```jsx=
const { useState } = React;

function Card() {
  return (<div className="card">
    <div className="card-body">

    </div>
  </div>)
}

function App() {
  const [person, setPerson] = useState({
    name: '小明',
  })

  return (
    <div>
      我是{person.name}
      <Card person={person} ></Card>
    </div>
  )
}
```
* `const { useState } = React;`
    * 從 React 這個物件中，取出名為 `useState` 的屬性，並宣告成同名變數。
* `const [person, setPerson] = useState({ ... ,})`
    * `useState()` 會回傳一個陣列，使用陣列解構將第 0、1 個值分別指定給 `person` 與 `setPerson`。

### 🔸 箭頭函式
#### 寫法
```javascript=
// 改寫前
function fn(a, b) {
  return a * b;
}
console.log(fn(2, 4)); // 8

// 改寫後
const fn = (a, b) => {
  return a * b;
}
console.log(fn(2, 4)); // 8

// 當回傳的內容是物件
// 請加上 ()
// 避免程式碼誤認 {} 為區塊主體 導致傳出的值為 undefined
// 未加上 ()
const fn = () => {
  name: "小明"
};
console.log(fn()); // undefined

// 加上 ()
const fn = () => ({
  name: "小明"
});
console.log(fn()); // {name: '小明'}
```
* **箭頭函式不是語法糖**，但有許多縮寫的特性。
* 是函式表達式，因此函式一定會宣告變數。

#### this 的變化
```javascript=
// 傳統函式
var name = '全域'
const person = {
  name: '小明',
  callName: function () { 
    console.log('1', this.name); // 1 小明
    setTimeout(function () {
      console.log('2', this.name); // 2 全域
      console.log('3', this); // 3 Window
    }, 10);
  },
}

person.callName();

// 箭頭函式
var name = '全域'
const person = {
  name: '小明',
  callName: function () { 
    console.log('1', this.name); // 1 小明
    setTimeout(() => { // 這裡改成箭頭函式
      console.log('2', this.name); // 2 小明
      console.log('3', this); // 3 小明物件
    }, 10);
  },
}

person.callName();
```
* 箭頭函式的 `this` 指向，會跟當前外層作用域是一致的。
* React Hook 寫法不太會遇到 `this` 的問題，這裡可以初步認識就好。

#### 縮寫
```javascript=
// 縮寫前
const newPeopleList = peopleList.map((person, key) => {
const templateString = `<li>${person.userName}喜歡吃${person.like}</li>`;
    return templateString;
});

const ul = document.querySelector("ul");
ul.innerHTML = newPeopleList.join(""); // map 會用逗號隔開資料 可使用 join("") 去除逗號

// 縮寫後
const newPeopleList = peopleList.map((person, key) => `<li>${person.userName}喜歡吃${person.like}</li>`);

const ul = document.querySelector("ul");
ul.innerHTML = newPeopleList.join("");

// 再縮寫
// 不再另外宣告變數 直接將程式碼移過去？！
const ul = document.querySelector("ul");
ul.innerHTML = peopleList.map((person, key) => `<li>${person.userName}喜歡吃${person.like}</li>`).join("");
```
* 因箭頭函式自帶 `return`，故可以進行以下縮寫：
    * `{}` 內只有一行的情況。
    * 刪除 `return`。
    * 刪除 `{}`。
    * 調整成同一行。
* 當參數只有一個的時候，可以將 `()` 刪除：
    ```javascript=
    const fn = a => a;
    console.log(fn(5)); // 5
    ```
* 在 React 的環境下會處理掉逗點，故可不用使用 `.join("")`。
    
#### 箭頭函式在 React 的運用
```jsx=
// 改寫前
function Card() {
  return <div className="card">
    <div className="card-body">這是一張卡片</div>
  </div>
}

function App() {
  return (
    <div>
      <Card></Card>
    </div>
  )
}

// 改寫後
const Card = () => {
  return <div className="card">
    <div className="card-body">這是一張卡片</div>
  </div>
}

const App = () => {
  return (
    <div>
      <Card></Card>
    </div>
  )
}
```

### 🔸 陣列迴圈處理方法
#### 操作同一個陣列內的值
```javascript=
const people = [
  {
    name: '卡斯伯',
    like: '鍋燒意麵',
    price: 80
  },
  {
    name: '小明',
    like: '牛肉麵',
    price: 120
  },
  {
    name: '漂亮阿姨',
    like: '滷味切盤',
    price: 40
  },
  {
    name: 'Ray',
    like: '大麻醬乾麵',
    price: 60
  }
];

people.forEach((item) => {
  item.newPrice = item.price * 0.8;
});

console.log(people); // {...newPrice: 64} {...newPrice: 96} {...newPrice: 32} ...newPrice: 48
```
* `forEach()` 不會產生新陣列，而是影響原本的陣列。

#### 建立新陣列
```javascript=
const order = people.map((item) => {
  return {
    item: item.like,
    price: item.price * 0.8
  }
});

console.log(order); // {item: '鍋燒意麵', price: 64} ...

// 縮寫
// 1. 刪除 {}、return
// 2. 補上 ()
const order = people.map((item) => ({
    item: item.like,
    price: item.price * 0.8
  })
);

console.log(order); // {item: '鍋燒意麵', price: 64} ...
```
* `map()` 會產生新陣列，並不影響原本的陣列。

#### 使用 map() 的注意事項
```javascript=
const people = [
  {
    name: '卡斯伯',
    like: '鍋燒意麵',
    price: 80
  },
  {
    name: '小明',
    like: '牛肉麵',
    price: 120
  },
  {
    name: '漂亮阿姨',
    like: '滷味切盤',
    price: 40
  },
  {
    name: 'Ray',
    like: '大麻醬乾麵',
    price: 60
  }
];

// 即使不回傳值 也會回傳 undefined
const order = people.map((item) => {
  if (item.price > 60) {
    return {
      item: item.like,
      price: item.price * 0.8
    }
  }
});
console.log(order); // [{…}, {…}, undefined, undefined]

// 解法
// 1. 先使用 filter 判斷結果
// 2. 再使用 map() 產生新陣列
const filterOrder = people.filter((item) => item.price > 60);
const order = filterOrder.map((item) => ({
  item: item.like,
  price: item.price * 0.8
}));
console.log(order); // {item: '鍋燒意麵', price: 64} {item: '牛肉麵', price: 96}

// 縮寫
const order = people.filter((item) => item.price > 60) // 注意這裡沒 ;
  .map((item) => ({
  item: item.like,
  price: item.price * 0.8    
  }));
console.log(order); // {item: '鍋燒意麵', price: 64} {item: '牛肉麵', price: 96}
```
* `map()` 的回傳數量會等於原始陣列的長度，若遇到不回傳（例如未符合條件），也會回傳 `undefined`。

#### `map()` 在 React 中的運用
```jxs=
const App = ()=> {
  const people = [
    {
      name: '卡斯伯',
      like: '鍋燒意麵',
      price: 80
    },
    {
      name: '小明',
      like: '牛肉麵',
      price: 120
    },
    {
      name: '漂亮阿姨',
      like: '滷味切盤',
      price: 40
    },
    {
      name: 'Ray',
      like: '大麻醬乾麵',
      price: 60
    }
  ];
  return <div>
    <ul>
      {
        people.map((item, i) => {
          return <li key={i}>{item.name}喜歡{item.like}</li>
        })
      }
    </ul>
  </div>
}
```

### 🔸 JSX 章節作業優化
```javascript=
function App() {
  const products = [
    {
      imageUrl: "https://images.unsplash.com/photo-1520013573795-38516d2661e4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1536&q=80",
      title: "強力牙刷",
      price: 99,
      link: "https://www.google.com/?hl=zh_TW"
    },
    {
      imageUrl: "https://images.unsplash.com/photo-1618038483079-bfe64dcb17f1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80",
      title: "海綿",
      price: 10,
      link: "https://www.google.com/?hl=zh_TW"
    },
    {
      imageUrl: "https://images.unsplash.com/photo-1546552696-7d5f4e89b0e8?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80",
      title: "掛勾",
      price: 100,
      link: "https://www.google.com/?hl=zh_TW"
    }
  ];

return <>
  <div className="row row-col-3">
      {
        products.map((item,i) => (<div className="col" key={i}>
          <a href={item.link} className="card">
            <img className="card-img-top object-fit" src={item.imageUrl} alt="" />
            <div className="card-body">
              <span className="text-dark">{item.title}</span>
              <span className="float-end">${item.price}</span>
              </div>
            </a>
        </div>))
      }
    </div>
  </>
}

ReactDOM.createRoot(document.querySelector("#root")).render(<App />);
```
* HTML 結構只要寫一次就好，剩下的迴圈會根據有幾筆資料，就產生幾次結構。

### 🔸 物件參考特性
#### 物件是以傳參考的形式賦值
```javascript=
const person = {
  name: '小明',
  obj: {}
};

const person2 = person;
person2.name = "杰倫";
console.log(person2); // {name: '杰倫', obj: {…}}

const { obj } = person;
obj.name = "person 下的物件";
console.log(person.obj.name); // person 下的物件
```

#### 物件作為參數
```javascript=
const fn = (item) => {
  item.name = '杰倫';
};

const person = {
  name: '小明',
  obj: {}
};

fn(person);
console.log(person.name); // 杰倫
```
* 物件作為參數傳遞，一樣具備傳參考特性。

#### 淺層拷貝
```javascript=
const person = {
  name: '小明',
  obj: {}
}

const person2 = { ...person };
person2.name = "杰倫"; // 指向不同記憶體位置
person2.obj.name = " person2 的值"; // 還是指向相同記憶體位置
console.log(person, person2); // {name: '小明', obj: {…}} {name: '杰倫', obj: {…}}
```
* 可以使用展開運算符進行淺層拷貝。
* 由於拷貝出來的物件，只有第一層指向的記憶體位置不同，再下一層（`obj`）的記憶體位置還是相同，故稱為淺層拷貝。
* 淺層拷貝的語法較精簡，若確認需求不會動到深層物件，可使用淺層拷貝處理資料即可。

#### 深層拷貝
```javascript=
const person = {
  name: '小明',
  obj: {}
};

const person2 = JSON.parse(JSON.stringify(person));
person2.name = "杰倫"; // 指向不同記憶體位置
person2.obj.name = " person2 的值"; // 指向不同記憶體位置
console.log(person, person2);
```
* 作法：
    1. 使用 `JSON.stringify();` 先轉成字串。
    2. 再使用 `JSON.parse()` 轉回物件。

### 🔸 非同步與 Promise
#### 非同步的觀念
```javascript=
function getData() {
  setTimeout(() => {
    console.log('... 已取得遠端資料');
  }, 0);
}
// 請問取得資料的順序為何
function init() {
  console.log(1);
  getData();
  console.log(2);
}

init(); // 1, 2, ... 已取得遠端資料
```
* 所有非同步操作都會最後執行，但無法控制執行順序，因此出現了 Promise 解決傳統非同步語法難以建構及管理的問題。

#### Promise
```javascript=
// 基本結構
const promiseSetTimeout = (status) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (status) {
        resolve('promiseSetTimeout 成功')
      } else {
        reject('promiseSetTimeout 失敗')
      }
    }, 0);
  })
};

// 使用 .then 接收結果
promiseSetTimeout(1)
  .then((res) => {
    console.log(res); // promiseSetTimeout 成功
  });

// 串接多個 Promise 結果
promiseSetTimeout(1)
  .then((res) => {
    console.log(res);
    console.log(1);
    return promiseSetTimeout(1); // 使用 return 傳出結果
  })
  .then((res) => { // 再次使用 .then 接收
    console.log(res);
    console.log(2);
  })

// 使用 .catch 捕捉錯誤
promiseSetTimeout(0)
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err); // promiseSetTimeout 失敗
  })
```
* 基於 Promise 建構的函式。
* `const promiseSetTimeout = (status) => { ... };` 傳入參數，決定結果是成功或失敗。
* `return new Promise((resolve, reject) => { ... };` 建構 Promise 的程式碼。
    * `resolve`：成功。
    * `reject`：失敗。
* 若直接執行 `promiseSetTimeout();` 會看不到結果，因為必須使用 `.then` 接收結果。
* 接收多個 Promise 的結果時，請避免使用巢狀結構接收，而是使用 `return` 傳出結果後，再使用 `.then` 接收。
* 若在「等待 Promise 結果」且「沒有捕捉錯誤」的情況，會中斷同一區塊中（`{}`）程式碼的執行，因此需要設定捕捉錯誤的方法（`catch`）。

#### 實戰運用
```javascript=
axios.get("https://randomuser.me/api/")
  .then((res) => {
    console.log(res);
    return axios.get("https://randomuser.me/api/")
  })
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  })
```
* [axios](https://github.com/axios/axios)
* [Random User Generator](https://randomuser.me/)
* axios 是基於 Promise 開發的，故可以使用 `.then` 跟 `.catch` 的方法處理回傳結果。

### 🔸 Async / Await
#### 當資料有順序性時
```javascript=
// 1. 取得 seed ( seed 可產生同一筆用戶資料 )
// 2. 再次取得同一筆用戶
axios.get("https://randomuser.me/api/")
  .then((res) => {
    console.log(res);
    const { seed } = res.data.info
    return axios.get(`https://randomuser.me/api/?seed=${seed}`)
  })
  .then((res) => {
    console.log(res); // 與上方為同一筆用戶資料    
  })
```
* [Random User Generator／Seeds](https://randomuser.me/documentation#seeds)

#### 使用 Async / Await 改寫
```javascript=
const asyncFn = async () => {
  const res = await axios.get("https://randomuser.me/api/");
  const { seed } = res.data.info;
  
  const res2 = await axios.get(`https://randomuser.me/api/?seed=${seed}`);
  console.log(res, res2);
};

asyncFn();
```
* Async / Await 是 Promise 的語法糖，因此對 Promise 越熟悉，有助於使用 Async / Await。
* `async` 標記一個函式為「非同步函式」。
* `await` 運算子用於等待 Promise 的結果。它將非同步邏輯以同步語法呈現，取代了傳統的 `.then()` 鏈式調用，顯著提升代碼的可讀性。
* 注意 `async` 只有一個作用域 `{}`，故變數命名不能重複。

#### 立即函式
```javascript=
// 基本結構
// (function() {
//   console.log(1);
// })();

// 改寫
(async () => {
  const res = await axios.get("https://randomuser.me/api/");
  const { seed } = res.data.info;
  
  const res2 = await axios.get(`https://randomuser.me/api/?seed=${seed}`);
  console.log(res, res2);
})();
```

### 🔸 Async / Await 實戰運用
#### 取得 Async function 回傳值的方法
```javascript=
const asyncFn = async () => {
  const res = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
  res.data.name = "小花"; // 新增資料
  return res;
};

asyncFn()
  .then(res => {
    console.log(res);
  });
```
* 在 `async` 函數內部使用 `await` 取代 `.then()`。
* 但在外層，依舊可使用 `.then()` 接收結果。
* Async / Await 與 Promise 通常會擇一使用，但也可以交互使用。

#### Async function 錯誤處理
```javascript=
const asyncFn = async () => {
  try {
    const res = await axios.get("https://jsonplaceholder.typicode.com/todos/1")
    return res;
  } catch (error) {
    throw error; // 拋出錯誤結果
  }
};

asyncFn()
  .then(res => {
    console.log("Promise:", res);
  })
  .catch(err => {
    console.log("Promise Error:", err);
  })
```
* `try...catch` 是一個程式設計中的錯誤處理結構。
* `throw` 是一種程式控制結構，用於在程式中拋出錯誤，並中斷當前執行流程。

#### Async Function 與 React
```jsx=
const App = () => {
  const [data, setData] = React.useState({}) 

  React.useEffect(() => {
    (async () => {
      const res = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
      console.log(res);
      setData(res.data); // React 的方法
    })();
  }, []);

  return <div>
    {data.title}  {/* React 的方法 */}
  </div>
}

ReactDOM
  .createRoot(document.getElementById('root'))
  .render(<App/>)
```

### 🔸 模組化 JavaScript：ESModule
#### 基礎知識
```javascript=
// 匯出
export default function fn() {
    console.log(1);
};

// 匯入
import fn from "./fnModule.js";
fn();
```
* 將函式模組化，以利重複調用（會將模組存在另一個檔案裡，再透過引入的方式匯入主要開發的檔案裡）。
* 運作概念基本上分為匯出與匯入。
* `<script>` 的 `type` 需要加入 `module` 模組才能運作，但會與 `type="text/babel"` 相衝，故課程前期不會用到 ESModule 定義元件。
* JavaScript 檔案放在 CDN 上提供時，檔名最好明確以 `.js` 結尾。
    * CDN 依副檔名判斷檔案類型。
    * `.js → application/javascript`

#### 預設匯出
```javascript=
export default {
    myName: "小花",
    fn() {
        console.log("我是小花");
    }
};
```
* 關鍵字 `export default`。
* 每個檔案裡只能有一個預設匯出。
* 不用為模組命名。
* 是常見的匯出方式，通常用於匯出物件，在 React 開發中可用來匯出元件。

#### 預設匯入
```javascript=
import obj from "./fnModule.js";
console.log(obj); // {myName: '小花', fn: ƒ}
obj.fn(); // 我是小花
```
* 因為預設匯出時不用命名，所以預設匯入時，可以為模組命名。

#### 具名匯出
```javascript=
export const myName = "小花";
export function fn() {
    console.log("我是小花");
}
```
* 關鍵字 `export`。
* 每個檔案裡可以有多個具名匯出。
* 要為模組命名（自定義名稱）。
* 可用於匯出已定義的變數、物件、函式，專案開發中通常用於「方法（函式）匯出」。
* 第三方的框架、函式、套件很常使用具名定義「方法（函式）」。

#### 具名匯入（單一匯入）
```javascript=
import { fn } from "./fnModule.js";
fn(); // 我是小花
```
* 使用解構匯入模組。
* 建議的匯入方法。

#### 具名匯入（全部匯入）
```javascript=
import * as allFn from "./fnModule.js";
console.log(allFn); // 會將 fnModule.js 所有方法全部匯入
allFn.fn(); // 調用其中的模組
```
* `import * as 自定義名稱 from ... ;`
* 不建議的匯入方法，錯誤較難以察覺（要逐一調查是哪個模組出錯）。

#### SideEffect
```javascript=
// 匯出
(function (global) {
  global.$ = '我是 jQuery';
})(window);

// 匯入
import "./sideEffect.js"
console.log($);
```
* `sideEffect.js` 的目的是在被匯入時**立刻執行程式碼**，因此通常不會 `export`，而是立即函式或正在執行的傳統函式。
* 是早期函式庫的匯入方法，例如 jQuery，目前較少使用。