跳到主要內容

FE Day 30 監控與錯誤追蹤

FE Day 30 監控與錯誤追蹤

執行需求:需外部服務帳號。今天是「前端開發實戰:React 與 Next.js 全套」系列的第三十天,我們把視角從「怎麼寫程式」轉到「怎麼知道程式在真實環境跑得怎樣」。昨天我們把 Next.js 部署上線,今天要把「使用者實際碰到什麼問題」這條線補齊。我們會用 Sentry JavaScript SDK(2026 年 3 月主流的版本是 8.x 與 9.x)做錯誤追蹤、用 Performance Monitoring 量效能、用 Source Map 還原壓縮後的堆疊;同時也會示範「沒有外部服務帳號」時的本地替代方案:React 的錯誤邊界、console 增強、本地日誌檔案。讀完之後你會拿到兩條並行的路徑,有 Sentry 帳號就走雲端版、沒有就用本地版,兩者介面接近,之後想搬家也只需要換一個 hook。

引言

前二十九天我們幾乎都在討論「怎麼寫」,但軟體上線之後真正困難的是「怎麼知道它在真實使用者手上跑得怎樣」。一支部署到 Vercel 的 Next.js 應用,可能在百分之九十九的請求都沒事,但剩下的百分之一可能就是某個使用者的舊版瀏覽器在某個特殊時區出了例外;如果沒有監控,你只會在客服信箱收到「我按按鈕沒反應」的抱怨,沒辦法重現、沒辦法定位、也沒辦法量化影響範圍。監控的角色就是「把這些看不見的失敗變成可觀察、可分析、可修補的事件」。

今天介紹的 Sentry 是 2026 年 3 月前端錯誤追蹤的主流選擇。它的 SDK(JavaScript SDK 8.x、9.x 世代)在 React 與 Next.js 都有官方支援,可以自動抓到未處理的例外、unhandled promise rejection、React 元件渲染錯誤;Performance Monitoring 可以量到 LCP(最大內容繪製)、INP(互動到下次繪製)、FID;Session Replay 可以把使用者操作錄成影片重播;Source Map 上傳讓壓縮後的堆疊能對應回原始碼行數。今天我們聚焦在「錯誤追蹤+效能監控+Source Map」三個核心,Session Replay 因為要付費方案才完整,今天先不展開。

沒有 Sentry 帳號也沒關係。今天我們也會實作一個本地的監控層:window.onerror、window.onunhandledrejection、React 的錯誤邊界(error boundary)、把錯誤資訊打到 console 與 localStorage。這層監控雖然沒有雲端儀表板,但對開發與除錯已經非常有用;之後真的要接 Sentry,只要把收集到的錯誤事件多送一份到雲端即可。今天的範例可以單獨跑,不依賴任何外部服務。

原理解念:錯誤、效能、使用者體驗三層訊號

前端監控分成三層訊號,每一層回答的問題不同。第一層是「錯誤訊號」:應用程式拋出未處理的例外,導致畫面破版或功能失效。第二層是「效能訊號」:畫面載入慢、互動卡頓、API 回應慢,使用者還看得到畫面但體驗變差。第三層是「使用者行為訊號」:點了哪個按鈕、停留多久、轉換漏斗在哪一步流失。三層訊號都要收集,但前兩層是工程問題、第三層偏產品分析,今天我們聚焦在前兩層。

Sentry 的設計把這兩層抽象成兩個物件:Sentry.captureException(error) 送錯誤事件、Sentry.startTransaction() 或 React 的 <SentryProfiler> 元件包住效能追蹤。所有事件都有共通的「上下文」(context):誰(user)、在哪(URL)、用什麼瀏覽器(userAgent)、什麼時間(timestamp)、做了什麼(breadcrumb,事件發生前的軌跡)。把這些上下文收集齊,未來除錯時才不會只看到「TypeError」而不知道發生在哪個使用者的哪一步操作。

