跳到主要內容

FE Day 29 部署:Vercel 與自架伺服器

FE Day 29 部署:Vercel 與自架伺服器

執行需求:需外部服務帳號。前 28 天我們把開發、測試、品質的工具鏈都走完了,今天進入部署篇的第一篇:怎麼把 Next.js 15 專案部署到正式環境。會介紹兩條路線:第一條是 Vercel(最快、最簡單、Next.js 原生整合、CDN 自動配、preview deploys 內建);第二條是自架伺服器(最低成本、完整控制、適合對資料落地有要求的場景)。兩條路線都用貫穿專案「預約管理系統」當範例,把 Day 32 寫好的 mock 模式正式切換到 live API。本篇需要 Vercel 帳號(免費方案即可)或一台 Linux 伺服器(Hetzner、AWS EC2 等)。

引言

部署是前端開發的終點也是起點:終點是「把程式放到使用者能存取的地方」,起點是「真實環境的變數會逼你重新思考很多開發時沒注意的細節」。對 side project 來說,部署的成本與門檻往往是「能不能讓朋友試用」的關鍵——花三小時搞懂部署比花三十小時寫完美程式更有商業價值。今天會用最少的設定把一個 Next.js 15 專案推到正式環境,並把環境變數、build 設定、CI/CD、CDN、快取策略這些常見的踩雷一次走完。

2026 年 3 月主流的 Next.js 部署選項有四個:Vercel(Next.js 同公司出品、整合最深)、Netlify(介面友善、有免費方案)、Cloudflare Pages(邊緣運算、CDN 全球節點)、自架 Node.js(最彈性、成本最低)。Vercel 對 Next.js 的支援最完整——App Router、Server Components、Server Actions、Image Optimization、Edge Functions 都自動處理;其他平台雖然也支援,但常會在邊緣案例需要手動設定。今天會把 Vercel 當主要示範,自架當對照組。我們的判斷原則:對個人 side project 或新創 MVP,Vercel 是 90% 情境的最佳選擇;只有資料合規、客製化 runtime、成本極端敏感才考慮自架。

今天的目標有五:第一,理解 Next.js 部署的兩個 runtime(Node.js 與 Edge);第二,把專案推到 Vercel 並設定環境變數;第三,把 Day 31 寫的 mock 模式切換到 live API;第四,用 GitHub Actions 跑 CD pipeline;第五,示範自架 Linux 伺服器的部屬流程。讀完之後你應該能依「成本 vs 便利」權衡選最適合自己專案的路線。

Next.js 部署的兩個 Runtime

Next.js 15 支援兩個 runtime:Node.js runtime(傳統、長連線、相容性最好)與 Edge runtime(V8 isolate、低延遲、全球分散)。兩者各有適用場景:

  • Node.js runtime:預設、支援所有 Node.js API(fs、child_process、native modules)、適合需要大量 CPU 的頁面(如 SSR、報表產生)、連線保持時間長。
  • Edge runtime:用 V8 isolate、體積小、啟動快(小於 5ms)、分散在全球 CDN 邊緣節點、適合 API middleware、地區導向的內容、輕量 SSR。限制:不能用 Node.js API、單次執行時間上限 30 秒(付費方案可調高)。

貫穿專案「預約管理系統」主要用 Node.js runtime:預約表單的 Server Actions 需要呼叫 FastAPI 後端(fetch 是標準 API,Edge 也支援),但 admin 後台的複雜查詢(資料表、日期運算)我們用 Node.js。Middleware(地區導向、A/B 測試)會用 Edge。先看路由怎麼指定 runtime:

// app/api/edge-example/route.ts
// Edge runtime:每個請求都在最近的 CDN 節點處理
export const runtime = "edge";

export async function GET(request: Request) {
  // 可以存取 request 與環境變數
  const country = request.headers.get("x-vercel-ip-country") ?? "TW";
  return Response.json({ country, message: "在邊緣節點執行" });
}
// app/api/heavy-query/route.ts
// Node.js runtime:可以用 Buffer、child_process 等 Node API
export const runtime = "nodejs";

import { readFileSync } from "node:fs";

