跳到主要內容

FE Day 16 專案結構與程式碼組織

FE Day 16 專案結構與程式碼組織

執行需求:CPU 可跑。今天是「前端開發實戰:React 與 Next.js 全套」系列的第十六篇。前十五天你學會了元件、狀態、Hook、樣式、測試,這些東西散落在幾個檔案時還能跑;專案長大到幾十個元件、數百個檔案時,沒有結構的程式會變得「找檔案比寫程式還累」。今天把後端工程師熟悉的「分層」思惟搬到 React 上,建立一套可以撐到數百個檔案也穩定的資料夾結構,並把路徑別名、Lint 規則、共用型別、Barrel Export 的取捨一次講清楚。整篇的範例都在本機 CPU 跑得起來,不依賴任何雲端服務。

引言

寫後端的工程師對「分層」一點都不陌生:route 層、service 層、repository 層、model 層,每層的職責清楚,跨層呼叫的方向固定,CI 跑測試時可以一層一層驗證。React 專案長大之後也需要類似的分層,新手最常把所有東西塞進 src/components/,結果一個資料夾裡同時混著 Button、useBooking、formatDate、api.ts,找檔案變成「捲動加猜測」,新成員 onboarding 變成考古。

本系列從 Day 5 開始的範例都是「小到能放在一個檔案」的程度,結構問題不明顯。但預約管理系統從 Day 31 開始要長到數十個元件、上百個檔案,如果現在不把骨架蓋好,後面每一天都會被「這段程式該放哪」這個問題打斷。今天的目標是把一個小型 React 專案重構成「以角色為主」的分層結構,讓檔案位置能反映程式碼的職責,並說明怎麼用 Lint 把不一致擋在 PR 階段。

版本基準延續前幾天:Node.js 22 LTS/24 LTS、TypeScript 5.9、React 19.x、Vite 7、Vitest 3.x、ESLint 9 flat config;明天 Day 17 才會切到 Next.js 15/16 的 App Router,所以今天的結構以「純 React 加 Vite」為主,但分層觀念完全通用,搬到 Next.js 也直接適用。讀完之後你應該能在 30 秒內決定任何一段新程式碼該放哪個資料夾,並且能用 Lint 把違反結構的程式碼擋下。

為什麼「全部塞進 components」會爆炸

新手最常見的起手式是「在 src/components/ 底下建一個資料夾裝所有東西」:UI 元件、Hook、工具函式、API 呼叫、TypeScript 型別全部混在一起。一個 components/ 資料夾裡同時出現 useBooking.ts、formatDate.ts、api.ts 是很常見的狀況,git log 看下去每次 commit 都會動到這個資料夾的三四個檔案,PR review 也難以聚焦。

這個結構在前兩週不會出問題,檔案少、邏輯短、IDE 的全域搜尋找得到。但長大到 50 個檔案以上時,三個症狀會同時出現。第一是「找檔案變難」:要改日期格式,得猜它是 components/formatDate.ts、components/utils/formatDate.ts、還是 lib/format.ts。第二是「互相依賴」(circular dependency):UI 元件需要某個型別,型別又需要某個 Hook,Hook 又用到某個工具函式,TypeScript 編譯器抱怨「circular reference」、ESLint 跳出紅字。第三是「測試不好切」:想單獨測 formatDate 卻發現它跟 fetch 寫在同一個檔案,必須 mock 一堆不相干的東西。

對照後端經驗,這就像把 controller、service、repository、model 全塞進 controllers/——能跑,但每次改一個地方都得擔心會不會打到別人。後端用「資料夾等於層」來切,React 也一樣。最直接的分法是「以程式碼的角色為單位」:元件、Hook、工具函式、型別、API 各自有家。下面幾個小節會把這個結構的每一層拆開來看。

以角色為主的資料夾分層

React 專案最常見、也最不容易後悔的分層方式是「按程式碼的角色切資料夾」。一個可以撐到數百個檔案的最小結構長這樣:

src/
├── app/                  # 應用程式入口(Vite main.tsx、root 元件、Provider)
├── components/           # 純 UI 元件:Button、Card、Modal、Field
├── features/             # 以業務功能切:booking/、customer/、report/
├── hooks/                # 共用 Hook:useToggle、useDebounce、useFetch
├── lib/                  # 純函式工具:formatDate、validatePhone、currency
├── api/                  # 與後端溝通:booking.ts、customer.ts、client.ts
├── types/                # 共用型別:Booking、Customer、Role
├── styles/               # 全域樣式:tailwind.css、reset.css、tokens.css
└── test/                 # 測試輔助:render.tsx、msw handlers、fixtures

