起因
React 写多了开始审美疲劳:useState 一个数字、useEffect 一个 fetch、
useMemo 一堆 callback...... 想试试别的范式。Svelte 5 的卖点是
"编译时把响应式翻译成命令式代码"——bundle 极小,写法更接近原生 JS。
跟一个小项目(个人博客 + 评论系统)试了一周。
装
npm create vite@latest myblog -- --template svelte-ts
cd myblog
npm i
npm run dev
或者 SvelteKit(带 SSR / routing / API routes):
npx sv create myblog
第一个组件
<!-- Counter.svelte -->
<script lang="ts">
let count = $state(0)
let doubled = $derived(count * 2)
function increment() {
count++
}
</script>
<button onclick={increment}>
{count} (doubled: {doubled})
</button>
<style>
button {
padding: 8px 16px;
border-radius: 4px;
}
</style>
$state 和 $derived 是 Svelte 5 引入的 "runes"——显式标记响应式
变量。
跟 React 对比:
const [count, setCount] = useState(0)
const doubled = useMemo(() => count * 2, [count])
return (
<button onClick={() => setCount(c => c + 1)}>
{count} (doubled: {doubled})
</button>
)
Svelte 优势:
count++直接改,无需 setterdoubled自动追踪依赖,无需 deps 数组<style>scoped 内置- 模板更接近 HTML,无 className 之类
bundle 大小:上面 Svelte 组件编译后约 1KB;React 等价组件需要 React +
ReactDOM ~45 KB。
$effect: 副作用
<script>
let count = $state(0)
$effect(() => {
console.log(`count changed to ${count}`)
document.title = `Count: ${count}`
})
</script>
$effect 类似 React useEffect 但自动追踪用到的响应式变量,
不需要 deps 数组。count 一变就重跑。
父子通信:$props / $bindable
<!-- Child.svelte -->
<script lang="ts">
let { name, count = $bindable(0) } = $props<{
name: string
count: number
}>()
</script>
<input bind:value={count} />
<p>{name}: {count}</p>
<!-- Parent.svelte -->
<script>
let n = $state(0)
</script>
<Child name="counter" bind:count={n} />
<p>Parent sees: {n}</p>
bind: 是 Vue v-model 的等价物。$bindable 让 prop 双向。
列表渲染
<script>
let items = $state([1, 2, 3])
function add() {
items.push(items.length + 1) // ✅ 直接 push,Svelte 5 能追踪
}
</script>
{#each items as item, i (item)}
<div>{i}: {item}</div>
{/each}
<button onclick={add}>add</button>
(item) 是 key(类似 React key)。
{#each} / {#if} / {#await} 是 Svelte 模板语法。
异步:{#await}
<script>
let promise = fetch('/api/users/1').then(r => r.json())
</script>
{#await promise}
<p>loading...</p>
{:then user}
<p>{user.name}</p>
{:catch err}
<p>error: {err.message}</p>
{/await}
直接在模板里处理 promise,不需要 useState + useEffect。
SvelteKit:File-based routing + SSR
src/routes/
├── +page.svelte # /
├── about/+page.svelte # /about
├── posts/
│ ├── +page.svelte # /posts
│ ├── +page.server.ts # 数据预取(server-only)
│ └── [id]/+page.svelte # /posts/:id
// src/routes/posts/+page.server.ts
export async function load() {
const posts = await db.posts.findMany()
return { posts }
}
<!-- src/routes/posts/+page.svelte -->
<script>
let { data } = $props()
</script>
{#each data.posts as post}
<a href="/posts/{post.id}">{post.title}</a>
{/each}
load 在服务端跑,结果通过 data prop 传给页面。
类似 Next.js getServerSideProps 但更轻量。
API routes
// src/routes/api/posts/+server.ts
import { json } from '@sveltejs/kit'
export async function GET() {
const posts = await db.posts.findMany()
return json(posts)
}
export async function POST({ request }) {
const data = await request.json()
const post = await db.posts.create({ data })
return json(post, { status: 201 })
}
文件即 API endpoint。
状态管理
不需要 Redux / Zustand。直接 $state 在共享模块里:
// src/lib/auth.svelte.ts
export const auth = $state({
user: null as User | null,
})
export async function login(email, pw) {
const r = await fetch('/api/login', ...)
auth.user = await r.json()
}
任何组件 import 后修改 auth.user,所有用到的地方自动更新。
bundle 大小对比
同一个 todo app:
| bundle (gzipped) | |
|---|---|
| React + Redux | 78 KB |
| React + Zustand | 52 KB |
| Vue 3 | 41 KB |
| Svelte 5 | 11 KB |
Svelte 编译模式让框架本身的运行时极小。
性能 benchmark
js-framework-benchmark
显示 Svelte 在多数操作上比 React 快 1.5-3 倍(虽然 React 19 + compiler
之后差距缩小)。
何时选 Svelte
- 个人项目 / 小团队 / 喜欢简洁
- bundle size 关键场景(嵌入式 widget / PWA)
- 已有 web 基础,不想被框架抽象绑死
何时选 React:
- 大生态需求(组件库 / 招聘 / 大公司支持)
- 已有 React 团队
- React Native 跨端
缺点
- 生态相对小:组件库 / 工具链 / 教程都比 React 少
- 招聘难:Svelte 工程师比 React 少很多
- Svelte 5 runes 模式刚出:4 → 5 迁移有小痛
- 大型应用案例少:Apple Music 等用了但还不算主流
一周体验感受
- 写起来明显比 React 顺手(无 useState 仪式、无 deps 数组焦虑)
- 编译后产物小到惊喜
- 但生态查个稍复杂的库选择少 / 文档少
- 对于个人博客 / 小工具站推荐;上生产前评估团队 / 长期维护成本
踩过的坑
-
Svelte 4 教程 / 文档不适用 Svelte 5:
export let foo→let { foo } = $props(),
$:→$derived/$effect。教程要看 5.x。 -
$state必须在 script setup 顶层:不能在条件 / 函数里。 -
数组 / 对象的响应性:
items.push(...)Svelte 5 能追踪,
但深层嵌套items[0].nested.value = ...仍要小心。深层用 store。 -
SSR + 浏览器 API:
window.localStorage在 SSR 时报错。
if (browser) { ... }包起来或onMount里访问。 -
VSCode 插件:要装官方 Svelte for VSCode + 语言服务器。
IntelliSense / 跳定义都依赖。
登录后参与评论。