export async function GET() {
  const data = readFileSync("./public/report.csv", "utf-8");
  return new Response(data, {
    headers: { "content-type": "text/csv" },
  });
}

兩個 runtime 都可以讀環境變數(process.env.*),但 Edge runtime 只能用 fetch、Request、Response 與 Web Streams API。選擇的原則:能用 Edge 就 Edge(更快、更便宜),但需要 Node.js 模組或大量 CPU 時退回 Node.js。

部署到 Vercel

第一步,把程式碼推到 GitHub。第二步,在 Vercel 連結 GitHub 並匯入專案。第三步,設定環境變數並部署。整個流程約 10 分鐘,比自架快 100 倍。我們一步步走:

用 vercel.json 設定 build 行為:

// vercel.json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "buildCommand": "pnpm build",
  "installCommand": "pnpm install --frozen-lockfile",
  "framework": "nextjs",
  "regions": ["sin1"],
  "github": {
    "silent": false,
    "autoAlias": true
  },
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "Referrer-Policy",
          "value": "strict-origin-when-cross-origin"
        }
      ]
    }
  ]
}

這份設定做了五件事:指定 build 與 install 指令(用 pnpm);宣告 framework 為 Next.js(讓 Vercel 套用預設最佳化);指定部署到 sin1(新加坡)區域(離台灣最近);設定 GitHub 整合(每個 PR 自動建立 preview deploy);加上常見的安全 header。

第二步,把 Day 31 寫的 mock 模式切換到 live:

// lib/api-mode.ts(在 production 強制走 live)
export type ApiMode = "mock" | "live";

export function getApiMode(): ApiMode {
  const raw = process.env.NEXT_PUBLIC_API_MODE;
  if (process.env.NODE_ENV === "production") {
    return "live";
  }
  return raw === "live" ? "live" : "mock";
}

export const API_MODE = getApiMode();

在 Vercel 的環境變數設定頁(Project → Settings → Environment Variables)設定:

# Vercel 環境變數(Production / Preview / Development 可分別設定)
NEXT_PUBLIC_API_BASE=https://api.booking.example.com
NEXT_PUBLIC_API_MODE=live
DATABASE_URL=postgresql://...
FASTAPI_API_KEY=...
SESSION_SECRET=...

關鍵細節:以 NEXT_PUBLIC_ 開頭的變數會被打包進 client bundle、可以被瀏覽器看到,所以只放公開資訊(如 API base URL);其他敏感變數(API key、session secret)只能 server 端讀取。Vercel 支援三種環境(Production、Preview、Development)各自的變數——通常 Production 用真實 API、Preview 用 staging API、Development 用 mock。

第三步,串接 GitHub repo:

# 本機用 Vercel CLI 部署
pnpm add -g vercel@latest
vercel login
vercel link  # 連結現有專案

# 第一次部署到 preview
vercel

# 部署到 production
vercel --prod

Vercel CLI 的 vercel 命令會讀 vercel.json、執行 build、把結果推到 Vercel 的 CDN。每次 git push 會自動觸發新的 preview deploy(PR 也會有獨立的 preview URL),合併到 main 才會更新 production deploy。

CDN、快取與 ISR

Vercel 的 Next.js 整合最強的功能是 ISR(Incremental Static Regeneration)——靜態頁面在背景重新生成、不需要重新部署就能更新內容。我們的服務清單頁 /services 就可以用 ISR:

// app/services/page.tsx
// ISR:每 60 秒重新生成一次靜態頁面
export const revalidate = 60;

import { fetchServices } from "@/lib/api/services";

export default async function ServicesPage() {
  const services = await fetchServices();
  return (
    <main>
      <h1>預約服務</h1>
      <ul>
        {services.map((s) => (
          <li key={s.id}>
            <a href={`/services/${s.id}`}>{s.name}</a>
          </li>
        ))}
      </ul>
    </main>
  );
}

加上 export const revalidate = 60; 後,這個頁面第一次請求時會 SSR 並 cache 60 秒,60 秒後的下一次請求會觸發「背景重新生成」,使用者仍拿到舊版 cache,下下次請求才拿到新版。對「服務頁面偶爾更新、不需要即時」的內容剛好。

