---
# System prepended metadata

title: React 前端環境相關
tags: [環境相關]

---

---
title: React 前端環境相關
tags: [環境相關]
---

# React 前端環境相關

> 📅 **最後更新：2025-12-23**
>
> **2025 年重要更新：**
>
> - ✅ **TailwindCSS v4**（2025 年 1 月發布）- 安裝流程大幅簡化，使用 `@tailwindcss/vite` 插件
> - ✅ **React Router v7** - 新版路由系統，改用 `@react-router/dev`
> - ✅ **ESLint Flat Config** - 現為標準配置方式
> - ✅ 所有套件版本已驗證為最新

# 🧰 專案初始化與安裝流程

## 1️⃣ 建立環境（系統框架與開發基礎）

📦 定義：為了讓整個專案能「順利執行、順利開發」的底層工具

### 🪜 建立 React + Vite 專案

```bash
npm create vite@latest
```

接下來依照指示輸入：
✔ Project name: » vite-project（← 你想要的專案名稱）
✔ Select a framework: » React
✔ Select a variant: » JavaScript

### 💿 接著進入資料夾 vite-project （← 你想要的專案名稱）並安裝

```bash
cd vite-project
npm install
```

### 📍 React 工具函式庫 與 Vite 插件行為檢查工具

```bash
npm i ahooks
npm i -D vite-plugin-inspect
```

📌 用途說明

- `ahooks`：給 React 組件內用的 Hooks 工具函式庫
  - 常見像 useRequest、useLocalStorageState、useDebounce 等實用 hooks，可以幫你快速處理非同步請求、本地儲存、防抖等功能。
- `vite-plugin-inspect`：前端開發用的 Vite 插件

### 📍 初始化 Node 專案（如果需要）

```bash
npm init --yes
npm i -D nodemon
```

### 📍 安裝路由

> 無換頁就不需要

```bash
npm i react-router
npm i -D @react-router/dev
```

**特點：**

- 統一的套件名稱 `react-router`（取代 react-router-dom）
- 使用 `@react-router/dev` 作為開發工具
- 支援 Vite 整合（透過 `@react-router/dev/vite`）
- 新的路由配置方式（`app/routes.ts`）

### 📌 如果沒有的話需手動建立的檔案（.gitignore）

#### 📝 建立檔案.gitignore

> 內容為：要設定讓 git 忽略的檔案

檔名

```
.gitignore
```

內容（最少）

```
.env
node_modules
dist
dump/*
!dump/.gitkeep
```

### 📍 安裝開發用工具（語法檢查、自動重啟）

> 檢查語法是否符合規則（eslint）
> 檔案變動時自動重啟伺服器（開發用）

```bash
npm init @eslint/config@latest
```

```bash
npm i -D eslint prettier eslint-plugin-prettier eslint-config-prettier eslint-plugin-react eslint-plugin-react-hooks
```

#### 📝 eslint.config.js 內容

##### js 版

檔名

```
eslint.config.js
```

檔案內容（JS 版）

```js
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";

export default [
  { ignores: ["dist"] },
  {
    files: ["**/*.{js,jsx}"],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parserOptions: {
        ecmaVersion: "latest",
        ecmaFeatures: { jsx: true },
        sourceType: "module",
      },
    },
    plugins: {
      "react-hooks": reactHooks,
      "react-refresh": reactRefresh,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      "react-refresh/only-export-components": [
        "warn",
        { allowConstantExport: true },
      ],
      "no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
      "prettier/prettier": [
        "warn",
        { printWidth: 100, semi: false, singleQuote: true },
      ],
    },
  },
  js.configs.recommended,
  eslintPluginPrettierRecommended,
];
```

##### ts 版

檔名

```
eslint.config.ts
```

檔案內容（TS 版）

```js
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
import { defineConfig } from "eslint/config";

export default defineConfig(
  js.configs.recommended,
  tseslint.configs.recommended,
  {
    files: ["**/*.{js,mjs,cjs,ts}"],
    languageOptions: {
      globals: globals.node,
    },
  },
  {
    files: ["**/*.ts"],
    rules: {
      "no-unused-vars": "off",
      "@typescript-eslint/no-unused-vars": [
        "warn",
        { argsIgnorePattern: "^_" },
      ],
    },
  },
  eslintPluginPrettierRecommended,
);
```

#### 📝 .prettierrc.json 內容

檔名

```
.prettierrc.json
```

檔案內容

```json
{
  "$schema": "https://json.schemastores.org/prettierrc",
  "printWidth": 100,
  "semi": false,
  "singleQuote": true
}
```

---