另一個關鍵觀念是「Source Map」。生產環境的 JavaScript 通常被壓縮(minify)成單行,變數名稱也被改成 a、b、c。沒有 Source Map 還原,看到的錯誤堆疊會指向壓縮檔的第幾行而不是原始碼的語意行,這對除錯幾乎沒幫助。Sentry 在初始化時可以設定「上傳 Source Map 到 Sentry 平台」,部署時 Next.js 透過 @sentry/nextjs 的 withSentryConfig 把 Source Map 自動上傳。這層整合看似簡單,但漏掉會讓整套錯誤追蹤失去意義——你只會知道「有錯」,不知道「錯在哪」。

最後一個觀念是「隱私」。監控收集的上下文包含 email、IP、URL 參數,可能夾帶個資。Sentry 提供 beforeSend hook 讓你在事件送出前做最後過濾,把個資欄位遮罩(mask)掉;本地版本也應該實作同樣的過濾,避免把信用卡號、身分證字號不小心送出去。這條規則在台灣的《個人資料保護法》底下特別重要,後面會示範具體寫法。

完整實作:Sentry 雲端版與本地版雙軌

今天的目標是讓監控可以「切換」:用環境變數 NEXT_PUBLIC_SENTRY_DSN 有沒有值來決定走 Sentry 或本地版本。我們先建立一個抽象層 lib/monitor.ts,把兩種實作藏在同一個介面後面,然後在 React 元件與 Next.js 路由裡呼叫這個介面。這種「介面固定、實作可換」的寫法跟 Day 13 的元件設計原則一致——把它套用到監控層就對了。

第一步,安裝 @sentry/nextjs 與其相依套件。Sentry 官方提供 Next.js 專屬整合:

# 安裝 Sentry Next.js 套件(2026 年 3 月主流版本為 9.x)
pnpm add @sentry/nextjs@^9.0.0
# 註冊 sentry-cli 用來上傳 Source Map(建議裝在 devDependencies)
pnpm add -D @sentry/cli@^2.40.0

裝完之後,我們建立抽象層 lib/monitor.ts:

// lib/monitor.ts
// 把錯誤與效能監控抽象成一個介面,背後可以是 Sentry 或本地實作。
// 2026 年 3 月主流:Sentry JavaScript SDK 9.x,本檔案沿用同一份介面。

export type MonitorContext = {
  user?: { id: string; email?: string; role?: string };
  url?: string;
  extra?: Record<string, unknown>;
};

export interface Monitor {
  init(): void;
  captureException(error: unknown, context?: MonitorContext): void;
  captureMessage(message: string, level?: "info" | "warning" | "error"): void;
  startTransaction(name: string): { finish(): void };
  setUser(user: MonitorContext["user"] | null): void;
}

// 內部常數:依環境變數決定要走哪一個實作
const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN;

class NoopMonitor implements Monitor {
  init(): void {}
  captureException(): void {}
  captureMessage(): void {}
  startTransaction() {
    return { finish(): void {} };
  }
  setUser(): void {}
}

class SentryMonitor implements Monitor {
  private sentry: typeof import("@sentry/nextjs") | null = null;
  async init(): Promise<void> {
    if (this.sentry) return;
    const mod = await import("@sentry/nextjs");
    this.sentry = mod;
    mod.init({
      dsn: SENTRY_DSN,
      tracesSampleRate: 0.1,
      replaysSessionSampleRate: 0,
      beforeSend(event) {
        // 把潛在個資欄位遮罩掉(個資法遵循)
        if (event.request?.cookies) {
          event.request.cookies = "[REDACTED]";
        }
        if (event.user?.email) {
          event.user.email = "[REDACTED]";
        }
        return event;
      },
    });
  }
  captureException(error: unknown, context?: MonitorContext): void {
    this.sentry?.captureException(error, { extra: context?.extra, tags: { url: context?.url ?? "" } });
  }
  captureMessage(message: string, level: "info" | "warning" | "error" = "info"): void {
    this.sentry?.captureMessage(message, level);
  }
  startTransaction(name: string) {
    const tx = this.sentry?.startTransaction({ name });
    return {
      finish(): void {
        tx?.finish();
      },
    };
  }
  setUser(user: MonitorContext["user"] | null): void {
    if (user) {
      this.sentry?.setUser({ id: user.id, email: user.email, role: user.role });
    } else {
      this.sentry?.setUser(null);
    }
  }
}