CDN 的部分,Vercel 預設把靜態檔案(/_next/static/*、/public/*)cache 到全球節點,Cache-Control 由 Next.js 自動設定(靜態資源 1 年、HTML 看 revalidate)。如果要更精細控制,可以在 next.config.js 用 headers() 自訂:

// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        source: "/_next/static/:path*",
        headers: [
          { key: "Cache-Control", value: "public, max-age=31536000, immutable" },
        ],
      },
      {
        source: "/api/:path*",
        headers: [{ key: "Cache-Control", value: "no-store" }],
      },
      {
        source: "/(.*)",
        headers: [
          { key: "X-Content-Type-Options", value: "nosniff" },
          { key: "Strict-Transport-Security", value: "max-age=63072000" },
        ],
      },
    ];
  },
};

export default nextConfig;

這個設定做了三件事:靜態資源 cache 一年(用 content hash 確保更新時自動失效);API 路由永遠不 cache;所有頁面加上 HSTS header 強制 HTTPS。

自架:Node.js 與 Docker

對資料落地有要求、或不想被 Vercel 綁定的場景,自架 Node.js 伺服器是合理選擇。我們用 Docker 容器化 + 反向代理 + 系統化服務的方式部署:

先寫 Dockerfile:

# Dockerfile
# 多階段 build:第一階段 build、第二階段只留 runtime
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# 為了安全:用非 root 使用者跑
RUN addgroup --system --gid 1001 nodejs \
  && adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]

Dockerfile 用了「多階段 build」模式:第一階段裝完整依賴並 build,第二階段只保留 standalone 模式的輸出。standalone 模式(output: "standalone" 在 next.config.ts)會把 node_modules 精簡到只剩 runtime 必要的部分,映像從 ~500MB 縮到 ~150MB。也要在 next.config.ts 啟用:

// next.config.ts
const nextConfig: NextConfig = {
  output: "standalone",
  // 其他設定略
};

第二步,用 systemd 管理服務:

# /etc/systemd/system/booking-frontend.service
[Unit]
Description=Booking Frontend (Next.js)
After=network.target

[Service]
Type=simple
User=nextjs
WorkingDirectory=/opt/booking-frontend
EnvironmentFile=/opt/booking-frontend/.env.production
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
# 資源限制
MemoryMax=512M
CPUQuota=80%

[Install]
WantedBy=multi-user.target

這份 systemd unit 做了六件事:After=network.target 等網路啟動後再啟動;EnvironmentFile 把環境變數從檔案讀進來(不放進 image);Restart=always 程式崩潰自動重啟;RestartSec=5 重啟前等 5 秒避免狂失敗;MemoryMax 與 CPUQuota 避免單一容器把機器資源吃光。最後 systemctl enable booking-frontend 開機自動啟動。

第三步,用 Nginx 做反向代理與 HTTPS:

# /etc/nginx/sites-available/booking-frontend
server {
    listen 80;
    server_name booking.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name booking.example.com;

    ssl_certificate /etc/letsencrypt/live/booking.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/booking.example.com/privkey.pem;

    # 靜態資源直接由 Nginx 處理
    location /_next/static/ {
        proxy_cache_valid 200 365d;
        add_header Cache-Control "public, max-age=31536000, immutable";
        try_files $uri =404;
    }

    # 其他路由 proxy 到 Node.js
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

這個 Nginx 設定做了五件事:把 HTTP 強制升級到 HTTPS;/_next/static/ 的靜態資源直接由 Nginx serve 並 cache 一年(這層 cache 比 Node.js 處理快得多);其他路由 proxy 到 127.0.0.1:3000(Node.js);保留 WebSocket 支援(Upgrade header)未來要用到;帶上 client 真實 IP(Node.js 才知道請求來自哪裡)。

用 Let's Encrypt 申請 HTTPS 憑證(完全免費、90 天自動續期):

# 安裝 certbot 並申請憑證
apt install certbot python3-certbot-nginx
certbot --nginx -d booking.example.com

# 確認自動續期
certbot renew --dry-run

CI/CD:GitHub Actions 自動部署

把部署串進 GitHub Actions 可以省下「每次手動 vercel --prod」的力氣,且能在 PR 階段就建立 preview 環境供審視。我們寫一份完整的 workflow:

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - uses: pnpm/action-setup@v4
        with: { version: 10 }
      - run: pnpm install --frozen-lockfile
      - run: pnpm typecheck
      - run: pnpm lint
      - run: pnpm build
      - name: 部署 preview (PR)
        if: github.event_name == 'pull_request'
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--yes'
      - name: 部署 production (main)
        if: github.ref == 'refs/heads/main'
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod --yes'

這份 workflow 做了四件事:先做 typecheck 與 lint 確保程式碼品質;build 驗證能產出正式版本;PR 自動建立 preview deploy(不需手動介入);merge 到 main 才部署 production。Vercel 的 token 在 Vercel 後台建立(Settings → Tokens),org 與 project ID 用 vercel link 連結後會自動寫進 .vercel/project.json,把這兩個值複製到 GitHub Secrets 即可。

常見錯誤與踩雷

第一個踩雷是「環境變數沒分環境」。把 NEXT_PUBLIC_API_BASE 設成 staging 的 URL 推到 production、或反過來,會讓使用者看到「正在維護中」的訊息。對策:Vercel 的 Production / Preview / Development 三個環境分別設定變數;或者用 NEXT_PUBLIC_VERCEL_ENV(Vercel 自動注入)判斷當前環境。

第二個是「build 失敗但 local 跑得起來」。常見原因:用了 Node.js API 但忘了在 client 元件 import(client 端 import 整個 server module 會炸)、環境變數 local 有但 CI 沒設、pnpm 版本不一致。對策:用 pnpm install --frozen-lockfile 確保 lockfile 一致;在 .env.example 列出所有變數;CI 跑的時候用同樣的 Node 與 pnpm 版本。

第三個是「ISR 沒生效」。加了 revalidate 但內容還是舊的?常見原因:fetch 預設有 cache,fetchServices 用 fetch 沒設 cache: "no-store" 也沒給 revalidate。對策:明示 next: { revalidate: 60 } 或用 cache: "no-store"。

第四個是「Edge runtime 不能用 Node.js 模組」。在 Edge runtime 寫 import { readFile } from "node:fs" 會 build 失敗。對策:Edge runtime 只能用 Web API(fetch、Request、Response、URL);需要 Node API 的就退回 runtime = "nodejs"。

第五個是「自架忘記設防火牆」。Docker 容器直接暴露 3000 port 在公網,被掃到就被打。對策:只讓 Nginx 在 80/443 對外,Node.js bind 127.0.0.1;用 ufw 或 iptables 鎖住其他 port。

第六個是「沒有 health check」。Vercel 會自動檢查,但自架的 systemd 服務沒人知道程式是否健康。對策:在 app/api/health/route.ts 寫一個檢查資料庫連線、外部 API 的端點,搭配 curl 與監控(Day 30 會深入)。

Preview Deploy 與分支策略

Vercel 對每個 PR 自動建立 preview deploy 是一個被低估的功能。每個 PR 都有一個獨立的 URL(例如 booking-frontend-git-feature-42.vercel.app),這個 URL 跑的是該 PR 的程式碼、連到的是 Preview 環境的環境變數與資料庫。這讓我們可以在 merge 之前就把 UI 改動分享給設計師或 PM 做視覺驗收,比「merge 之後才發現某個按鈕顏色不對」省下大量來回修改的時間。

實務上建議的分支策略:trunk-based development,主分支只有 main,所有功能開發從 feature branch 開出、PR 進 main。每個 PR 會自動拿到一個 preview URL;合併後 main 的部署會自動升級為 production。我們貫穿專案(Day 31-44)的所有 PR 都會附上 preview URL,Day 40 驗收時也會以 preview URL 為基準,方便驗收人員直接打開連結確認 UI 是否符合設計稿。

如果團隊比較大、需要區分 staging 與 production,可以用 Vercel 的「Multiple Environments」功能:把 develop 分支對應到 staging 環境,main 對應到 production。staging 用獨立的 API 與資料庫,方便在「類正式」環境做整合測試。

// vercel.json(進階:自訂 git branch 對應環境)
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "git": {
    "deploymentEnabled": {
      "main": true,
      "develop": true
    }
  }
}

監控部署後的健康狀態

部署上 production 不等於「完成」。Day 30 會展開監控,但這裡先講最基本的三個指標:第一,部署後 5 分鐘內看 Sentry 有沒有新錯誤(vibe check);第二,看 Vercel Analytics 的 Real User Monitoring(RUM)有沒有異常的 page load 時間;第三,看錯誤率(4xx/5xx response 比例)有沒有暴增。這三個指標可以讓你在「使用者回報壞掉之前」先發現問題。

把這三個指標做成「部署後自動檢查」腳本:

// scripts/post-deploy-check.mjs
// 部署後 5 分鐘的健康檢查:呼叫 Sentry API、Vercel Analytics API、錯誤率
const SITE = process.env.SITE_URL;
const SENTRY_TOKEN = process.env.SENTRY_AUTH_TOKEN;

async function checkErrorRate() {
  const since = new Date(Date.now() - 5 * 60 * 1000).toISOString();
  const res = await fetch(
    `https://sentry.io/api/0/projects/booking-frontend/events/?since=${since}`,
    { headers: { Authorization: `Bearer ${SENTRY_TOKEN}` } },
  );
  const data = await res.json();
  const errorCount = data.length;
  if (errorCount > 50) {
    console.error(`部署後 5 分鐘內有 ${errorCount} 個錯誤,請檢查!`);
    process.exit(1);
  }
  console.log(`部署後 5 分鐘內 ${errorCount} 個錯誤,狀態正常`);
}

await checkErrorRate();

這份腳本可以掛進 GitHub Actions 的 deploy job(if: github.ref == 'refs/heads/main'),部署完成後自動跑一次。Day 30 的監控篇會把這套延伸到更完整的 SLO 指標與 alert 機制。

效能與實務提醒

Vercel 的免費方案(Hobby)對個人 side project 已經夠用:每月 100 GB 頻寬、無限 preview deploys、SSL 憑證自動配置。如果流量大於這個門檻(罕見),再升 Pro(每月 $20、1 TB 頻寬)。自架的話,一台 Hetzner CPX11(4 GB RAM、2 vCPU)每月 €4.5,跑一個中型 Next.js 專案綽綽有餘。

另一個實務提醒是「觀察部署的時間」。Next.js 的 build 時間通常 1-3 分鐘,Vercel 在 build 之後還會做 edge optimization 再部署,整個流程約 3-5 分鐘。如果 build 慢,可以檢查是否啟用了 SWC、是否有用 experimental.serverComponentsExternalPackages 把大依賴(如 Prisma client)排除在 bundle 外。

最後一個提醒是「Preview deploy 的價值」。Vercel 對每個 PR 自動建立獨立的 preview URL(例如 booking-frontend-git-feature-xyz.vercel.app),這個 URL 可以分享給設計師、PM、或客戶做視覺驗收。在 merge 之前就能發現問題,比「merge 後才發現 UI 壞了」省下大量除錯時間。我們貫穿專案(Day 31-44)會充分利用這個機制,每個 PR 都會附上 preview URL。

成本與容錯策略

選 Vercel 或自架不能只看技術,也要看「出事時誰負責」。Vercel 把 CDN、SSL、auto scaling、DDoS 防護都包進去,你只要專心寫程式;但當服務掛掉時你只能開 ticket 等他們修,且每個月的帳單會隨流量成長。自架則相反:所有事情自己處理(從 SSL 憑證到 OS patch),但完全可控、成本固定、可以放敏感資料。

實務上的折衷是「主要在 Vercel、關鍵資料落地台灣或自己的機器」。例如 Next.js 純前端 + 靜態頁面在 Vercel;使用者個資、金流、醫療資料放台灣的雲端或自家機房。這種 hybrid 架構可以兼顧便利與合規,是 side project 邁向商業化的合理路線。

另一個常被忽略的是「備份與還原」。Vercel 的程式碼本身在 GitHub,但 production 環境變數、vercel.json、DNS 設定並沒有版本控制。建議把 vercel.json 進版控、環境變數用 1Password 之類的工具記錄下來、每個 domain 的 DNS 設定要 export 留存。自架則要記得備份 /var/log/、Nginx 設定、SSL 憑證與 .env 檔,否則主機掛了要從零重建會非常痛苦。

最後一個常見問題是「為什麼部署上去第一次開很慢」。Next.js 預設會做 cold start,第一次存取某個頁面時需要做 SSR 與 hydration。Vercel 在 production 環境會把這個頁面 cache 起來,第二次就快了。如果你看到某個頁面一直 cold start,可能是 revalidate 設太短(每次都重新生成)或 cache header 設定錯誤。用 Vercel 的 Speed Insights 看實際指標會比憑感覺準。

小結

今天把部署篇的兩條路線走完。重點回顧:Next.js 15 支援 Node.js 與 Edge 兩個 runtime,依需求選擇;Vercel 提供 Next.js 最佳整合,10 分鐘就能上線,搭配 ISR 與 CDN 自動處理靜態資源;自架可用 Docker + systemd + Nginx 三件套,搭配 Let's Encrypt 拿免費 HTTPS;GitHub Actions 串 CD 後,PR 自動 preview、merge 自動 production。明天 Day 30 我們會進入部署的延伸主題:監控與錯誤追蹤,會講怎麼用 Sentry / OpenTelemetry / Vercel Analytics 看到「真實使用者」的體驗,而不是只看本地 dev server 的 console。

貫穿專案(Day 31-44)會把今天講的所有工具都用上:Vercel preview deploy 給每個 PR 一個獨立的 URL、ISR 給服務頁 60 秒 cache、自架的 systemd + Nginx 三件套在 Day 41 部署實戰會再深入。讀到這裡你應該有完整的部署心智模型,知道什麼時候選 Vercel、什麼時候選自架,並能在 10 分鐘內把一個全新的 Next.js 專案推到 production。

結語

今天的重點是讓部署從「神秘的最後一關」變成「日常的最後一步」。透過 Vercel 的零設定部署、自架的 Docker + Nginx 三件套、GitHub Actions 的 CD 自動化,你應該能在 push 程式碼的 5 分鐘內看到 production 上的成果。讀完這篇你應該能回答:為什麼 Vercel 是 Next.js 的最佳部署平台?Edge runtime 與 Node.js runtime 怎麼選?ISR 是什麼、怎麼設定?自架伺服器最少需要哪些設定?CD pipeline 怎麼寫?

明天,我們會進入部署的延伸主題:監控與錯誤追蹤。會講怎麼用 Sentry 自動收集前端錯誤、用 OpenTelemetry 看真實使用者的 page load 時間、用 Vercel Analytics 看轉換漏斗,並把這些工具整合進 Day 32 寫好的根 layout。實作上需要一個 Sentry 帳號(免費方案即可)與一個 analytics 服務。

延伸資源

選讀順序建議:先把 Next.js 官方部署指南讀完(必讀),再看 Vercel 文件(了解 ISR 與 Edge),最後看 Docker 與 Nginx 設定(自架路線才需要)。Sentry 與監控工具會在 Day 30 展開,這裡先知道有這些東西即可。

  • Vercel 官方文件(2026 年 3 月):https://vercel.com/docs,Next.js 部署、ISR、Edge runtime。
  • Next.js — Deployment:https://nextjs.org/docs/app/building-your-application/deploying,官方部署指南。
  • Docker — Next.js standalone:https://nextjs.org/docs/app/api-reference/config/next-config-js/output,Docker 映像最佳化。
  • Let's Encrypt:https://letsencrypt.org/,免費 HTTPS 憑證。
  • systemd.service 官方文件:https://www.freedesktop.org/software/systemd/man/systemd.service.html,Linux 服務管理。
  • GitHub Actions 官方文件:https://docs.github.com/en/actions,workflow 撰寫與 secrets 管理。

留言

這個網誌中的熱門文章

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