> 選擇想要的 UI 框架，ex: [Material-UI](https://mui.com/material-ui/getting-started/installation/) 套件、[Ant Design](https://ant.design/docs/react/introduce) 套件、[Chakra UI](https://chakra-ui.com/getting-started) 套件

---

### 🪜 安裝 UI 框架

#### Material-UI (MUI)

```bash
npm install @mui/material @emotion/react @emotion/styled
npm install @mui/icons-material
```

##### 設定 Material-UI

參考官方文件：https://mui.com/material-ui/getting-started/

#### Ant Design

```bash
npm install antd
```

##### 設定 Ant Design

參考官方文件：https://ant.design/docs/react/introduce-cn

#### Chakra UI

```bash
npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion
```

##### 設定 Chakra UI

參考官方文件：https://chakra-ui.com/

#### shadcn/ui

```bash
npx shadcn@latest init
```

##### 設定 shadcn/ui

參考官方文件：https://ui.shadcn.com/docs/installation/vite

**注意事項：**

- 需要 TypeScript 專案（shadcn/ui 主要支援 TypeScript）
- 會自動設定 `tsconfig.json` 和 `tsconfig.app.json` 的 path aliases
- 建議先安裝 TailwindCSS v4，再初始化 shadcn/ui

---

### 🕶️ TailwindCSS 相關

#### 只想安裝 TailwindCSS ，不安裝其他 UI 框架

```bash
npm install -D tailwindcss @tailwindcss/vite
```

#### Tailwind 的設定

{%preview https://hackmd.io/@63Ywhax5TLKtGhFSWnacvQ/SJMNh1Oqxe %}
{%preview https://tailwindcss.com/docs/guides/vite %}

##### 在 src/index.css 匯入 Tailwind

```css
@import "tailwindcss";
```

##### 檢查 main.jsx 中是否有匯入這個 css 檔案

```jsx
import "./index.css";
```

---

## 2️⃣ 常駐工具型功能（必裝輔助開發用，不是主流程但超常出現）

📦 定義：專案未必一開始就用到，但絕大多數專案後期一定會補，甚至常常預設就先裝。

### 狀態管理

> 二選一即可

使用 Zustand：

> 輕量級狀態管理，新手或中小型專案，建議使用

```bash
npm i zustand
```

或使用 Redux Toolkit：

> Redux 官方推薦的狀態管理

```bash
npm i @reduxjs/toolkit react-redux
```

### 狀態儲存、 HTTP 請求、表單驗證

> 無論選擇哪種狀態管理工具，以下套件都建議安裝

```bash
npm i axios react-hook-form yup @hookform/resolvers
```

### 開發用套件

自動引入 import 的工具、路徑別名

```bash
npm i -D @vitejs/plugin-react vite-tsconfig-paths
```

自動化路由

```bash
npm i @generouted/react-router react-router-dom
```

##### 若為 TS 專案

建議在 `package.json` 的 `script` 區塊新增：

```
"type-check": "tsc --noEmit"
```

```
"scripts": {
  "dev": "vite",
  "build": "vite build",
  "lint": "eslint .",
  "preview": "vite preview",
  "type-check": "tsc --noEmit"
}
```

### React 專案推薦資料夾結構（Generouted 自動路由版）

> **💡 使用 Generouted 檔案系統路由**，檔案結構即路由配置

```
src/
├── assets/              # 靜態資源（圖片、字體、icons）
│   ├── images/
│   ├── fonts/
│   └── icons/
│
├── components/          # 可重用的 UI 元件
│   ├── common/         # 通用元件（Button、Input、Modal）
│   ├── layout/         # 版型相關（Header、Footer、Sidebar）
│   └── features/       # 功能專屬元件
│
├── pages/              # ⭐ 檔案系統路由（自動生成路由）
│   ├── _app.jsx        # 全站布局（包裹所有頁面）
│   ├── _404.jsx        # 404 頁面
│   ├── index.jsx       # 首頁 → /
│   │
│   ├── about.jsx       # 關於頁面 → /about
│   │
│   ├── products/
│   │   ├── index.jsx                 → /products
│   │   ├── [id].jsx                  → /products/:id
│   │   └── components/               # 頁面專屬元件
│   │       ├── ProductFilter.jsx
│   │       └── ProductGrid.jsx
│   │
│   ├── (auth)/         # 認證相關群組（不影響 URL）
│   │   ├── _layout.jsx    # 認證布局
│   │   ├── +login.jsx     → /login (Modal)
│   │   └── +register.jsx  → /register (Modal)
│   │
│   └── dashboard/      # 儀表板
│       ├── _layout.jsx    # 儀表板專屬布局
│       ├── index.jsx      → /dashboard
│       ├── profile.jsx    → /dashboard/profile
│       └── settings.jsx   → /dashboard/settings
│
├── hooks/              # 自定義 React Hooks
│   ├── useAuth.js
│   ├── useLocalStorage.js
│   └── useFetch.js
│
├── stores/              # 狀態管理（Zustand / Redux）
│   ├── slices/         # Redux Toolkit slices（如使用 Redux）
│   └── useUserStore.js # Zustand stores
│
├── services/           # API 呼叫相關
│   ├── api.js          # Axios 實例設定
│   ├── authService.js  # 認證相關 API
│   └── userService.js  # 使用者相關 API
│
├── utils/              # 工具函式（純 JS，無 React 依賴）
│   ├── formatters.js   # 格式化函式
│   ├── validators.js   # 驗證函式
│   └── helpers.js      # 其他輔助函式
│
├── constants/          # 常數定義
│   ├── apiEndpoints.js # API 端點
│   └── config.js       # 設定檔
│
├── types/              # TypeScript 型別定義（如使用 TS）
│   ├── user.ts
│   └── product.ts
│
├── styles/             # 全域樣式
│   ├── globals.css     # 全域 CSS
│   ├── variables.css   # CSS 變數
│   └── mixins.scss     # SCSS mixins（如使用 SCSS）
│
├── router.ts           # ✨ Generouted 自動生成（勿手動編輯）
├── main.jsx            # 應用程式入口
└── index.css           # 主樣式檔
```

> **💡 提示**：`router.ts` 即使在純 JavaScript 專案中也會是 `.ts` 檔案，這是 Generouted 的設計，無需改為 `.js`

**🔄 與傳統路由的差異：**

| 項目               | 傳統手動路由          | Generouted 自動路由      |
| ------------------ | --------------------- | ------------------------ |
| `router/` 資料夾   | ✅ 需要手動建立       | ❌ 不需要                |
| `layouts/` 資料夾  | ✅ 存放布局元件       | ❌ 改用 `pages/_app.jsx` |
| `constants/routes` | ✅ 手動定義路由常數   | ❌ 自動生成型別安全路由  |
| `App.jsx`          | ✅ 根元件             | ❌ 改用 `pages/_app.jsx` |
| `main.jsx`         | 使用 RouterProvider   | 使用 Generouted Routes   |
| 路由配置           | 手動在 `router/` 定義 | 檔案結構即路由           |
| 布局元件           | 在 `layouts/` 資料夾  | 在 `pages/_app.jsx` 等   |

---

#### 在 /src 需手動建立的檔案建議（結構）

##### 新增 components 資料夾

**用途**：可重用元件（推薦按功能分類）

```
components
```

**範例**：

```
components/
├── common/              # 通用 UI 元件
│   ├── Button/
│   │   ├── Button.jsx
│   │   ├── Button.module.css
│   │   └── index.js
│   ├── Input/
│   ├── Modal/
│   └── Card/
│
├── layout/              # 版型相關元件
│   ├── Header.jsx
│   ├── Footer.jsx
│   ├── Sidebar.jsx
│   └── Navbar.jsx
│
└── features/            # 功能專屬元件
    ├── ProductCard/     # 商品卡片
    ├── UserProfile/     # 使用者資料
    └── CartItem/        # 購物車項目
```

---

##### 新增 pages 資料夾

**用途**：頁面元件（Generouted 檔案系統路由）

```
pages
```

###### 規則說明：Generouted 檔案命名

| 檔案名稱       | 路由     | 說明                   |
| -------------- | -------- | ---------------------- |
| `index.jsx`    | `/`      | 該層級的預設頁面       |
| `about.jsx`    | `/about` | 固定路由頁面           |
| `[id].jsx`     | `/:id`   | 動態路由參數           |
| `[...all].jsx` | `/*`     | 通配符路由             |
| `_app.jsx`     | -        | 全站布局               |
| `_layout.jsx`  | -        | 區域布局               |
| `(group)/`     | -        | 路徑群組（不影響 URL） |
| `+modal.jsx`   | `/modal` | Modal 路由             |
| `_ignored.jsx` | -        | 忽略的檔案             |

詳細的檔案命名規則說明請參考

{%preview https://hackmd.io/@63Ywhax5TLKtGhFSWnacvQ/rk309Luxbx %}

---

##### 新增 `pages/_app.jsx` 檔案，用於全站布局

**用途**：全站布局元件 檔案（使用 Generouted）

**`pages/_app.jsx` - 全站布局：**

同 Vue `layouts/default.vue`

> **🔔 重要提醒**：使用 Generouted 時，所有路由相關的功能都應從 `@/router` 導入（而非 `react-router-dom`），以獲得完整的型別安全檢查。

```
pages/_app.jsx
```

```jsx
// pages/_app.jsx
import { Outlet, Link } from "@/router";

export default function App() {
  return (
    <div className="min-h-screen flex flex-col">
      {/* Header */}
      <header className="bg-white shadow-sm border-b">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
          <div className="flex justify-between items-center h-16">
            {/* Logo */}
            <Link to="/" className="text-xl font-bold text-gray-900">
              MyApp
            </Link>

            {/* Navigation */}
            <nav className="flex gap-6">
              <Link to="/" className="text-gray-700 hover:text-gray-900">
                首頁
              </Link>
              <Link to="/about" className="text-gray-700 hover:text-gray-900">
                關於
              </Link>
              <Link
                to="/products"
                className="text-gray-700 hover:text-gray-900"
              >
                商品
              </Link>
            </nav>
          </div>
        </div>
      </header>

      {/* Main Content */}
      <main className="flex-1">
        <Outlet /> {/* 各頁面內容渲染位置 */}
      </main>

      {/* Footer */}
      <footer className="bg-gray-50 border-t">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
          <p className="text-center text-gray-600 text-sm">
            © {new Date().getFullYear()} MyApp. All rights reserved.
          </p>
        </div>
      </footer>
    </div>
  );
}
```

---

##### 新增 hooks 資料夾

**用途**：自定義 React Hooks，封裝常用邏輯

```
hooks
```

**Zustand 版本：**

```js
// hooks/useAuth.js
import { useState, useEffect } from "react";
import useUserStore from "@/stores/useUserStore";

export function useAuth() {
  const { user, isLoggedIn, login, logout } = useUserStore();
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // 檢查登入狀態
    setLoading(false);
  }, []);

  return { user, isLoggedIn, login, logout, loading };
}
```

**Redux Toolkit 版本：**

```js
// hooks/useAuth.js
import { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import {
  login as loginAction,
  logout as logoutAction,
} from "@/stores/slices/userSlice";

export function useAuth() {
  const dispatch = useDispatch();
  const { user, isLoggedIn } = useSelector((state) => state.user);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // 檢查登入狀態
    setLoading(false);
  }, []);

  const login = (userData, token) => {
    dispatch(loginAction({ user: userData, token }));
  };

  const logout = () => {
    dispatch(logoutAction());
  };

  return { user, isLoggedIn, login, logout, loading };
}
```

```js
// hooks/useLocalStorage.js
import { useState, useEffect } from "react";

export function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const saved = localStorage.getItem(key);
    return saved ? JSON.parse(saved) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}
```

---

##### 新增 stores 資料夾

**用途**：狀態管理（相對於 Vue 的 Pinia）

```
stores
```

###### Zustand 版本

適合：新手或中小型專案，輕量級狀態管理

```js
// stores/useUserStore.js
import { create } from "zustand";
import { persist } from "zustand/middleware";

const useUserStore = create(
  persist(
    (set) => ({
      user: null,
      token: "",
      isLoggedIn: false,

      login: (userData, token) => {
        set({ user: userData, token, isLoggedIn: true });
      },

      logout: () => {
        set({ user: null, token: "", isLoggedIn: false });
        localStorage.removeItem("token");
      },

      updateUser: (userData) => {
        set({ user: userData });
      },

      updateToken: (newToken) => {
        set({ token: newToken });
      },
    }),
    {
      name: "user-storage",
      partialize: (state) => ({ token: state.token, user: state.user }),
    }
  )
);

export default useUserStore;
```

###### Redux Toolkit 版本

適合：大型專案或需要複雜狀態管理

```
stores/
├── index.js           # Store 設定
└── slices/
    ├── userSlice.js
    └── cartSlice.js
```

```js
// stores/slices/userSlice.js
import { createSlice } from "@reduxjs/toolkit";

const userSlice = createSlice({
  name: "user",
  initialState: {
    user: null,
    token: "",
    isLoggedIn: false,
  },
  reducers: {
    login: (state, action) => {
      state.user = action.payload.user;
      state.token = action.payload.token;
      state.isLoggedIn = true;
    },
    logout: (state) => {
      state.user = null;
      state.token = "";
      state.isLoggedIn = false;
      localStorage.removeItem("token");
    },
    updateToken: (state, action) => {
      state.token = action.payload;
    },
    updateUser: (state, action) => {
      state.user = action.payload;
    },
  },
});

export const { login, logout, updateToken, updateUser } = userSlice.actions;
export default userSlice.reducer;
```

**基礎版本（無持久化）：**

```js
// stores/index.js
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./slices/userSlice";

const store = configureStore({
  reducer: {
    user: userReducer,
    // 其他 reducers 可以加在這裡
    // cart: cartReducer,
  },
});

export default store;
```

**進階版本（使用 redux-persist 持久化）：**

> **💡 提示**：需要先安裝 `npm install redux-persist`，才能使用持久化功能

```js
// stores/index.js
import { configureStore } from "@reduxjs/toolkit";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage"; // 使用 localStorage
import userReducer from "./slices/userSlice";

// 持久化配置
const persistConfig = {
  key: "root",
  storage,
  whitelist: ["user"], // 只持久化 user，其他 state 不持久化
  // blacklist: ['temporaryData'], // 或用黑名單排除不需要持久化的
};

// 包裹 reducer
const persistedReducer = persistReducer(persistConfig, userReducer);

const store = configureStore({
  reducer: {
    user: persistedReducer,
    // 其他 reducers 可以加在這裡
    // cart: cartReducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        // 忽略 redux-persist 的 action
        ignoredActions: ["persist/PERSIST", "persist/REHYDRATE"],
      },
    }),
});

export const persistor = persistStore(store);
export default store;
```

**使用持久化時的 main.jsx：**

```jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import store, { persistor } from "./stores";
import "./index.css";
import App from "./App.jsx";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>
  </StrictMode>
);
```

---

##### 新增 services 資料夾

**用途**：API 呼叫

```
services
```

```
services/
└── api.js # Axios 實例設定 + 串後端基礎建設
```

**Zustand 版本：**

```js
// src/services/api.js
import axios from "axios";
import useUserStore from "@/stores/useUserStore";

// 無須登入即可使用（接後端）
const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || "http://localhost:4000",
});

// 須登入才能使用（接後端）
const apiAuth = axios.create({
  baseURL: import.meta.env.VITE_API_URL || "http://localhost:4000",
});

// 每次發送請求前自動加上 token
apiAuth.interceptors.request.use((config) => {
  const { token } = useUserStore.getState();
  config.headers.Authorization = `Bearer ${token}`;
  return config;
});

// token 相關 成功處理與失敗處理
apiAuth.interceptors.response.use(
  (res) => res,
  async (error) => {
    if (
      error.response &&
      error.response.status === 400 &&
      error.response.data.message === "token 已過期" &&
      error.config.url !== "/user/refresh"
    ) {
      try {
        // 直接使用 apiAuth instance 發送請求，避免循環相依
        const { data } = await apiAuth.patch("/user/refresh");

        // 使用 store 的 updateToken action 更新 token
        useUserStore.getState().updateToken(data.token);

        error.config.headers.Authorization = `Bearer ${data.token}`;

        return axios(error.config);
      } catch {
        // 刷新失敗則登出
        useUserStore.getState().logout();
      }
    }
    throw error;
  }
);

export default { api, apiAuth };
```

**Redux Toolkit 版本：**

```js
// src/services/api.js
import axios from "axios";
import store from "@/stores"; // 需要導入 store
import { updateToken, logout } from "@/stores/slices/userSlice";

// 無須登入即可使用（接後端）
const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || "http://localhost:4000",
});

// 須登入才能使用（接後端）
const apiAuth = axios.create({
  baseURL: import.meta.env.VITE_API_URL || "http://localhost:4000",
});

// 每次發送請求前自動加上 token
apiAuth.interceptors.request.use((config) => {
  const { token } = store.getState().user;
  config.headers.Authorization = `Bearer ${token}`;
  return config;
});

// token 相關 成功處理與失敗處理
apiAuth.interceptors.response.use(
  (res) => res,
  async (error) => {
    if (
      error.response &&
      error.response.status === 400 &&
      error.response.data.message === "token 已過期" &&
      error.config.url !== "/user/refresh"
    ) {
      try {
        // 直接使用 apiAuth instance 發送請求，避免循環相依
        const { data } = await apiAuth.patch("/user/refresh");

        // 使用 store 的 dispatch 更新 token
        store.dispatch(updateToken(data.token));

        error.config.headers.Authorization = `Bearer ${data.token}`;

        return axios(error.config);
      } catch {
        // 刷新失敗則登出
        store.dispatch(logout());
      }
    }
    throw error;
  }
);

export default { api, apiAuth };
```

---

##### 新增 utils 資料夾

**用途**：工具函式

```
utils
```

```jsx
// utils/validators.js
export const isEmail = (email) => {
  const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return re.test(email);
};

export const isPhone = (phone) => {
  const re = /^09\d{8}$/;
  return re.test(phone);
};
```

---

##### 新增 constants 資料夾

**用途**：常數定義

```
constants
```

> **⚠️ 注意**：使用 Generouted 後不需要 `constants/routes.js`，改用型別安全的路由

---

##### 路由導航（使用 Generouted）

**使用型別安全的路由導航：**

> 要在匯入什麼檔案匯入？

> 1. 先確定你已經安裝並設定好 Generouted。
> 2. 確保你的專案結構符合 Generouted 的要求，並且已經生成了 router.ts 檔案。
> 3. 在你的元件中，從生成的 router.ts 檔案中匯入 Link 和 useNavigate。

```jsx
// 從 Generouted 自動生成的 router.ts 匯入
import { Link, useNavigate } from "@/router";
```

例如：

```jsx
// 從 Generouted 自動生成的 router.ts 匯入
import { Link, useNavigate } from "@/router";

export default function Navigation() {
  const navigate = useNavigate();

  return (
    <nav>
      {/* ✅ 型別安全，TypeScript 會檢查路由是否存在 */}
      <Link to="/">首頁</Link>
      <Link to="/about">關於</Link>
      <Link to="/products">商品列表</Link>
      <Link to="/products/:id" params={{ id: "123" }}>
        商品詳情
      </Link>

      {/* 程式化導航 */}
      <button onClick={() => navigate("/dashboard")}>前往儀表板</button>

      {/* ❌ 如果路由不存在，TypeScript 會報錯 */}
      {/* <Link to="/not-exist">不存在</Link> */}
    </nav>
  );
}
```

**動態路由參數：**

```jsx
import { useParams } from "@/router";

export default function ProductDetail() {
  const { id } = useParams();

  return <h1>商品 ID: {id}</h1>;
}
```

---

### 日期處理

```bash
npm i dayjs
```

或

```bash
npm i date-fns
```

### Icon 額外擴充

{%preview https://hackmd.io/@63Ywhax5TLKtGhFSWnacvQ/SkLmkfOlWe %}

---

## 3️⃣ 明確功能型功能（安裝你需要用的功能套件，依需求增減）

📦 定義：為了「特定功能」才會加的套件，專案不同會差很多。

作業要用到 gsap、swiper、animejs

```bash
npm install gsap swiper animejs
```

| 套件名       | 分類       | 用途                 | 特點                          | 適合情境                         |
| ------------ | ---------- | -------------------- | ----------------------------- | -------------------------------- |
| **GSAP**     | 明確功能型 | 強大動畫函式庫       | 功能完整、控制精細、效能佳    | 頁面轉場、滾動動畫、複雜動畫流程 |
| **Swiper**   | 明確功能型 | 幻燈片輪播工具       | 手機觸控友善、支援 React 元件 | Banner、圖片輪播、步驟流程切換   |
| **Anime.js** | 明確功能型 | 動畫函式庫（較輕巧） | 語法簡潔、適合小動畫          | 元素浮動、icon 動畫、數字增減    |

> **Swiper** 有 React 版，所以：
> 📌 使用 Swiper 時需使用 `swiper/react` 元件，並引入樣式 `swiper/css`

```jsx
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
```

---

## 需增加的檔案內容

安裝完之後要思考：

1. vite.config 是否需掛載
2. main.jsx（或 .tsx）是否需要匯入

### vite-project/vite.config.js

```js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import generouted from "@generouted/react-router/plugin";
import { fileURLToPath, URL } from "node:url";

export default defineConfig({
  plugins: [
    react({
      // React 19 的 Fast Refresh 自動啟用
      // 只在需要自定義 Babel 時配置
    }),
    generouted(),
    tailwindcss(),
  ],

  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
      "@components": fileURLToPath(
        new URL("./src/components", import.meta.url)
      ),
      "@pages": fileURLToPath(new URL("./src/pages", import.meta.url)),
      "@hooks": fileURLToPath(new URL("./src/hooks", import.meta.url)),
      "@services": fileURLToPath(new URL("./src/services", import.meta.url)),
      "@stores": fileURLToPath(new URL("./src/stores", import.meta.url)),
      "@assets": fileURLToPath(new URL("./src/assets", import.meta.url)),
    },
  },

  server: {
    port: 3000,
    host: true,
    open: true,
    cors: true,
    // API 代理配置 (根據後端調整)
    proxy: {
      "/api": {
        target: "http://localhost:4000",
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ""),
      },
    },
  },

  build: {
    chunkSizeWarningLimit: 1000,
    reportCompressedSize: false,
    rollupOptions: {
      output: {
        manualChunks(id) {
          // 更智能的代碼分割策略
          if (id.includes("node_modules")) {
            // React 核心
            if (
              id.includes("react") ||
              id.includes("react-dom") ||
              id.includes("react-router")
            ) {
              return "react-vendor";
            }
            // Redux 狀態管理
            if (id.includes("redux") || id.includes("@reduxjs")) {
              return "redux-vendor";
            }
            // 表單處理
            if (
              id.includes("react-hook-form") ||
              id.includes("yup") ||
              id.includes("@hookform")
            ) {
              return "form-vendor";
            }
            // 動畫庫 (只在實際使用時才分割)
            if (
              id.includes("gsap") ||
              id.includes("animejs") ||
              id.includes("swiper")
            ) {
              return "animation-vendor";
            }
            // 工具庫
            if (
              id.includes("axios") ||
              id.includes("dayjs") ||
              id.includes("ahooks")
            ) {
              return "utils-vendor";
            }
            // 其他 node_modules
            return "vendor";
          }
        },
        chunkFileNames: "js/[name]-[hash].js",
        entryFileNames: "js/[name]-[hash].js",
        assetFileNames: (assetInfo) => {
          // 更細緻的資源文件分類
          const fileName = assetInfo.names?.[0] || "asset";
          const ext = fileName.split(".").pop();
          if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(ext)) {
            return `images/[name]-[hash].[ext]`;
          } else if (/woff2?|eot|ttf|otf/i.test(ext)) {
            return `fonts/[name]-[hash].[ext]`;
          } else if (ext === "css") {
            return `css/[name]-[hash].[ext]`;
          }
          return `assets/[name]-[hash].[ext]`;
        },
      },
    },
  },

  optimizeDeps: {
    include: [
      "react",
      "react-dom",
      "react-router-dom",
      "@generouted/react-router",
      "@reduxjs/toolkit",
      "react-redux",
      "axios",
      "dayjs",
      "ahooks",
    ],
    exclude: ["@react-router/dev"],
  },

  css: {
    devSourcemap: true,
  },

  preview: {
    host: true,
    open: true,
  },
});
```

> 💡 如果使用 TailwindCSS，請加入 `tailwindcss` 插件：
>
> ```js
> import tailwindcss from "@tailwindcss/vite";
>
> export default defineConfig({
>   plugins: [react(), tailwindcss()],
>   // ... 其他設定
> });
> ```

### vite-project/src/App.jsx

```jsx
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import { routes } from "@generouted/react-router";
import "./App.css";

const router = createBrowserRouter(routes);

function App() {
  return <RouterProvider router={router} />;
}

export default App;
```

### vite-project/src/main.jsx

**基礎版本（無狀態管理）：**

```jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.jsx";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>
);
```

**Redux Toolkit 版本（需配置 Provider）：**

> **⚠️ 重要**：使用 Redux Toolkit 時，**必須**用 `<Provider>` 包裹應用，否則 `useSelector` 和 `useDispatch` 會報錯！

```jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import store from "./stores";
import "./index.css";
import App from "./App.jsx";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>
);
```

### vite-project/.browserslistrc

定義要支援的瀏覽器版本（例如 last 2 versions, > 1%）。
Vite / Babel / PostCSS / Autoprefixer 會依這裡決定要轉譯的語法和加的 CSS 前綴。

```
.browserslistrc
```

```
> 1%
last 2 versions
not dead
not ie 11
```

### vite-project/.editorconfig

統一編輯器的基本格式設定（縮排、換行符號、字元編碼等），確保團隊開發風格一致。

```
.editorconfig
```

```
[*.{js,jsx,ts,jsx}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
```

### vite-project/.env

環境變數設定檔，用於存放 API URL、金鑰等敏感資訊。

> **⚠️ 重要**：
>
> - Vite 專案中，只有以 `VITE_` 開頭的變數才能在前端存取
> - `.env` 檔案應加入 `.gitignore`，避免上傳敏感資訊
> - 使用 `import.meta.env.VITE_變數名稱` 來存取

```
.env
```

**開發環境範例：**

```bash
# API 設定
VITE_API_URL=http://localhost:4000
VITE_API_TIMEOUT=10000

# 功能開關
VITE_ENABLE_MOCK=true
VITE_ENABLE_DEBUG=true

# 第三方服務（範例，實際使用時請替換成真實的）
VITE_GOOGLE_MAPS_API_KEY=your_google_maps_api_key_here
VITE_FIREBASE_API_KEY=your_firebase_api_key_here
VITE_STRIPE_PUBLIC_KEY=your_stripe_public_key_here

# 應用程式設定
VITE_APP_NAME=My React App
VITE_APP_VERSION=1.0.0
```

**生產環境範例（.env.production）：**

```bash
# API 設定
VITE_API_URL=https://api.your-domain.com
VITE_API_TIMEOUT=30000

# 功能開關
VITE_ENABLE_MOCK=false
VITE_ENABLE_DEBUG=false

# 第三方服務
VITE_GOOGLE_MAPS_API_KEY=your_production_google_maps_api_key
VITE_FIREBASE_API_KEY=your_production_firebase_api_key
VITE_STRIPE_PUBLIC_KEY=your_production_stripe_public_key

# 應用程式設定
VITE_APP_NAME=My React App
VITE_APP_VERSION=1.0.0
```

**在程式中使用：**

```js
// services/api.js
const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || "http://localhost:4000",
  timeout: import.meta.env.VITE_API_TIMEOUT || 10000,
});

// 檢查是否為開發模式
if (import.meta.env.DEV) {
  console.log("開發模式");
}

// 檢查是否為生產模式
if (import.meta.env.PROD) {
  console.log("生產模式");
}
```

### vite-project/index.html

Vite 的專案入口 HTML，會載入 JS 入口檔（如 main.jsx 或 main.tsx）

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>vite-project</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>
```

# 各資料夾「建立時機」與啟用條件

| 資料夾 / 功能區 | 建立時機 or 什麼情況需要？                         | 備註                                             |
| --------------- | -------------------------------------------------- | ------------------------------------------------ |
| `assets/`       | 專案一開始就建立，存放靜態資源                     | 圖片、字體、icons、SVG 等                        |
| `components/`   | 專案一開始就建立，存放可重用元件                   | 建議分 `common/`、`layout/`、`features/`         |
| `pages/`        | 專案一開始就建立，對應每個路由頁面                 | 採用 Feature-based 結構，每個頁面一個資料夾      |
| `layouts/`      | 有共用版型需求時（如前台、後台不同框架）           | 配合 `<Outlet />` 使用，至少有 MainLayout        |
| `hooks/`        | 出現重複邏輯時（如 useAuth、useLocalStorage）      | 自定義 React Hooks，保持元件簡潔                 |
| `stores/`       | 有跨元件全域狀態需求時（登入、購物車、主題等）     | Zustand 或 Redux Toolkit，建議從 Zustand 開始    |
| `services/`     | 開始串接 API 時就應建立                            | 每個功能模組一個檔案（authService、userService） |
| `utils/`        | 有通用工具函式需求時（格式化、驗證、計算等）       | 純 JS 函式，無 React 依賴                        |
| `constants/`    | 有固定資料、設定值時（路由路徑、API 端點、選項等） | 方便維護與修改                                   |
| `types/`        | 使用 TypeScript 時                                 | 定義型別、介面                                   |
| `router/`       | 安裝 `react-router-dom` 後                         | 集中管理路由設定與守衛                           |
| `styles/`       | 有全域樣式、CSS 變數需求時                         | 存放 globals.css、variables.css 等               |
| `.env`          | 有環境變數需求時（API URL、金鑰等）                | `VITE_` 開頭的變數可在前端使用                   |
| `.gitignore`    | 專案第一天就該建立                                 | 避免上傳 node_modules、.env、dist 等             |

---

## 📊 React 專案資料夾優先順序

### 🔴 必須建立（專案一開始）

1. `components/` - 元件庫
2. `pages/` - 頁面
3. `assets/` - 靜態資源
4. `.gitignore` - Git 設定
5. `.env` - 環境變數

### 🟡 很快會需要（前期開發）

6. `layouts/` - 版型（有多頁面時）
7. `router/` - 路由設定（使用 react-router-dom）
8. `services/` - API 呼叫（開始串接後端）
9. `hooks/` - 自定義 Hooks（出現重複邏輯）
10. `stores/` - 狀態管理（有全域狀態需求）

### 🟢 依需求建立（中後期）

11. `utils/` - 工具函式（出現通用邏輯）
12. `constants/` - 常數定義（有固定資料）
13. `styles/` - 全域樣式（需統一管理樣式）
14. `types/` - 型別定義（使用 TypeScript）

---

## 💡 React vs Vue 資料夾結構差異

| 項目         | Vue 3 習慣              | React 習慣                                 | 說明                             |
| ------------ | ----------------------- | ------------------------------------------ | -------------------------------- |
| 元件資料夾   | `components/`           | `components/`（分類更細）                  | React 更強調按功能分類           |
| 頁面資料夾   | `views/` 或 `pages/`    | `pages/`（Feature-based）                  | React 常用 Feature-based 結構    |
| Hooks 資料夾 | `composables/`          | `hooks/`                                   | 命名不同，概念相同               |
| 狀態管理     | `stores/` (Pinia)       | `stores/` (Zustand/Redux)                  | React 更多選擇                   |
| API 呼叫     | `services/` 或 `api/`   | `services/` 或 `api/`                      | 概念相同                         |
| 路由設定     | `router/index.js`       | `router/index.jsx`                         | 概念相同，React 用 JSX           |
| 靜態資源     | `assets/`               | `assets/` 或 `public/`                     | React 有 public 資料夾可直接訪問 |
| 版型         | `layouts/`              | `layouts/`                                 | 概念相同，React 用 `<Outlet />`  |
| 元件組織方式 | 較扁平                  | 更傾向 Feature-based（功能導向）           | React 常用資料夾包元件的方式     |
| CSS 處理     | `.vue` 檔案內 `<style>` | CSS Modules / Styled-components / Tailwind | React 較多樣化                   |
| 自動引入     | unplugin 系列           | 較少使用，傾向明確 import                  | React 更強調明確性               |

---

## 🎯 React 開發建議

### 1️⃣ 元件組織建議

```
✅ 推薦：Feature-based（功能導向）
components/
├── common/          # 通用元件
├── layout/          # 版型元件
└── features/        # 功能專屬元件

pages/
├── Home/
│   ├── index.jsx
│   └── components/  # 頁面專屬元件
└── Products/
    ├── index.jsx
    ├── ProductDetail.jsx
    └── components/

❌ 避免：所有元件全部扁平放在 components/
```

### 2️⃣ 命名規範

- 元件檔案：**PascalCase**（`Button.jsx`、`UserProfile.jsx`）
- Hooks 檔案：**camelCase** 以 `use` 開頭（`useAuth.js`、`useLocalStorage.js`）
- 工具函式：**camelCase**（`formatDate.js`、`validators.js`）
- 常數檔案：**camelCase** 或 **UPPER_CASE**（`routes.js`、`API_ENDPOINTS.js`）

### 3️⃣ 資料夾 index 模式

```jsx
// ✅ 推薦：使用 index.js 統一匯出
components/Button/
├── Button.jsx
├── Button.module.css
└── index.js        // export { default } from './Button'

// 使用時
import Button from '@/components/Button'  // 簡潔！

// ❌ 避免：沒有 index.js
import Button from '@/components/Button/Button'  // 重複！
```

### 4️⃣ 狀態管理選擇

- **小型專案**：React Context API + hooks
- **中型專案**：Zustand（推薦新手）
- **大型專案 / 複雜狀態**：Redux Toolkit
- **伺服器狀態**：React Query / SWR

### 5️⃣ 樣式處理建議

- **Tailwind CSS**：最流行，開發快速
- **CSS Modules**：作用域隔離，適合元件化
- **Styled-components**：CSS-in-JS，動態樣式方便
- **Sass/SCSS**：傳統但強大，適合大型專案

# 🧩 前端套件分類表

| 套件名稱               | 安裝位置 | 類型       | 用途說明                  | 代表常見用途     |
| ---------------------- | -------- | ---------- | ------------------------- | ---------------- |
| `react`                | ✅ 前端  | 主框架     | 建立 React App            | 必裝主角         |
| `react-dom`            | ✅ 前端  | 主框架     | React DOM 渲染            | 必裝主角         |
| `vite`                 | ✅ 前端  | 開發工具   | 開發伺服器、熱更新        | 必裝主角         |
| `@vitejs/plugin-react` | ✅ 前端  | 插件       | 支援 `.jsx/.jsx` 組件語法 | Vite 必備        |
| `react-router-dom`     | ✅ 前端  | 路由工具   | 頁面切換、SPA             | 路由系統         |
| `zustand`              | ✅ 前端  | 狀態管理   | 輕量級 state 管理工具     | 全域資料         |
| `@reduxjs/toolkit`     | ✅ 前端  | 狀態管理   | Redux 官方推薦工具        | 全域資料         |
| `axios`                | ✅ 前端  | HTTP 工具  | 前端請求 API              | 抓資料           |
| `react-hook-form`      | ✅ 前端  | 表單驗證   | 表單輸入處理與驗證        | 登入、註冊       |
| `yup` / `zod`          | ✅ 前端  | 表單驗證   | Schema 驗證               | 表單驗證         |
| `@iconify/react`       | ✅ 前端  | Icon       | 通用 icon 套件            | 自定義圖示       |
| `react-icons`          | ✅ 前端  | Icon       | 整合多種 icon 庫          | 自定義圖示       |
| `dayjs` / `date-fns`   | ✅ 前端  | 日期處理   | 處理時間與格式            | 預約、顯示日期   |
| `gsap` / `animejs`     | ✅ 前端  | 動畫       | 控制動畫流程              | 頁面轉場         |
| `swiper`               | ✅ 前端  | 輪播工具   | 輪播圖、滑動效果          | banner、步驟切換 |
| `ahooks`               | ✅ 前端  | 工具函式庫 | 一堆超實用的 React Hooks  | 類似小助手       |
| `vite-plugin-inspect`  | ✅ 前端  | Vite 插件  | 檢查套件掛載情況          | debug 開發流程   |
| `@mui/material`        | ✅ 前端  | UI 框架    | Material Design UI 元件庫 | UI 設計          |
| `antd`                 | ✅ 前端  | UI 框架    | Ant Design UI 元件庫      | UI 設計          |
| `@chakra-ui/react`     | ✅ 前端  | UI 框架    | Chakra UI 元件庫          | UI 設計          |
| `framer-motion`        | ✅ 前端  | 動畫       | React 動畫函式庫          | 頁面動畫         |

---

## React vs Vue 主要差異對照表

| 項目         | Vue 3                       | React                          |
| ------------ | --------------------------- | ------------------------------ |
| 主要檔案     | `App.vue`                   | `App.jsx` / `App.jsx`          |
| 入口檔案     | `main.js`                   | `main.jsx` / `main.jsx`        |
| 路由套件     | `vue-router`                | `react-router-dom`             |
| 狀態管理     | `pinia`                     | `zustand` / `@reduxjs/toolkit` |
| 表單驗證     | `vee-validate` + `yup`      | `react-hook-form` + `yup`      |
| Hooks 工具庫 | `@vueuse/core`              | `ahooks`                       |
| 樣板語法     | `<template>`                | JSX                            |
| 響應式資料   | `ref()` / `reactive()`      | `useState()` / `useReducer()`  |
| 副作用處理   | `watch()` / `watchEffect()` | `useEffect()`                  |
| 組件定義     | `<script setup>`            | `function Component()`         |
| 路由插槽     | `<router-view />`           | `<Outlet />`                   |

---

# 總結

這份文件涵蓋了 React + Vite 專案的完整初始化流程，包括：

1. ✅ **基礎環境建立**（React + Vite + ESLint + Prettier）
2. ✅ **UI 框架選擇**（MUI / Ant Design / Chakra UI / shadcn/ui）
3. ✅ **必備工具安裝**（路由、狀態管理、HTTP 請求、表單驗證）
4. ✅ **專案結構建議**（pages / components / hooks / services / stores）
5. ✅ **設定檔範例**（vite.config.js / eslint.config.js / jsconfig.json）

根據你的專案需求，選擇性安裝對應的套件，建立完善的 React 開發環境！🚀

---

---

## 📚 官方文件連結

- [Vite 官方文件](https://vite.dev/)
- [React 官方文件](https://react.dev/)
- [TailwindCSS v4 文件](https://tailwindcss.com/)
- [React Router v7 文件](https://reactrouter.com/)
- [Zustand 文件](https://zustand.docs.pmnd.rs/)
- [shadcn/ui 文件](https://ui.shadcn.com/)

---

**文件維護說明：**
本文件會定期更新以確保資訊的正確性和時效性。最後檢查日期：2025-12-23