class LocalMonitor implements Monitor {
  // 把錯誤事件寫進 console 與 localStorage,方便沒有 Sentry 時的除錯
  private buffer: Array<{ type: string; payload: unknown; at: string }> = [];
  init(): void {
    if (typeof window === "undefined") return;
    window.addEventListener("error", (event) => {
      this.captureException(event.error ?? event.message, { url: location.href });
    });
    window.addEventListener("unhandledrejection", (event) => {
      this.captureException(event.reason, { url: location.href });
    });
  }
  captureException(error: unknown, context?: MonitorContext): void {
    const payload = { message: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, context };
    this.push("exception", payload);
    if (typeof console !== "undefined") {
      console.error("[monitor:exception]", payload);
    }
  }
  captureMessage(message: string, level: "info" | "warning" | "error" = "info"): void {
    this.push("message", { message, level });
    const fn = level === "error" ? console.error : level === "warning" ? console.warn : console.info;
    fn("[monitor:message]", message);
  }
  startTransaction(name: string) {
    const startedAt = performance.now();
    return {
      finish(): void {
        const dur = performance.now() - startedAt;
        this.push("transaction", { name, durationMs: Math.round(dur) });
      },
    };
  }
  setUser(user: MonitorContext["user"] | null): void {
    this.push("user", user);
  }
  private push(type: string, payload: unknown): void {
    const entry = { type, payload, at: new Date().toISOString() };
    this.buffer.push(entry);
    if (typeof window !== "undefined") {
      try {
        window.localStorage.setItem("monitor:buffer", JSON.stringify(this.buffer.slice(-50)));
      } catch {
        // localStorage 可能滿了或被關閉,忽略錯誤不影響主流程
      }
    }
  }
}

export const monitor: Monitor = SENTRY_DSN ? new SentryMonitor() : new LocalMonitor();

這份抽象層有三個關鍵設計。第一,用 interface Monitor 把所有監控操作統一,captureException、captureMessage、startTransaction、setUser 四個方法構成最小可用介面。第二,SentryMonitor 用 dynamic import("@sentry/nextjs") 動態載入,避免在沒有 DSN 的環境也被打包進去;這對「一個專案同時有雲端與本地兩種部署」的情境特別重要。第三,LocalMonitor 把事件寫進 localStorage 的 monitor:buffer 鍵,保留最近 50 筆,這樣即使沒有雲端儀表板,開發者也可以在瀏覽器的 Application 分頁打開來看。

本地版的 init() 註冊了兩個全域監聽:window.error 接同步例外、window.unhandledrejection 接 Promise 拒絕。這兩個事件是「使用者拋出但程式沒接住」的保險,沒有它們很多 bug 會默默消失。注意 LocalMonitor 的 captureException 收到 Error 物件時會把 stack 一起存,這樣就算沒有 Source Map 也能看到堆疊的檔名與行數(雖然是壓縮後的行數,但至少知道是哪個檔出問題)。

接下來實作 React 錯誤邊界(error boundary)。React 19.x 提供函式元件版的錯誤邊界 hook,但官方建議還是寫成類別元件。我們寫一個會把錯誤送到 monitor 的邊界:

// components/ErrorBoundary.tsx
"use client";
import { Component, type ReactNode } from "react";
import { monitor } from "@/lib/monitor";

type Props = {
  children: ReactNode;
  fallback?: (error: Error, reset: () => void) => ReactNode;
};

type State = { error: Error | null };

export class ErrorBoundary extends Component<Props, State> {
  state: State = { error: null };

  static getDerivedStateFromError(error: Error): State {
    return { error };
  }

  componentDidCatch(error: Error, info: { componentStack?: string }): void {
    monitor.captureException(error, {
      url: typeof window === "undefined" ? undefined : window.location.href,
      extra: { componentStack: info.componentStack },
    });
  }

  reset = (): void => {
    this.setState({ error: null });
  };

  render(): ReactNode {
    if (this.state.error) {
      if (this.props.fallback) {
        return this.props.fallback(this.state.error, this.reset);
      }
      return (
        <div role="alert" className="rounded border border-red-300 bg-red-50 p-4">
          <h2 className="text-lg font-semibold text-red-700">發生錯誤</h2>
          <p className="mt-2 text-sm text-red-600">{this.state.error.message}</p>
          <button
            type="button"
            onClick={this.reset}
            className="mt-3 rounded bg-red-600 px-3 py-1 text-white"
          >
            重試
          </button>
        </div>
      );
    }
    return this.props.children;
  }
}

