Nuxt vs SvelteKit。哪一個比較好?
這就是我這週一直在測試的內容。我把同一個應用程式做了兩次。一次是用 Nuxt 並開啟版本 5 相容性預覽,另一次則是使用 SvelteKit 的實驗性遠端函式。
我建立了一個基本的待辦事項應用程式,可以新增或移除任務。我也記錄了網路請求和時間。最明顯的差異是,當建立新任務時,SvelteKit 應用程式只發出一個請求,而 Nuxt 應用程式則需要兩個。
這個差異成了整個比較中最有趣的部分,讓我們來深入探討。
設定環境
兩個應用程式都是簡單的待辦清單,後端使用瀏覽器記憶體儲存。兩者都是在本地端執行的正式版本建置,並且都在伺服器端渲染初始任務清單。
快速提醒,Nuxt 5 尚未正式發布。我的 Nuxt 應用程式是穩定的 Nuxt 4.5,並設定相容性旗標:
// nuxt.config.ts
export default defineNuxtConfig({
compatibilityDate: '2026-07-01',
future: {
compatibilityVersion: 5,
},
})
Enter fullscreen mode Exit fullscreen mode
而 SvelteKit 的遠端函式在文件上仍標示為實驗性。因此這是方向性的比較,而不是成品的比較。
數據比較
當我在 Nuxt 應用程式中新增任務時,會發出一個 POST 請求到 /api/tasks(約 690 毫秒),接著發出一個 GET 請求到 /api/tasks(約 450 毫秒)來刷新清單。總計超過 1,100 毫秒,且時間軸面板顯示有兩個瀏覽器請求。
當我在 SvelteKit 應用程式中新增任務時,只發出一個請求,約 1,100 毫秒。伺服器仍然執行兩項操作(變更和讀取),但它們會在單一回應中返回。
一般刷新幾乎相同。Nuxt 為 447 毫秒,SvelteKit 為 448 毫秒。我執行了很多次,如果要選擇的話,SvelteKit 整體感覺稍快一些。但總時間非常接近,我不會根據這些數據來選擇框架。
讓我們來談談每個框架中的請求是如何運作的。
Nuxt 版本:明確的 API 路由
如果你之前使用過 Nuxt,這會感覺很熟悉。我在 server/api 中有兩個處理程式:
// server/api/tasks.get.ts
import { defineEventHandler } from 'h3'
import { readTasks } from '../utils/task-store'
export default defineEventHandler(() => readTasks())
Enter fullscreen mode Exit fullscreen mode
POST 處理程式會驗證標題並儲存任務。這些是正常的 HTTP 端點。任何能使用 HTTP 的東西都可以呼叫它們。
在頁面上,useFetch 會在 SSR 期間載入初始資料,因此 hydration 不會再次抓取資料。當我新增任務時,我會使用 $fetch 發出 POST,然後呼叫 refresh():
const { data: snapshot, refresh } = await useFetch<TaskSnapshot>('/api/tasks', {
key: 'task-dashboard',
})
async function submitTask() {
await $fetch('/api/tasks', {
method: 'POST',
body: { requestId: crypto.randomUUID(), title },
})
await refresh()
}
Enter fullscreen mode Exit fullscreen mode
程式碼很明確,網路分頁也與程式碼完全相符。先 POST,再 GET。
我可以避免第二次請求嗎?當然可以。POST 可以返回更新後的清單,然後我可以自己修補本地狀態。我這樣寫是因為 invalidate-and-refetch 是大多數人會使用的流程,而這正是 SvelteKit 的遠端函式所設計要改善的模式。我也發現,透過新增 GET 請求,我們可以驗證 POST 之後的確切輸出。
SvelteKit 版本:遠端函式
這是實驗性功能。你可以在 svelte.config.js 中啟用它:
const config = {
kit: {
adapter: adapter(),
experimental: {
remoteFunctions: true,
},
},
compilerOptions: {
experimental: {
async: true,
},
},
}
Enter fullscreen mode Exit fullscreen mode
然後建立一個以 .remote.ts 結尾的檔案,並匯出你的伺服器函式:
// tasks.remote.ts
import { command, query } from '$app/server'
import { addTask as addTaskToStore, readTasks } from '$lib/server/task-store'
import * as v from 'valibot'
const taskInput = v.object({
requestId: v.pipe(v.string(), v.trim(), v.minLength(1), v.maxLength(100)),
title: v.pipe(v.string(), v.trim(), v.minLength(1), v.maxLength(80)),
})
export const getTasks = query(async () => readTasks())
export const addTask = command(taskInput, async ({ requestId, title }) => {
const result = await addTaskToStore(title, requestId)
// This runs on the server, and the refreshed query value
// comes back in the same command response.
void getTasks().refresh()
return result
})
Enter fullscreen mode Exit fullscreen mode
函式主體總是在伺服器上執行。在瀏覽器中,它們會變成 SvelteKit 為你生成的端點的型別包裝器。我很喜歡沒有公開端點可以呼叫,它是隔離的。它是為呼叫而建立的,這意味著我可以在其中使用環境變數和密鑰,而不需要特別考慮。
void getTasks().refresh() 這行是在伺服器端觸發刷新的 API。在變更之後,SvelteKit 會在伺服器上刷新查詢,並將新值打包到命令的回應中。這就是單一飛行變更(single-flight mutation),也是為什麼網路分頁顯示一個請求而不是兩個。
在頁面上,我只需匯入並呼叫函式:
<script lang="ts">
import { addTask, getTasks } from './tasks.remote'
const tasks = getTasks()
async function submitTask(event: SubmitEvent) {
event.preventDefault()
await addTask({ requestId: crypto.randomUUID(), title })
}
</script>
{@render dashboard(await tasks)}
Enter fullscreen mode Exit fullscreen mode
底部的 await tasks 幾乎就像一個訂閱。當伺服器端刷新發生時,任務清單會自動更新。無需擔心伺服器路由。
我第一次看到這個模式時,覺得有點複雜。但它很快就理解了,而查詢刷新在命令內的想法,一旦你在網路分頁中看到它,就會變得合理。如果你使用過 Next 或 TanStack Start 中的伺服器動作,這會感覺很熟悉。我會說我還是比較喜歡 TanStack Start 的伺服器動作,但這個已經很接近了。
注意:遠端函式從 SvelteKit 2.27 開始提供,目前仍為實驗性。API 在過去幾個月已經改變了好幾次。如果你提前採用,請鎖定版本並預留遷移時間。
Nuxt 5 提供了什麼?
Nuxt 5 目前沒有對應遠端函式的方案。大多數已確認的工作都在底層結構上。新版本的 Nitro、新的 Vite 整合,以及框架內部。 升級指南 說明了預期內容,大多是基礎工作,而不是新的應用層級 API。
我的結論
我會繼續使用 Nuxt。我喜歡 API 路由模式,我喜歡 Vue,而這個示範沒有任何理由需要重寫現有應用程式。如果你覺得有需要,你今天就可以從變更中返回更新後的資料,並自己跳過第二次請求。
但我確實懷念伺服器動作。SvelteKit、Next 和 TanStack Start 等框架都有某種版本的型別化、非公開伺服器函式,你可以從元件中呼叫。我希望 Nuxt 在未來的更新中加入類似的功能,超越它們現在擁有的伺服器元件。
如果你正在開始一個 SvelteKit 專案,並且可以接受實驗性 API,請先嘗試遠端函式。單一飛行變更是很棒的,而跨邊界的型別免費提供是很好的開發者體驗。
你站在哪一邊:明確的 API 路由,還是為你生成傳輸的遠端函式?如果你同意或不同意,請在評論中告訴我。
另外,我在這篇文章和影片的所有研究都使用了 Kiro!歡迎看看,它是一個很棒的工具!



0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.