每個資料夾的角色都很單純:components/ 放可以被多個頁面共用的純展示元件;features/ 放「業務功能」相關的程式碼(一個 feature 內部可以有自己的元件、Hook、型別,全部 co-locate);hooks/ 放跨功能共用的狀態邏輯;lib/ 放沒有 React 依賴的純函式;api/ 集中所有對後端的 HTTP 呼叫;types/ 放跨功能共用的 TypeScript 型別。這個對應關係很直覺,新成員只要花半天就能掌握。

「features/ 內部可以有自己的元件」這條很重要。它解決了「元件太多、全部塞 components/ 又太雜」的矛盾:純展示的 Button、Field 放 components/;跟「預約」業務邏輯綁定的 BookingCard、BookingForm 放 features/booking/components/。這個分法在 Day 13「元件設計原則」提過,叫 feature-sliced design,能讓大型專案在維持秩序的同時允許業務獨立演進。

另一個關鍵字是「co-locate(同位置)」:一個 feature 用到的元件、Hook、型別、測試盡量放在一起。例如 features/booking/ 底下可能長這樣:

features/booking/
├── components/
│   ├── BookingCard.tsx
│   └── BookingForm.tsx
├── hooks/
│   └── useBookingList.ts
├── types.ts
├── api.ts            # 跟 booking 相關的 API 呼叫
└── index.ts          # barrel export:對外只暴露需要的 API

這跟後端的「bounded context(限定上下文)」很像:booking 這個業務範圍有自己的所有東西,不會跟 customer 互相依賴。要新增跟預約有關的小工具,往 features/booking/ 找位置就對了;不需要的東西不會被誤 import。這條對「隨著專案長大、import 路徑變得亂七八糟」的問題是治本的方法。

完整實作:把扁平專案重構為標準分層

實際動手做一次最有感。我們從「全部塞 components」的扁平專案開始,把結構重整到標準分層。先看「重構前」的狀態:

src/
├── App.tsx
├── main.tsx
└── components/
    ├── Button.tsx
    ├── Card.tsx
    ├── BookingCard.tsx       # booking 業務元件
    ├── BookingForm.tsx       # booking 業務元件
    ├── useBookingList.ts     # booking 相關 Hook
    ├── formatDate.ts         # 純函式工具
    ├── validatePhone.ts      # 純函式工具
    ├── api.ts                # 所有後端呼叫混在一起
    ├── booking.ts            # Booking 型別
    └── customer.ts           # Customer 型別

這個結構的問題:components/ 裡混著 UI 元件、Hook、工具函式、API 檔;型別散落在各處;找「怎麼打預約 API」得開三個檔案。我們重構到標準分層:

src/
├── app/
│   ├── main.tsx
│   ├── App.tsx
│   └── providers.tsx
├── components/
│   ├── Button.tsx
│   └── Card.tsx
├── features/
│   └── booking/
│       ├── components/
│       │   ├── BookingCard.tsx
│       │   └── BookingForm.tsx
│       ├── hooks/
│       │   └── useBookingList.ts
│       ├── api.ts
│       └── types.ts
├── hooks/
│   └── useDebounce.ts        # 跨功能共用
├── lib/
│   ├── formatDate.ts
│   └── validatePhone.ts
├── api/
│   └── client.ts             # 共用 HTTP client
├── types/
│   └── customer.ts           # 跨功能共用
└── styles/
    └── tailwind.css

幾個重構決策值得記下來。第一,UI 元件只有「通用」與「業務」兩種,後者搬到 features/booking/components/。第二,Hook 拆成兩層:跨功能共用的(如 useDebounce)放 hooks/;跟單一業務相關的(如 useBookingList)放在 feature 內部。第三,api/ 只放「跨功能共用的 HTTP client」(例如 axios 實例、攔截器),每個 feature 自己的 API 呼叫放在 features/<name>/api.ts,呼叫時用共用的 client。第四,types/ 只放跨功能共用的型別;feature 自己的型別放在 feature 內部。

重構完之後,新成員 onboard 只要被告知「找 UI 元件看 components/,找業務邏輯看 features/booking/」,整個專案的形狀就清楚了。Code review 也比較容易:「這個 PR 改了 features/booking/api.ts」比「這個 PR 改了 components/api.ts」更直覺,重構風險一眼看得出來。

為了讓重構更具體,我們看一個典型的「預約 Hook」應該長什麼樣:

// src/features/booking/hooks/useBookingList.ts
// 業務專屬 Hook:放在 feature 內部,不對外暴露
import { useEffect, useState } from "react";
import { listBookings } from "../api";
import type { Booking, BookingFilter } from "../types";

type State = {
  bookings: Booking[];
  loading: boolean;
  error: Error | null;
};

export function useBookingList(filter: BookingFilter): State {
  const [bookings, setBookings] = useState<Booking[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    listBookings(filter)
      .then((data) => {
        if (!cancelled) setBookings(data);
      })
      .catch((e: unknown) => {
        if (e instanceof Error) setError(e);
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [filter]);

  return { bookings, loading, error };
}

這支 Hook 引用同 feature 內的 api.ts 與 types.ts,對外只暴露 useBookingList。把它放在 features/booking/hooks/ 而不是 hooks/,是因為它的存在完全依賴 booking 業務;booking 不做了,這個 Hook 也不該被任何地方引用。

路徑別名、Lint 規則與 Barrel Export 的取捨

結構蓋好之後,import 路徑會開始變長。從 src/features/booking/components/BookingCard.tsx 引用 src/lib/formatDate.ts 要寫 ../../../lib/formatDate,三個點的相對路徑對眼睛負擔很大。修法是用 TypeScript 的 paths 設定路徑別名,並讓 Vite 7 自動讀取:

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2023", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "jsx": "react-jsx",
    "strict": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@/components/*": ["src/components/*"],
      "@/features/*": ["src/features/*"],
      "@/hooks/*": ["src/hooks/*"],
      "@/lib/*": ["src/lib/*"],
      "@/api/*": ["src/api/*"],
      "@/types/*": ["src/types/*"]
    }
  },
  "include": ["src"]
}

設好之後,整個專案都寫 import { Button } from "@/components/Button"、import { formatDate } from "@/lib/formatDate"。Vite 7 會自動讀 tsconfig.json 的 paths,不需要另外寫 vite.config.ts 的 alias(除非你想自訂 alias 跟 tsconfig 不一樣)。記得裝 @types/node,tsc --noEmit 與 vitest 都能正確解析。

「Barrel Export(在 index.ts 集中 re-export)」是另一個常見手法。一個 feature 的入口通常長這樣:

// src/features/booking/index.ts
// Barrel Export:對外契約集中點
export { BookingCard } from "./components/BookingCard";
export { BookingForm } from "./components/BookingForm";
export { useBookingList } from "./hooks/useBookingList";
export type { Booking, BookingStatus, BookingFilter } from "./types";
export { listBookings, createBooking } from "./api";

這樣外面只要寫 import { BookingCard, useBookingList } from "@/features/booking",不必管內部結構。Barrel Export 的好處是「內部結構可以重構、對外 API 穩定」;壞處是「所有東西都被綁進同一個 chunk,可能拖慢初次載入」。在純 React 加 Vite 專案影響不大;到 Next.js 15 的 App Router 就要特別小心,Server Component 的 import 路徑會被追蹤,不小心把 client 邏輯拉進 server bundle 就糟了(Day 17 會展開)。

Lint 規則的部分,ESLint 9 flat config 推薦裝 eslint-plugin-import 並啟用「import 順序」與「禁止互相依賴」。下面是實際可跑的設定檔:

// eslint.config.js
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import importPlugin from "eslint-plugin-import";

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    files: ["src/**/*.{ts,tsx}"],
    plugins: { import: importPlugin },
    rules: {
      "import/order": [
        "warn",
        {
          groups: [
            "builtin",
            "external",
            "internal",
            ["parent", "sibling", "index"],
          ],
          pathGroups: [{ pattern: "@/**", group: "internal" }],
          "newlines-between": "always",
          alphabetize: { order: "asc", caseInsensitive: true },
        },
      ],
      "import/no-cycle": ["error", { maxDepth: 5 }],
      "import/no-self-import": "error",
      "import/no-unresolved": "error",
    },
  },
];

這份設定做了三件事:第一,import/order 把內建、外部、@/、相對路徑分群,CI 跑 lint 時自動排整齊;第二,import/no-cycle 抓到互相依賴(追 5 層以內),這在前述分層沒做好時最容易出現;第三,import/no-self-import 禁止「自己 import 自己」、import/no-unresolved 確保路徑寫錯會立刻被擋下。裝好這幾條規則後,PR review 就不必再花時間討論「這行 import 該放哪」。

工具函式與共用工廠應該放在 lib/。舉一個「預約系統格式化時間」的範例:

// src/lib/formatDate.ts
// 純函式工具:沒有 React、沒有 fetch、沒有 DOM
const TAIWAN_TIME_FORMAT = new Intl.DateTimeFormat("zh-Hant-TW", {
  year: "numeric",
  month: "2-digit",
  day: "2-digit",
  hour: "2-digit",
  minute: "2-digit",
  hour12: false,
});

export function formatBookingTime(iso: string): string {
  const date = new Date(iso);
  if (Number.isNaN(date.getTime())) return "—";
  return TAIWAN_TIME_FORMAT.format(date);
}

export function isPastBooking(iso: string, now: Date = new Date()): boolean {
  return new Date(iso).getTime() < now.getTime();
}

這支檔案沒任何 import、沒 React、沒 fetch,可以單獨被 Vitest 測試、可以被 Server Component 引用(明天就會用到)、也可以被 Client Component 引用。「lib/ 只能放純函式」這個約定守住,整個專案的工具程式就能維持單純。

常見錯誤與踩雷

第一個雷是「把 features/<name>/components/ 跟 components/ 混著用」。很多人最後會在 components/ 看到 BookingCard,又在 features/booking/components/ 看到 BookingCard,結果兩個版本各自演進。修法是寫進 README:「通用元件放 components/、業務元件放 feature 內」,code review 時嚴格執行,必要時開一個 ESLint 自訂規則擋下違規。一個簡單的做法是在 eslint.config.js 加 no-restricted-imports:

// eslint.config.js(追加規則)
{
  files: ["src/features/booking/components/**/*.tsx"],
  rules: {
    "no-restricted-imports": [
      "error",
      {
        patterns: [
          {
            group: ["@/components/*"],
            message: "booking 元件請放在 src/features/booking/components/",
          },
        ],
      },
    ],
  },
}

這條規則只對 booking feature 內的元件檔生效:禁止從 @/components/ 引入,避免誤把通用元件跟業務元件混用。對其他 feature 可以複製貼上、再改 pattern 與 message,整套 Lint 規則就能隨著團隊約定一起長大。

第二個雷是「lib/ 變成垃圾場」。lib/ 一開始放 formatDate、validatePhone,後來有人把 api.ts、useToggle.ts、colors.ts 全塞進去,原本的「純函式工具」定位完全崩壞。修法是約定 lib/ 只能放「無 React、無 fetch、無 DOM」的純函式;違反這條的 PR 一律退件,並寫成 README 守則。另一個保險做法是在 lib/ 底下加一個 README.md,明文列出允許與禁止的內容。

第三個雷是「Barrel Export 把整個 feature 拉進來」。例如 import "@/features/booking" 想拿 BookingCard,但這個 index.ts 也 re-export 了其他重型元件,結果 bundle 把它們全部載入。修法是 Barrel Export 只 re-export「對外契約」,內部 helper 不要放進去;或改用具名 import import { BookingCard } from "@/features/booking/components/BookingCard",配合 ESLint 的 no-restricted-imports 規則擋下「從 index 拉整包」。

第四個雷是「路徑別名跟 IDE 對不上」。裝好 paths 之後,VS Code 的 TypeScript Language Server 有時候沒抓到,要重啟或刪掉 .vscode/.cache 才會生效。修法是在 tsconfig.json 裡多加 "plugins": [{ "name": "@typescript-eslint" }],或在 VS Code 設定裡指定 "typescript.tsdk": "./node_modules/typescript/lib"。裝好之後,「跳到定義」與「自動 import」才會用上路徑別名。

效能與實務提醒

結構對執行效能的直接影響很小,主要影響的是「bundle 的 code splitting(程式碼分割)」與「tree shaking(搖樹最佳化)」。以 Vite 7 為例,它依賴 ES Module 的靜態分析來做搖樹;如果 Barrel Export 把一堆東西 re-export 出去,靜態分析可能誤判「有引用」而把整個模組打包。要達到最佳搖樹,每個 import 必須用具名、不能 import * as、不能從 index 拉一整包。

路徑別名設定完成後,可以用 tsc --noEmit 與 vitest run 兩個指令確認兩件事:第一,TypeScript 能正確解析 @/ 開頭的路徑(沒有「Cannot find module」錯誤);第二,Vitest 在 jsdom 環境下能載入元件測試、跑 @testing-library/react 的 render 與 screen 工具。這兩個指令應該在 CI 的兩個獨立 step 跑,先 typecheck、再跑測試,能抓出「型別正確但 runtime 失敗」與「runtime 通過但型別錯誤」兩種不同性質的問題。