這個錯誤邊界有三個重點。getDerivedStateFromError 在渲染階段更新 state,觸發 fallback UI;componentDidCatch 在 commit 階段把錯誤送到 monitor,這兩階段分工是 React 設計的硬規則。fallback 接受函式讓呼叫端決定怎麼顯示錯誤,例如可以做成「重新整理」按鈕、Toast、或整頁錯誤頁。reset 方法把 state 清回 null,讓子樹重新嘗試渲染,這對「網路短暫斷線」這類暫時性錯誤特別有用。

接下來在 Next.js App Router 的根 layout 套上邊界,並且註冊路由層級的錯誤頁:

// app/layout.tsx
import type { ReactNode } from "react";
import { ErrorBoundary } from "@/components/ErrorBoundary";

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="zh-Hant">
      <body>
        <ErrorBoundary>{children}</ErrorBoundary>
      </body>
    </html>
  );
}
// app/global-error.tsx
"use client";
import { useEffect } from "react";
import { monitor } from "@/lib/monitor";

export default function GlobalError({ error, reset }: { error: Error; reset: () => void }) {
  useEffect(() => {
    monitor.captureException(error, { url: typeof window === "undefined" ? undefined : window.location.href });
  }, [error]);
  return (
    <html lang="zh-Hant">
      <body>
        <h1>系統暫時無法回應</h1>
        <button type="button" onClick={reset}>重試</button>
      </body>
    </html>
  );
}

app/layout.tsx 的 ErrorBoundary 包住所有頁面內容,app/global-error.tsx 是 Next.js 對「layout 自己也出錯」這條死角的保險——當錯誤發生在 layout 內部(例如讀取根 metadata 失敗),只有 global-error 才能救。這兩個層級搭配起來,整個應用程式的錯誤覆蓋率就接近百分之百。

Sentry 專屬整合:sentry.client.config.ts

用 @sentry/nextjs 時,Next.js 會期待專案根目錄有兩個設定檔:sentry.client.config.ts 給瀏覽器用、sentry.server.config.ts 給伺服器用。我們把昨天的 monitor.init() 改成自動呼叫它們:

// sentry.client.config.ts
import * as Sentry from "@sentry/nextjs";

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 0.1,
  // 在開發環境不要把錯誤送出去,避免干擾本機除錯
  enabled: process.env.NODE_ENV === "production",
  beforeSend(event) {
    if (event.user?.email) event.user.email = "[REDACTED]";
    return event;
  },
});
// sentry.server.config.ts
import * as Sentry from "@sentry/nextjs";

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 0.05,
  enabled: process.env.NODE_ENV === "production",
});

接著調整 next.config.js 讓 Next.js 在 build 時自動上傳 Source Map:

// next.config.js
import { withSentryConfig } from "@sentry/nextjs";

const baseConfig = {
  // 你原本的 Next.js 設定
  reactStrictMode: true,
};

export default withSentryConfig(baseConfig, {
  org: process.env.SENTRY_ORG,
  project: process.env.SENTRY_PROJECT,
  authToken: process.env.SENTRY_AUTH_TOKEN,
  silent: !process.env.CI,
  hideSourceMaps: true,
});

withSentryConfig 會在 build 結束後呼叫 sentry-cli,把 .next/static/chunks 裡的 Source Map 上傳到 Sentry 平台。實務上 SENTRY_AUTH_TOKEN 必須在 CI 或本機的環境變數裡設定;Vercel 用 Project Settings → Environment Variables 設定,build log 會印出上傳結果。這層整合漏掉的話,錯誤追蹤雖然能收到事件,但堆疊會指向壓縮檔,看不出來原始碼行。

效能監控:Web Vitals 與 transaction

錯誤追蹤之外,效能監控是另一個獨立但相關的需求。Google 在 2026 年 3 月把 LCP(最大內容繪製)、INP(互動到下次繪製)、CLS(版面配置位移)列為 Core Web Vitals。Next.js 15 提供 useReportWebVitals hook 把這些指標送出去,我們把它接到 monitor:

// app/layout.tsx(節錄)
"use client";
import { useReportWebVitals } from "next/web-vitals";
import { monitor } from "@/lib/monitor";

export function WebVitalsReporter() {
  useReportWebVitals((metric) => {
    monitor.captureMessage(`web-vital:${metric.name}=${metric.value}`, "info");
  });
  return null;
}

這段把每個 Web Vital 指標變成一則訊息送到 monitor。本地版本會寫進 console;Sentry 版本會被 Performance 收集,可以在 Sentry 儀表板用「瀏覽器效能分布」圖表看到 P75 的 LCP、INP 數值。對前端團隊來說,這比看 Lighthouse 報表更即時——Lighthouse 是本機模擬、Sentry 是真實使用者。

沒有外部服務時的替代流程

如果不想註冊 Sentry 或還在評估要不要付費,前面寫的 LocalMonitor 就足夠開發與 staging 環境使用。我們寫一支小工具把 localStorage.monitor:buffer 的事件匯出成 JSON 檔,方便貼進 issue 或寄給同事:

# 本地開發時,把 monitor 事件印到終端機
pnpm dev
# 在瀏覽器 DevTools console 執行:
# JSON.parse(localStorage.getItem("monitor:buffer") || "[]")
# 觀察最近 50 筆錯誤與效能事件

對沒有 Sentry 帳號的讀者,這條路徑可以讓你在不依賴外部服務的前提下,把監控當作「開發紀律」先做起來。當流量長大、需要跨使用者的彙總報表時再升級到 Sentry;介面與程式碼完全不變,只差一個環境變數的設定。

常見錯誤與踩雷

第一個踩雷是「把開發環境的錯誤也送出去」。很多團隊在 sentry.client.config.ts 漏設 enabled: process.env.NODE_ENV === "production",結果本地按 F5 重整瀏覽器也會把一堆 TypeError 送到 Sentry,污染正式環境的資料、也讓自己的 quota 提早用完。務必用 enabled 把開發環境關掉,或者用 Sentry 的 environment 區分 dev / staging / prod。

第二個踩雷是「Source Map 沒上傳」。很多團隊以為接好 SDK 就結束,結果第一次發生錯誤,看到的堆疊是「main.bundle.js:1:1234」這種沒有意義的位置。其實只要在 next.config.js 套上 withSentryConfig,Source Map 就會自動上傳,否則要手動用 sentry-cli 跑一次 sourcemaps upload。Vercel 部署時如果 build log 沒看到 Uploading source maps 那行,幾乎可以確定沒有上傳成功。

第三個踩雷是「個資外洩」。Sentry 預設會把錯誤訊息與堆疊完整送出去,但錯誤訊息裡可能夾帶信用卡號、表單輸入的 email、URL 裡的 token。務必在 beforeSend hook 過濾敏感欄位,並對表單輸入做額外的 mask;本地版本也應該實作相同的過濾,否則開發者打開 localStorage 就會看到完整個資。在台灣的《個人資料保護法》底下這條特別重要,曾有團隊因為 Sentry 事件被搜尋引擎索引而收到罰款。

第四個踩雷是「把所有元件都包進錯誤邊界」。邊界不是越多越好,包太多會讓錯誤被吞掉、看不到原本的錯誤介面。建議策略是「每個有意義的功能區塊一個邊界、整頁一個根邊界」。當一個邊界接住錯誤並顯示 fallback UI 時,其他區塊仍然正常運作——這就是分區隔離的價值;但邊界太多則會把真正的問題埋在巢狀 fallback 後面,反而難除錯。

第五個踩雷是「效能監控 sample rate 設太高」。Sentry 的 transaction 是有成本的,每一個 transaction 都要付費或佔 quota。預設的 tracesSampleRate: 0.1 是合理值(10% 取樣);如果你的流量很高,可以進一步降到 0.01 或 0.05,並在重要的頁面(例如結帳頁)用 tracesSampler 函式把 sample rate 拉到 1.0,兼顧成本與重要頁面的資料完整度。

效能與實務提醒

