Communitygithub.com

Svelte 5 靜態網站專用 (Runes)

建立、遷移或審查使用 runes($state, $derived, $effect, $props, $bindable)的靜態(無伺服器)Svelte 5 應用程式,取代舊版 Svelte 4 的反應性語法(export let、$:、stores)。

Svelte 5 靜態網站專用 (Runes) 是什麼?

此技能專注於使用 Svelte 5 的 runes 模式開發「靜態網站」,也就是最終產出為一組預先建置好的 HTML/CSS/JS 檔,可在 GitHub Pages、Netlify、Cloudflare Pages 或 S3+CloudFront 等靜態代管服務上直接運作,無需任何 Node.js 或邊緣伺服器。技能涵蓋從專案初始化、元件撰寫到安全性檢查的完整流程。它會根據需求引導使用者選擇適當的專案架構:單頁應用用 Vite+Svelte,多路由且重視 SEO 則用 SvelteKit + adapter-static。在撰寫元件時,強制使用 runes 語法($state、$derived、$effect、$props)並提供與 Svelte 4 的對照表,同時特別提醒必須從記憶體中調用參考文件以正確設定 adapter 選項。最後,技能強調靜態網站仍須面對客戶端安全威脅,因此內建一套安全檢查清單,包括處理 {`@html`} 的 XSS 風險、避免在 VITE_ 環境變數中存放機密、設定 Content-Security-Policy,以及驗證動態 href/src。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/agentmemoir/skills/tree/HEAD/skills/frontend/svelte5-static-runes

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Svelte 5 靜態網站專用 (Runes) 是做什麼的?

技能涵蓋範圍

「靜態網站」意指最終產出是一組預先建置好的 HTML/CSS/JS 檔案,在請求期間不需要 Node.js 或邊緣伺服器——它可託管於 GitHub Pages、Netlify/Cloudflare/Vercel 的靜態代管服務、S3+CloudFront,或直接以本地檔案開啟。此限制排除了 +page.server.js、+server.js、表單動作(form actions)、hooks.server.js 以及任何假設有伺服器在運行的程式碼路徑。「Runes」則代表元件反應性是使用 Svelte 5 的 $state / $derived / $effect / $props 基本語法來撰寫,而非 Svelte 4 的 export let / $: / 隱含反應性。

這兩個限制會相互影響:只有當沒有任何程式碼——包括 $effect 內部的——假設它是在每次請求的伺服器上運行時,靜態建置才能順利運作。請全程牢記這點;這是人們將 SvelteKit 應用遷移到 adapter-static 時最常見的錯誤來源。

步驟 1 — 選擇正確的專案架構

詢問(或從上下文推斷)應用程式是否需要基於檔案的路由系統、多個預先渲染的頁面,或是利於 SEO 的每路由 HTML。這會決定整個專案的配置方式,因此請勿跳過此步驟。

需求使用方式
單頁面 / 儀表板 / 小工具 / 嵌入元件,無需路由純 Vite + Svelte (npm create vite@latest -- --template svelte-ts)。這是最簡單的靜態專用配置——完全不會有意外引入伺服器的可能。
多個路由,每個路由預先渲染成獨立的 HTML 檔案,且 SEO 重要SvelteKit + @sveltejs/adapter-static 並設定 prerender = true。
多個路由,但 SEO 不重要(內部工具、Chrome 擴充功能、Electron/Tauri UI)SvelteKit + adapter-static 並使用 SPA 模式(一個單一的 fallback: 'index.html' 外殼,用戶端路由)。

除非我有明確的路由需求,否則預設使用純 Vite+Svelte——它的配置項較少,不容易出錯。這兩種路徑的完整設定步驟、設定檔和部署注意事項都寫在 references/static-build.md 中;請在憑記憶產生 svelte.config.js / vite.config.js 之前先閱讀該文件,因為 prerender/adapter 的選項會隨著 Svelte 版本而改變,而且很容易犯下細微的錯誤(例如忘記 trailingSlash,或在你也想預先渲染的頁面上設定 ssr = false,這會產生一個空殼而非真正的 HTML)。

步驟 2 — 使用 runes 而非 Svelte 4 語法撰寫元件

在新的 Svelte 5 程式碼中,永遠不要將 export let、$:、on:click 或單純的 writable() store 作為管理元件狀態的主要方式——這些語法在多數配置下仍可編譯,但它們是舊版模式,而我們在這裡要討論的是 runes。核心對照表如下:

