
內容摘要
SvelteKit 3 大多是打磨,但有四項不是外觀變更:設定移到 vite.config.ts、$lib 變成 #lib、tsconfig 改 extends $app/tsconfig、環境變數移到 src/env.ts。
SvelteKit 3 於 2026 年 10 月 1 日發布,而發布說明異常坦白:它是同一個框架,但打磨更多、型別更安全、雜物更少。這對升級是好消息,因為工作大多是機械性的——但仍有幾項變更會碰到大量檔案,所以官方遷移是唯一理性的起點。
本文會先跑遷移,再逐一說明它無法替你完成的四個變更。
01實際上改了什麼
破壞性變更數量不多,但影響範圍很廣:
| 領域 | SvelteKit 2 | SvelteKit 3 |
|---|---|---|
| 框架設定 | svelte.config.js | vite.config.ts |
| 共用程式碼別名 | $lib | #lib subpath imports |
| TypeScript 設定 | extends ./.svelte-kit/tsconfig.json | extends $app/tsconfig |
| 環境變數 | 隱式 | 在 src/env.ts 宣告 |
| Service worker API | $service-worker | $app/env、$app/paths、$app/manifest |
| 淺層路由 | pushState / replaceState | goto(..., { shallow: true }) |
| Vite | 支援 7 | 需要 8(Rolldown) |
另外兩項在正式環境很重要:錯誤處理現在涵蓋 render 錯誤,不只 load 錯誤;而且所有錯誤——包括你自己用 error(...) 丟出的那些——都會經過 handleError。堆疊追蹤現在也套用了 sourcemap。
需要 Svelte 5 與 Vite 8
SvelteKit 3 建立在 Svelte 5 的 error boundary 之上,並需要 Vite 8。若你還在 Svelte 4,請先升到 Svelte 5,並把兩次遷移分開。
02先跑官方遷移
sv CLI 內置遷移,會改寫它能改的部分,並把其餘留成 TODO 清單。請在乾淨、沒有未提交變更的分支上執行。
git checkout -b chore/sveltekit-3
npx sv migrate sveltekit-3 --tasks all --confirm
--tasks all 會依序執行每個遷移任務,--confirm 跳過互動提示,讓執行可重現。動手編輯前先看產生的 TODO 清單——它會告訴你工具無法改寫哪些 import 與設定區塊。
接著更新相依套件,讓工具鏈浮出剩下的問題:
npm install
npx svelte-kit sync
npm run check
svelte-kit sync 會重新產生 tsconfig 與 #lib 變更所依賴的型別。若跳過它,編輯器會回報建置時其實能解析的壞掉 import。
03把設定移進 vite.config.ts
SvelteKit 插件現在在 Vite 需要的地方接收設定,而不是透過非同步解析 svelte.config.js。實務上,你把插件 import 進 vite.config.ts,並把原本的設定物件傳給它。
import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
sveltekit({
// 原本 svelte.config.js 的 `kit` 物件。
adapter: undefined,
alias: {}
})
]
})
物件的確切形狀記載於 SvelteKit 3 遷移指南,因為它取決於你的應用用了哪個 adapter 與前處理器選項。請把上面的區塊當成目的地,而不是可直接複製的答案:把你的 kit 設定搬過去、刪掉 svelte.config.js,並在真實建置中確認 adapter 能解析,而不是只在 dev 裡。
04把 $lib 換成 #lib
共用程式碼仍放在 src/lib,只有 import 路徑改變。Subpath imports 是 Node 功能,Vite 與 TypeScript 原生支援,這也是 SvelteKit 能刪掉過去同步兩者程式碼的原因。
// SvelteKit 2
import { formatPrice } from '$lib/format'
import { Button } from '$lib/components'
// SvelteKit 3
import { formatPrice } from '#lib/format.ts'
import { Button } from '#lib/components/index.ts'
陷阱在解析。Subpath imports 必須明確,所以單獨的 #lib/format 會失敗,你需要 #lib/format.ts 或 #lib/format/index.ts。遷移任務會自動改寫多數 import;完成後請 grep 出任何殘留的 $lib。
grep -rn "\$lib" src/ --include='*.ts' --include='*.svelte' | head -30
模糊的 subpath import 會在型別檢查時失敗
若
npm run check回報找不到#lib/foo,解法幾乎都是補上明確副檔名或index.ts。這是解析規則,不是 SvelteKit 的 bug。
05簡化 tsconfig 並宣告環境變數
你的 tsconfig.json 不再 extends .svelte-kit 裡的產生檔,而是 extends $app/tsconfig。它帶有更多建議的編譯器選項,所以多數應用可以刪掉自己的 compilerOptions 區塊。你應明確設定 include 與 exclude,並把 service worker 放進 exclude。
環境變數的變更更大。不再到處臨時使用 $env/static/private 之類模組,而是在 src/env.ts 宣告應用依賴的變數,標記每個是公開或私有、在建置時或啟動時解析。Standard Schema 函數庫可以驗證它們。
// src/env.ts —— 形狀示意;確切 API 請查遷移指南。
import { defineEnv } from '$app/env'
export const env = defineEnv({
DATABASE_URL: { private: true },
PUBLIC_API_BASE: { public: true, buildTime: true }
})
好處是型別安全、經驗證的變數,在使用處 import,而不是從雜物袋模組拉出來。代價是每個隱式 process.env 參照都需要宣告,漏掉會在建置時而非執行時失敗——這正是目的。
06Service worker、錯誤與淺層路由
三個值得手動檢查的較小變更:
- Service worker 不再從
$service-workerimport,改從$app/env、$app/paths與新的$app/manifest取得所需內容。若想要準確型別,請在 service worker 旁新增 extends$app/tsconfig/service-worker的tsconfig.json。 - 錯誤處理 現在涵蓋 render 錯誤,且每個錯誤都會經過
handleError。請稽核該函數:過去假設只有 load 錯誤會進來的記錄邏輯,可能突然看到大量流量。 - 淺層路由 改用
goto(url, { shallow: true }),而非pushState/replaceState。淺層導覽現在會觸發beforeNavigate,而persistState: true讓頁面狀態在重新載入後保留。
07合併前先驗證升級
- 跑
npm run check並清掉每個型別錯誤,不要繞過它們合併。 - 跑正式建置,而不只是 dev,讓 adapter 與 Vite 8 路徑都被走過。
- 端到端測一條完整用戶流程,包含 form action 與一條會失敗的路由。
- 檢查錯誤回報:觸發一個 render 錯誤,確認它帶著 sourcemap 堆疊抵達
handleError。 - 把
vite.config.ts與舊的svelte.config.js做 diff,確認搬移過程中沒有漏掉任何kit選項。
讓遷移單獨成為一個 commit
sv遷移會碰到大量檔案。請讓它單獨提交,再用 diff 工具審查,之後才加入你自己的後續修正。把 200 個檔案的機械式更名與功能開發混在一起,會讓回歸幾乎無法二分定位。
08常見錯誤與修正
| 錯誤 | 原因 | 修正 |
|---|---|---|
找不到模組 #lib/foo | subpath import 不明確 | 改用 #lib/foo.ts 或 #lib/foo/index.ts |
| 設定選項沒有作用 | 還留在殘餘的 svelte.config.js | 搬進 sveltekit() 插件 |
| 乾淨 checkout 出現型別錯誤 | 產生的型別過期 | 執行 npx svelte-kit sync |
| 建置時缺少環境變數 | 未在 src/env.ts 宣告 | 宣告它,公開或私有 |
| Service worker 型別失敗 | 舊的 $service-worker import | 改從 $app/* 模組 import |
對小型應用來說,升級是一下午的工作;對大型應用則是一週的謹慎活。但形狀是可預測的:讓 sv 做更名,再把注意力放在設定搬移、環境宣告與錯誤路徑上。那三個地方,是綠色建置仍可能藏著壞掉應用的所在。
比對自動遷移前後的 svelte.config.js 與 vite.config.ts。
Diff 比對工具常見問題
SvelteKit 3 是重寫嗎?
不是。團隊形容它是同一個框架,但有更多打磨、更好的型別安全與更少的歷史包袱。多數變更是機械性的,官方遷移能處理大部分。
最大的破壞性變更是什麼?
設定從 svelte.config.js 移到 vite.config.ts。其餘都較小,但 #lib 更名與 tsconfig 變更幾乎會碰到大型應用裡的每個檔案。
#lib 一定要寫明確的副檔名嗎?
解析上要。因為這些是 Node subpath imports,TypeScript 與 Node 要求路徑明確,所以 `#lib/foo` 會變成 `#lib/foo.ts` 或 `#lib/foo/index.ts`。
SvelteKit 3 的 remote functions 穩定了嗎?
還沒。remote functions 仍需實驗性旗標,團隊表示完成它是發布後的首要任務。
可以從 Svelte 4 應用直接升級嗎?
SvelteKit 3 需要 Svelte 5,所以實務順序是先升到 Svelte 5,再跑 SvelteKit 遷移。一次做兩件事會讓失敗難以隔離。
相關文章
- 用 diff 工具比對兩段文字
提交前先審查遷移 diff。
- 如何格式化、驗證與壓縮 JSON
package.json 與 tsconfig 都是 JSON,改完請驗證。
相關工具
- Diff 比對工具
找出遷移動過的每一行設定。
- JSON 驗證工具
抓出壞掉的 tsconfig 或 package.json。
- JSON 格式化工具
讀懂吵雜的 package.json 差異。