實務上另一個常見痛點是「co-located 測試」的位置。features/booking/components/BookingCard.tsx 旁邊到底要不要放 BookingCard.test.tsx?兩派各有擁護者。我自己的偏好是「小元件 co-locate、大元件集中」:小於 100 行的元件把測試放旁邊方便找,超過的集中到 __tests__/ 子資料夾;Vitest 3.x 對兩種位置都能正確解析,並支援 vitest.config.ts 的 test.include 設定:

// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import path from "node:path";

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "src"),
    },
  },
  test: {
    environment: "jsdom",
    include: [
      "src/**/*.{test,spec}.{ts,tsx}",
      "src/**/__tests__/**/*.{ts,tsx}",
    ],
    coverage: {
      provider: "v8",
      reporter: ["text", "html"],
      include: ["src/**/*.{ts,tsx}"],
      exclude: ["src/**/*.{test,spec}.{ts,tsx}", "src/test/**"],
    },
  },
});

這份設定把兩種測試位置(co-locate 與 __tests__/)都納入掃描,Vite 解析 @/ 路徑別名,jsdom 提供瀏覽器環境;coverage 用 v8 provider 跑 text 與 html 兩種報表,測試本體排除在覆蓋率統計之外。

最後是「命名一致性」。資料夾用單數還是複數?components/(複數)與 feature/(單數)混用是新團隊最常見的小摩擦。建議在 README 寫明:放東西的資料夾用複數(components/、hooks/、features/),表示「這裡裝了一組同類的東西」;業務範圍的資料夾用單數(booking/、customer/),表示「這是一個獨立的概念」。這個小約定能讓資深開發者看一眼就知道新檔案該放哪。

另一個常被忽略的細節是「package.json 的 scripts 區塊」。專案結構蓋好後,pnpm dev、pnpm build、pnpm test、pnpm typecheck、pnpm lint 五個指令應該在 scripts 裡齊全。新成員 clone 專案只要看 README 跑 pnpm install 與 pnpm dev 就能動起來,不必另外問人「怎麼跑測試」。CI 端則把同樣這五個指令排成獨立 step,依序執行、任一失敗就中斷 build。

預約管理系統從 Day 31 開始會大量引用今天定的這套結構:features/booking/ 裝預約相關的所有程式、features/customer/ 裝顧客相關的;明天進入 App Router 後,src/app/ 只放路由檔、共用元件依然在 src/components/、feature 內部的元件會跟著對應的 route 一起長大。

小結

今天我們把 React 專案的結構問題攤開來看。「以角色為主」的分層——app/、components/、features/、hooks/、lib/、api/、types/、styles/、test/——可以讓數百個檔案的專案維持秩序;feature 內部用 co-locate 結構保持內聚;路徑別名讓 import 路徑簡潔;Barrel Export 與 Lint 規則把「跨檔案」的不一致擋在 CI。我們也看到一個業務專屬 Hook、一個純函式工具、ESLint 9 flat config 的真實寫法,分別示範「feature 內部」、「lib/」、「eslint.config.js」三種角色的程式該長什麼樣。

對後端工程師來說,這套分層最直接的對應是:features/<name>/ 像是一個 bounded context、api/ 像 service 層、lib/ 像 util 層、components/ 像 view 層。一旦你接受這個比喻,把後端熟悉的依賴方向(controller → service → repository)套到 React 上就會非常自然,React 元件不該直接呼叫 api.ts、應該透過 Hook 或 props 串接;Hook 才是「React 版的 controller」,負責組裝狀態與副作用。這條原則在大型專案特別重要,少了它,前端很容易長出「每個元件都自己抓資料」的怪結構。

結語

明天,我們會正式進入「前端開發實戰」系列的下一個里程碑——Next.js 15 起步。我們會用 create-next-app 建立一個 App Router 專案,把 layout.tsx、page.tsx、loading.tsx、error.tsx 四個約定檔都寫成完整範本,搞懂 Server Component 與 Client Component 的邊界,並讓你看到這套檔案約定怎麼跟我們今天蓋好的分層結構共存。Day 17 結束時,你會有一個能跑、能加新頁面、能擴充巢狀路由的最小 Next.js 專案。

延伸資源

  • React 官方「Thinking in React」結構建議:https://react.dev/learn/thinking-in-react
  • Feature-Sliced Design 官方方法論(前端分層設計):https://feature-sliced.design/
  • TypeScript 官方 paths 設定說明:https://www.typescriptlang.org/tsconfig#paths
  • Vite 7 官方 resolve.alias 與 tsconfig 相容性:https://vite.dev/config/shared-options.html#resolve-alias
  • ESLint 9 flat config 官方範例:https://eslint.org/docs/latest/use/configure/configuration-files

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...