Svelte 4Svelte 5
let count = 0(因位於頂層而具有反應性)let count = $state(0)
$: doubled = count * 2let doubled = $derived(count * 2) (或針對多陳述式邏輯使用 $derived.by(() => ...))
$: { sideEffect(count) }$effect(() => { sideEffect(count) })
export let namelet { name } = $props()
export let value; bind:value(子元件可修改)let { value = $bindable() } = $props()
用於僅操作 DOM 的生命週期 onMount/onDestroy$effect(() => { ...; return () => cleanup(); })
<slot name="header">{#snippet header()}...{/snippet} + {@render header()}
writable()/readable() store,元件內使用 $store在 .svelte.js/.svelte.ts 模組中使用 $state(...),直接匯入使用(反應性現在可在元件外部運作)

在撰寫非平凡的 rune 程式碼之前,請先閱讀 references/runes.md——它涵蓋了人們經常搞錯的部分:$derived 必須保持純粹(內部不能有副作用或修改狀態),$effect 絕對不能寫入它所讀取的相同狀態(會造成無限迴圈),$state.raw 用於你需要重新賦值而非修改的大型/凍結資料,$effect.root/untrack 用於在元件生命週期之外共享的狀態,以及用於跨元件/模組狀態的確切基於類別的模式。弄錯這些會產生難以在事後診斷的錯誤——它們只會表現為奇怪的重新渲染循環或過時的 UI——因此最好一開始就寫對。

在遷移現有的 Svelte 4 程式碼時,npx sv migrate svelte-5 可以處理機械性的改寫(這是目前使用的工具;較舊的文件可能仍參考 svelte-migrate),但請務必審查 diff——自動化遷移無法可靠地修復像是 store 作為跨模組狀態的模式,或是那些本來應該是 derived values 的 effect。

步驟 3 — 套用安全檢查清單

靜態代管並不代表「沒有安全風險」——它移除了伺服器端的攻擊面,但客戶端的攻擊面(XSS、機密外洩、不安全的第三方嵌入)完全落在你身上,因為沒有伺服器端層級可以攔截。在完成任何涉及使用者產生的內容、{@html}、環境變數或第三方腳本的任務之前,請檢查 references/security.md。以下是影響最大的規則,即使在快速任務中也不應遺漏:

  1. 永遠不要將未經消毒的內容傳遞給 {@html}。 Svelte 會自動跳脫 {expression} 大括號中的所有內容,但 {@html ...} 會完全繞過此機制——這是 Svelte 應用程式中最大的 XSS 來源。請在渲染前立即使用 DOMPurify(或嚴格的許可清單)進行消毒,而不是在管線的較早階段,以免後續的編輯繞過它。
  2. 永遠不要將真正的機密放在以 VITE_ 為前綴(或 SvelteKit 的 PUBLIC_ 前綴)的環境變數中。 Vite 在建置時會將這些變數內聯到已發布的 JS 套件中——任何人都可以從建置後的檔案中讀取它們。靜態網站完全沒有伺服器可以隱藏機密,因此如果應用程式需要真正的 API 金鑰,該金鑰應屬於第三方服務的伺服器,或安排一個我另行部署的獨立 serverless 函式/代理——請向我標示此情況,而不是默默地將金鑰寫入客戶端程式碼。
  3. 即使沒有伺服器可以發送標頭,也請設定 Content-Security-Policy。 靜態代管服務仍然支援透過 app.html/index.html 中的 <meta http-equiv="Content-Security-Policy"> 標籤,或透過特定於託管服務商的標頭檔案(Netlify _headers、Cloudflare Pages _headers、Vercel vercel.json headers)來實現——請參閱 references/security.md 了解這兩種方式。
  4. 如果 href/src 可能來自使用者輸入或 API 回應,請將每個動態的 href/src 視為攻擊者可控的內容。 Svelte 不會阻擋 javascript: 開頭的 URL 出現在 href 綁定中——請自行驗證/許可該協定。
  5. 在發布前執行 npm audit(或同等指令)並鎖定 lockfile——靜態網站仍然會將每個依賴項的程式碼直接發布到瀏覽器,因此一個被入侵的依賴項就是一個直接的客戶端入侵。

參考檔案

  • references/runes.md — 完整的 rune-by-rune 指南,包含正確/錯誤的模式、基於類別的共享狀態、props/snippets 的 TypeScript 型別、以及詳細的 Svelte 4 到 5 遷移對照表。
  • references/static-build.md — 純 Vite+Svelte 設定、SvelteKit+adapter-static 設定(完全預渲染和 SPA 備援模式)、trailing-slash/base-path 的陷阱、以及各託管服務商的部署注意事項(GitHub Pages、Netlify、Cloudflare Pages、S3/CloudFront)。
  • references/security.md — 完整檢查清單:XSS/{@html}/DOMPurify、靜態代管服務的 CSP(meta 標籤 + _headers 範本)、機密/環境變數、安全的外部連結、依賴項稽核、Trusted Types、localStorage/sessionStorage 的敏感性、以及發布前檢查清單。
  • assets/ — 可複製的入門檔案:vite.config.ts、svelte.config.js(adapter-static)、_headers(CSP 範本)和 SecureHtml.svelte(一個 DOMPurify 包裝元件)。

請視需要參考這些檔案,而不是憑記憶重現其內容——配置選項名稱和 CSP 指令的細節正是那種容易過時或被記錯的內容。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相關技能