Sentry 的 bundle size 約 30 KB gzipped,這對大多數 Next.js 應用不會有明顯影響,但如果你很在意 Core Web Vitals,可以用 dynamic import 把 SDK 延遲到互動後再載入:const Sentry = await import("@sentry/nextjs")。這種「延遲載入監控」的模式犧牲一點錯誤捕捉率(使用者離開太快的事件會漏掉),換取較好的 LCP 與 INP。對內部工具來說不必這樣做;對電商或內容網站則可以考慮。

另一個實務提醒是「錯誤事件需要去重」。同一個 bug 可能在短時間內被上千個使用者觸發,如果每個事件都送到 Sentry,quota 一下就用完。Sentry 預設會做事件去重(fingerprint),但對於「同一個 API 短暫壞掉、造成 500 個失敗請求」這種情況,可以用 beforeSend 回傳 null 直接丟棄,或用 Sentry.withScope 自訂 fingerprint 把同一個原因的事件合併。

對於沒有 Sentry 帳號又想看效能趨勢的團隊,本地版本可以擴充成「上傳到自己的後端」:把 LocalMonitor.buffer 用 navigator.sendBeacon 批次送到自家 API,再把資料存進 Postgres、用 Grafana 畫圖表。這條路徑工作量較大,但完全不需要外部服務、也可以掌握所有資料。對 side project 或內部系統來說,這是個經濟實惠的選擇。

最後一個提醒:monitor.captureException 不應該在 render 階段呼叫,只能在事件處理、useEffect、或錯誤邊界的 componentDidCatch 呼叫。原因很直接:render 階段是 React 的純函式階段,不該有 side effect(送網路請求);如果 render 階段自己出錯、又被送出去,會形成「錯誤送出 → 觸發新錯誤 → 再送出」的無止盡迴圈。本地版本目前用 try/catch 包住 console 呼叫,所以即使在 render 階段誤用也不會炸,但生產環境仍應遵循這個慣例。

小結

今天把前端監控的兩個核心——錯誤追蹤與效能監控——用 Sentry 與本地雙軌實作完成。lib/monitor.ts 抽象出統一介面,ErrorBoundary 接住 React 元件錯誤,global-error.tsx 接住 layout 死角的錯誤,useReportWebVitals 把 Core Web Vitals 串到監控管線。withSentryConfig 把 Source Map 上傳自動化,beforeSend 把個資遮罩寫進流程。沒有外部服務帳號時,LocalMonitor 把事件寫進 localStorage,可以在 DevTools 看到。明天 Day 31 開始進入貫穿專案「預約管理系統」:我們要定義前端的設計、路由、與 Web 系列 FastAPI 後端的串接契約,把前三十天學到的 React、Next.js、Tailwind、TypeScript 全部集合起來。

結語

監控不是「上線以後再加」,而是「寫第一支元件時就要設計」。今天把抽象層做好,未來要換工具、改策略、補欄位都不用改業務程式碼。明天,我們會正式開工貫穿專案:定義路由結構、設計系統的視覺語彙、與 Web 系列 Day 35–44 定義的 API 契約對齊,把這個預約管理系統的前端骨架先畫出來。Day 32 會在這個骨架上實際建立 Next.js 專案、Day 33 開始寫清單頁、Day 34 寫預約流程,一路到 Day 45 結束。今天寫的 monitor 介面會被後續 14 天的所有元件用到,是整個專案篇的基礎建設之一。

延伸資源

  • Sentry Next.js 官方整合指南(2026 年 3 月,9.x 世代):https://docs.sentry.io/platforms/javascript/guides/nextjs/
  • React 19 錯誤邊界官方說明:https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary
  • Next.js 15 Web Vitals 與 useReportWebVitals:https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals
  • Core Web Vitals 官方指標定義(LCP、INP、CLS):https://web.dev/vitals/
  • 個資法遵循與 Sentry 事件過濾實務:https://docs.sentry.io/platforms/javascript/configuration/filtering/

留言

這個網誌中的熱門文章

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 中,資料型別決定我們可以對變數進行哪些操作...

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 建構深度學習模型。 開發者與研究人員 :想更深入了...

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 等工具能處理和分析龐...