Svelte 什么是 Svelte? Svelte 是一个现代的前端框架,由 Rich Harris 在 2016 年创建。与 React、Vue 等框架不同,Svelte 在构建时 进行编译,而不是在运行时。本文章 svelet 版本为 5.39。
核心优势 1 2 3 4 5 6 7 import React from "react" ;import ReactDOM from "react-dom" ;
编译时 vs 运行时 1 2 3 4 5 6 7 8 9 <script > let count = 0 ; function increment ( ) { count += 1 ; } </script > <button onclick ="{increment}" > Count: {count}</button >
编译后生成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function create_fragment (ctx ) { let button; return { c ( ) { button = element ("button" ); button.textContent = `Count: ${count} ` ; }, m (target, anchor ) { insert (target, button, anchor); }, p (ctx, dirty ) { if (dirty & 1 ) { button.textContent = `Count: ${count} ` ; } }, d (detaching ) { if (detaching) detach (button); }, }; }
项目创建
推荐使用 SvelteKit (degit 方式淘汰)
1 npm create svelte@latest my-app
Runes
Svelte 5 引入了全新的响应式系统 Runes (符文系统),用特殊的符号($开头)来声明响应式状态,替代了传统的 let 变量和 $: 语法。
核心概念对比 Svelte 4(传统方式) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script > let count = 0 ; let doubled; $ : doubled = count * 2 ; $ : { console .log ("Count changed:" , count); } function increment ( ) { count++; } </script > <button on:click ="{increment}" > Count: {count}, Doubled: {doubled}</button >
Svelte 5(Runes 方式) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script > let count = $state(0 ); let doubled = $derived(count * 2 ); $effect(() => { console .log ("Count changed:" , count); }); function increment ( ) { count++; } </script > <button onclick ="{increment}" > Count: {count}, Doubled: {doubled}</button >
$state
$state 用于声明响应式变量,类似于 Vue 3 的 ref。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script > let count = $state(0 ); let user = $state({ name : "Alice" , age : 25 , }); function updateUser ( ) { user.name = "Bob" ; user.age = 30 ; } </script >
$state.raw()
只追踪顶层属性,不追踪嵌套属性(性能更好)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <script > let deep = $state({ level1 : { level2 : { value : 1 , }, }, }); let shallow = $state.raw ({ level1 : { level2 : { value : 1 , }, }, }); function testDeep ( ) { deep.level1 .level2 .value ++; shallow.level1 .level2 .value ++; shallow = { level1 : { level2 : { value : shallow.level1 .level2 .value + 1 , }, }, }; } </script > <div > <p > Deep: {deep.level1.level2.value}</p > <p > Shallow: {shallow.level1.level2.value}</p > <button onclick ="{testDeep}" > Test</button > </div >
$state.snapshot()
创建状态的非响应式快照(普通对象)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <script > let user = $state({ name : "Alice" , age : 25 , settings : { theme : "dark" , language : "en" , }, }); function saveToLocalStorage ( ) { const snapshot = $state.snapshot (user); localStorage .setItem ("user" , JSON .stringify (snapshot)); } let history = $state([]); function saveHistory ( ) { history.push ($state.snapshot (user)); } function restoreHistory (index ) { const snapshot = history[index]; user.name = snapshot.name ; user.age = snapshot.age ; user.settings = snapshot.settings ; } async function updateUserOnServer ( ) { const snapshot = $state.snapshot (user); await fetch ("/api/user" , { method : "POST" , body : JSON .stringify (snapshot), }); } </script > <div > <input bind:value ="{user.name}" /> <button onclick ="{saveHistory}" > Save Snapshot</button > <button onclick ="{saveToLocalStorage}" > Save to Storage</button > </div >
$state.frozen()
创建冻结的响应式状态,对象内容不可修改(类似于 Object.freeze)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <script > let config = $state({ apiUrl : "https://api.example.com" , timeout : 5000 , }); let frozenConfig = $state.frozen ({ apiUrl : "https://api.example.com" , timeout : 5000 , }); function tryModify ( ) { config.timeout = 10000 ; frozenConfig = { apiUrl : "https://new-api.example.com" , timeout : 8000 , }; } </script >
$derived
$derived 用于创建基于其他状态计算的值 (表达式),替代了 $: 响应式声明。类似于 Vue 3 的 computed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script > let count = $state(0 ); let doubled = $derived(count * 2 ); let description = $derived( count === 0 ? "Zero" : count > 0 ? `Positive: ${count} ` : `Negative: ${count} ` ); </script >
$derived.by
接收函数,用于创建不适合放在简短表达式中的复杂派生
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script > let numbers = $state([1 , 2 , 3 ]); let total = $derived.by (() => { let total = 0 ; for (const n of numbers) { total += n; } return total; }); </script > <button onclick ={() => numbers.push(numbers.length + 1)}> {numbers.join(' + ')} = {total} </button >
$effect
$effect 用于执行副作用,替代了 $: 响应式块和生命周期函数 在 await 之后或在 setTimeout 内部等情况下读取的值将不会被追踪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <script > let count = $state(0 ); let size = $state(10 ); $effect(() => { console .log ("Count changed:" , count); }); $effect(() => { const interval = setInterval (() => { count++; }, 1000 ); setTimeout (() => { size = 20 ; }, 0 ); return () => { clearInterval (interval); }; }); </script >
理解依赖关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 <script > let user = $state({ name : 'Alice' , age : 25 , address : { city : 'New York' , country : 'USA' } }); $effect(() => { console .log ('Effect 1: user object' , user); }); $effect(() => { console .log ('Effect 2: user.name' , user.name ); }); $effect(() => { console .log ('Effect 3: user.address.city' , user.address .city ); }); $effect(() => { console .log ('Effect 4:' , user.name , user.age ); }); </script > <button onclick ={() => { user.name = 'Bob'; }}> 修改 name </button > <button onclick ={() => { user.age = 30; }}> 修改 age </button > <button onclick ={() => { user.address.city = 'LA'; }}> 修改 city </button > <button onclick ={() => { user = { name: 'Charlie', age: 35 }; }}> 替换整个对象 </button >
想监听整个对象的变化
方案 1:$state.snapshot
$state.snapshot() 会读取对象的所有属性,所以任何属性变化都会被追踪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <script > let user = $state({ name : "Alice" , age : 25 , email : "alice@example.com" , }); $effect(() => { const snapshot = $state.snapshot (user); console .log ("User changed:" , snapshot); }); user.name = "Bob" ; user.age = 30 ; </script >
方案 2:显式读取所有属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <script > let user = $state({ name : "Alice" , age : 25 , email : "alice@example.com" , }); $effect(() => { const { name, age, email } = user; console .log ("User changed:" , name, age, email); saveToLocalStorage ({ name, age, email }); }); user.name = "Bob" ; </script >
方案 3:$inspect (开发调试)
$inspect 只用于开发调试,生产环境会被移除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script > let user = $state({ name : "Alice" , age : 25 , email : "alice@example.com" , }); $inspect(user); user.name = "Bob" ; user.age = 30 ; user.email = "bob@..." ; </script >
$effect vs useEffect 自动依赖追踪 vs 手动依赖数组 React useEffect(手动依赖)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { useEffect, useState } from "react" ;function Counter ( ) { const [count, setCount] = useState (0 ); const [name, setName] = useState ("John" ); useEffect (() => { console .log ("Count:" , count); }, [count]); useEffect (() => { console .log ("Count:" , count, "Name:" , name); }, [count, name]); }
Svelte $effect(自动依赖)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <script > let count = $state(0 ); let name = $state("John" ); $effect(() => { console .log ("Count:" , count, "Name:" , name); }); $effect(() => { console .log ("Count:" , count); }); </script >
细粒度响应 vs 全量执行 React useEffect
1 2 3 4 5 6 7 8 9 useEffect (() => { console .log ("Effect running" ); console .log ("Count:" , count); console .log ("Name:" , name); }, [count, name]);
Svelte $effect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script > let count = $state(0 ); let name = $state("John" ); $effect(() => { console .log ("Effect running" ); console .log ("Count:" , count); console .log ("Name:" , name); }); $effect(() => { if (count > 0 ) { console .log ("Count is positive:" , count); } }); </script >
执行时机 React useEffect
1 2 3 4 5 6 7 8 9 useEffect (() => { console .log ("Effect runs AFTER render" ); }, [count]); useLayoutEffect (() => { console .log ("Effect runs BEFORE paint" ); }, [count]);
Svelte $effect
1 2 3 4 5 6 7 8 9 10 11 12 <script > $effect(() => { console .log ("Effect runs synchronously" ); }); $effect.pre (() => { console .log ("Before DOM update" ); }); </script >
无限循环处理 React useEffect - 容易无限循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function Counter ( ) { const [count, setCount] = useState (0 ); useEffect (() => { setCount (count + 1 ); }, [count]); useEffect (() => { if (count < 10 ) { setCount (count + 1 ); } }, [count]); return <div > {count}</div > ; }
Svelte $effect - 自动防止无限循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <script > let count = $state(0 ); $effect(() => { count++; }); $effect(() => { if (count < 10 ) { count++; } }); $effect(() => { $effect.untrack (() => { count++; }); }); </script >
清理函数 React useEffect
1 2 3 4 5 6 7 8 9 10 useEffect (() => { const timer = setInterval (() => { console .log ("Tick" ); }, 1000 ); return () => { clearInterval (timer); }; }, []);
Svelte $effect
1 2 3 4 5 6 7 8 9 10 11 12 <script > $effect(() => { const timer = setInterval (() => { console .log ("Tick" ); }, 1000 ); return () => { clearInterval (timer); }; }); </script >
$effect 的高级用法 $effect.pre 1 2 3 4 5 6 7 8 9 10 11 12 13 <script > let count = $state(0 ); $effect.pre (() => { console .log ("Before DOM update:" , count); }); $effect(() => { console .log ("After DOM update:" , count); }); </script >
$effect.untrack
在 $effect 内部使用,用于读取状态但不追踪它作为依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <script > let userId = $state(1 ); let filter = $state("all" ); let sortBy = $state("name" ); $effect(() => { console .log ("Fetching data for user:" , userId); const currentFilter = $effect.untrack (() => filter); const currentSort = $effect.untrack (() => sortBy); fetchData (userId, currentFilter, currentSort); }); </script >
使用场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <script > let userId = $state(1 ); let debugMode = $state(false ); $effect(() => { console .log ("Fetching user:" , userId); fetchUser (userId); $effect.untrack (() => { if (debugMode) { console .log ("Debug: User ID is" , userId); } }); }); </script >
1 2 3 4 5 6 7 8 9 <script > let count = $state(0 ); $effect(() => { $effect.untrack (() => { count++; }); }); </script >
会执行,但只执行一次
初始化时执行一次 1 count = 0 → effect运行 → untrack块执行 → count变成1
之后 count 变化不会再触发 1 count = 1 → effect不再运行(因为没有追踪count)
$effect.tracking
检测当前代码是否在响应式追踪上下文中,返回 boolean 值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <script > let count = $state(0 ); function processValue (value ) { if ($effect.tracking ()) { console .log ("Reactive mode:" , value); return value * 2 ; } else { console .log ("Non-reactive mode:" , value); return value; } } $effect(() => { const result = processValue (count); }); const result = processValue (count); </script >
使用场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <script > import { writable } from "svelte/store" ; let state = $state({ value : 0 }); function getData ( ) { if ($effect.tracking ()) { return state; } else { return $state.snapshot (state); } } $effect(() => { const data = getData (); console .log ("Data:" , data.value ); }); const snapshot = getData (); console .log ("Snapshot:" , snapshot.value ); </script >
注意事项:
$effect.tracking() 在组件初始化期间返回 false
主要用于编写可在 effect 内外使用的通用函数
适用于条件性响应式逻辑
$effect.root
手动管理 effect 生命周期,使用场景很少。主动执行清理函数,否则 effect 一直存在,使用不当会导致内存泄漏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <script > let count = $state(0 ); const cleanup = $effect.root (() => { $effect(() => { console .log ("Count:" , count); }); return () => { console .log ("Cleanup root" ); }; }); function destroy ( ) { cleanup (); } </script >
$props
$props 用于声明组件的 props,提供更好的类型支持。
1 2 3 4 5 6 7 8 9 <script > let { count = 0 , step = 1 } = $props(); function increment ( ) { count += step; } </script > <button onclick ="{increment}" > Count: {count}</button >
$bindable
$bindable 用于创建可以从父组件双向绑定的 props,使用 bind 指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script > let { value = $bindable("" ) } = $props(); </script > <input bind:value ="{value}" /> <script > let text = $state("" ); </script > <input bind:value ="{text}" /> <p > You typed: {text}</p >
$inspect
$inspect 大致等同于 console.log,不同之处在于当其参数发生变化时它会重新运行。仅在开发环境有效,生产环境会被移除。
1 2 3 4 5 6 7 8 9 10 11 12 13 <script > let count = $state(0 ); let user = $state({ name : "Alice" , age : 25 }); $inspect(count); $inspect(count, user); $inspect("User data:" , user); </script >
$inspect(…).with(fn)
使用自定义函数来处理调试输出,而不是默认的 console.log。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script > let user = $state({ name : "Alice" , age : 25 }); $inspect(user); $inspect(user).with ((type, value ) => { console .group ("🔍 User Changed" ); console .log ("Type:" , type); console .log ("Value:" , value); console .log ("Timestamp:" , new Date ().toLocaleTimeString ()); console .groupEnd (); }); </script >
参数说明:
type: "init" (初始化) 或 "update" (更新)
value: 当前的值
$inspect.trace(…)
不仅打印值的变化,还会显示调用栈 ,帮助你追踪是哪里修改了这个值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <script > let count = $state(0 ); $inspect(count); $inspect.trace (count); function increment ( ) { count++; } function handleClick ( ) { increment (); } function deepClick ( ) { handleClick (); } </script >
$host
$host() 用于获取自定义元素(Web Component)的宿主 DOM 元素引用,仅在配置了 customElement 选项的组件中可用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <svelte:options customElement ="my-counter" /> <script > const host = $host(); let count = $state(0 ); $effect(() => { host.style .border = count > 10 ? '2px solid red' : '1px solid gray' ; host.classList .toggle ('high' , count > 10 ); }); </script > <button onclick ={() => count++}> Count: {count} </button >
1 2 <my-counter > </my-counter >
基础语法与组件开发 基础标记 注释 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <h1 > Hello world</h1 > <input bind:value ={name} autofocus /> <script > let { name } = $props(); </script > <main > <h1 > Hello, {name} </h1 > </main >
#if 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <script > let user = { name : 'John' , age : 25 }; let items = ['apple' , 'banana' , 'orange' ]; let showDetails = false ; </script > {#if user.age >= 18} <p > Welcome, {user.name}! You are an adult.</p > {:else if user.age >= 12} <p > Welcome, {user.name}! You are a man.</p > {:else} <p > Sorry, you must be 12 or older.</p > {/if} <button onclick ={() => showDetails = !showDetails}> {showDetails ? 'Hide' : 'Show'} details </button > {#if showDetails} <div class ="details" > <p > Name: {user.name}</p > <p > Age: {user.age}</p > </div > {/if}
#each 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <script > let todos = [ { id : 1 , text : "Learn Svelte" , done : false }, { id : 2 , text : "Build an app" , done : true }, { id : 3 , text : "Deploy to production" , done : false }, ]; function addTodo ( ) { const text = prompt ("Enter todo:" ); if (text) { todos = [ ...todos, { id : Date .now (), text, done : false , }, ]; } } function toggleTodo (id ) { todos = todos.map ((todo ) => todo.id === id ? { ...todo, done : !todo.done } : todo ); } </script > <button on:click ="{addTodo}" > Add Todo</button > <ul > {#each todos as todo, index (todo.id)} <li class:done ="{todo.done}" > <input type ="checkbox" checked ={todo.done} on:change ={() => toggleTodo(todo.id)} > {todo.text} </li > {:else} <p > 列表为空</p > {/each} </ul >
#key
表达式变化重新渲染,可添加过渡效果
1 2 3 {#key value} <div transition:fade > <Component /> </div > {/key}
#await
允许根据 Promise 的三种可能状态 — 等待中(pending)、已完成(fulfilled)或已拒绝(rejected) — 进行分支处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {#await promise} <p > 等待 promise 解决中...</p > {:then value} <p > 值是 {value}</p > {:catch error} <p > 出现错误: {error.message}</p > {/await} {#await promise then value} <p > 值是 {value}</p > {/await} {#await promise catch error} <p > 错误是 {error}</p > {/await} {#await import('./Component.svelte') then { default: Component }} <Component /> {/await}
#snippet
#snippet 用于定义可复用的模板片段,类似于”模板函数”,可以接收参数并在组件内多次渲染。
基础用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script> let items = $state(['Apple', 'Banana', 'Orange']); </script> <!-- 定义 snippet --> {#snippet itemCard(item, index)} <div class="card"> <h3>#{index + 1}</h3> <p>{item}</p> </div> {/snippet} <!-- 使用 snippet --> <div class="grid"> {#each items as item, i} {@render itemCard(item, i)} {/each} </div>
语法 定义:
1 2 3 {#snippet 名称(参数1, 参数2, ...)} <!-- 模板内容 --> {/snippet}
渲染:
1 {@render 名称(参数1, 参数2, ...)}
应用场景
消除重复模板
作为 Props 传递 (Render Props)
1 2 3 4 5 6 7 8 9 10 <!-- List.svelte --> <script> let { items, renderItem } = $props(); </script> <ul> {#each items as item} <li>{@render renderItem(item)}</li> {/each} </ul>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!-- App.svelte --> <script> import List from './List.svelte'; let users = $state([ { id: 1, name: 'Alice', role: 'Admin' }, { id: 2, name: 'Bob', role: 'User' } ]); </script> {#snippet userItem(user)} <strong>{user.name}</strong> <span class="role">{user.role}</span> {/snippet} <List items={users} renderItem={userItem} />
递归渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <script> let tree = $state({ name: 'Root', children: [ { name: 'Child 1', children: [ { name: 'Grandchild 1', children: [] } ] } ] }); </script> <!-- 递归 snippet --> {#snippet treeNode(node)} <li> <span>{node.name}</span> {#if node.children.length > 0} <ul> {#each node.children as child} {@render treeNode(child)} {/each} </ul> {/if} </li> {/snippet} <ul class="tree"> {@render treeNode(tree)} </ul>
条件渲染不同布局
注意事项
必须在顶层定义,不能放在条件语句中定义
可以访问外部作用域
支持导出,需要 svelte 5.5.0 或更新版本
1 2 3 4 5 <script module > export { add }; </script > {#snippet add(a, b)} {a} + {b} = {a + b} {/snippet}
可选的 snippet
1 2 3 4 5 6 <script> let { customRender } = $props(); </script> <!-- 使用可选链 --> {@render customRender?.() ?? 'Default content'}
createRawSnippet 动态创建 snippet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { createRawSnippet } from "svelte" ;const snippet = createRawSnippet ((param1, param2 ) => { return { render : () => string, setup : (element ) => { return () => { }; }, }; });
{@render …}
渲染一个代码片段或者表达式
{@html …}
渲染一个有效的独立 HTML
{@const …}
用于在模板中定义局部常量,避免重复计算或提高代码可读性。只能在模板中使用,只在组件渲染时计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script> let user = $state({ firstName: 'Alice', lastName: 'Smith', age: 25 }); </script> <!-- 定义局部常量 --> {@const fullName = user.firstName + ' ' + user.lastName} {@const isAdult = user.age >= 18} <h1>Welcome, {fullName}!</h1> <p>Status: {isAdult ? 'Adult' : 'Minor'}</p>
使用建议:
✅ 复杂计算提取为 {@const}
✅ 需要在多处使用的临时值
✅ 提高代码可读性
❌ 简单值直接使用
❌ 需要响应式用 $derived
{@debug …}
在特定变量发生变化时记录这些变量的值,并且如果打开了开发者工具,它会暂停代码执行。接受一个以逗号分隔的变量名列表(不接受任意表达式)
1 2 3 4 5 6 7 8 9 10 11 <script > let user = { firstname : "Ada" , lastname : "Lovelace" , }; </script > {@debug user} {@debug user1, user2, user3} {@debug user.firstname}
use
Actions(动作)是在元素挂载时调用的函数。它们通过 use: 指令添加,通常会使用 $effect 以便在元素卸载时重置任何状态 可接收 3 个可选参数,节点类型,参数,由 action 创建的任何自定义事件处理程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script > function myaction (node, data, { onswiperight: (e: CustomEvent) => void ; onswipeleft: (e: CustomEvent) => void ; } ) { $effect(() => { node.dispatchEvent (new CustomEvent ('swipeleft' )); node.dispatchEvent (new CustomEvent ('swiperight' )); return () => { }; }); } </script > <div use:myaction ="{data}" onswipeleft ="{next}" onswiperight ="{prev}" > ...</div >
style / class
分为 指令 和 属性 两种模式,指令优先级高于属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <div style:color ="red" > ...</div > <div style ="color: red;" > ...</div > <div style:color |important ="red" > ...</div > <div style ="color: blue;" style:color ="red" > 这里将显示为红色</div > <div class ={{ cool , lame: !cool }}> ...</div > <div class:cool ={cool} class:lame ={!cool} > ...</div > <div class ={[faded && 'saturate-0 opacity-50 ', large && 'scale-200 ']}> ...</div >
状态管理与数据流 组件间通信 Props(父传子) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script > import Child from "./Child.svelte" ; let message = "Hello from parent" ; let count = 0 ; </script > <Child {message } {count } /> <script > const { message, count } = $props(); </script > <div > <p > Message: {message}</p > <p > Count: {count}</p > </div >
事件(子传父) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <script > import Child from "./Child.svelte" ; let total = 0 ; function handleIncrement (event ) { total += event.detail ; } </script > <Child on:increment ="{handleIncrement}" /> <p > Total: {total}</p > <script > import { createEventDispatcher } from "svelte" ; const dispatch = createEventDispatcher (); function increment ( ) { dispatch ("increment" , { amount : 1 }); } </script > <button on:click ="{increment}" > Increment</button >
状态管理(Stores) Writable Store writable(value, start?, options?)
value: 初始值
start: 启动函数,当第一个订阅者订阅时调用,返回一个清理函数。两个参数 (set, opdate) => {},set(val): 更新 store 的值,update(fn): 通过函数更新 store 的值
options: 配置项,包含一个属性 equals 函数,返回值控制是否通知订阅者,默认用 === 逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 const count = writable (0 );count.set (5 ); count.update ((n ) => n + 1 ); const unsubscribe = count.subscribe ((value ) => { console .log ("Count:" , value); }); const websocket = writable (null , (set ) => { console .log ("创建 WebSocket 连接" ); const ws = new WebSocket ("wss://example.com" ); ws.onopen = () => { console .log ("连接已建立" ); set (ws); }; ws.onmessage = (event ) => { console .log ("收到消息:" , event.data ); }; ws.onerror = (error ) => { console .error ("WebSocket 错误:" , error); }; return () => { console .log ("关闭 WebSocket 连接" ); ws.close (); }; }); const unsubscribe1 = websocket.subscribe ((ws ) => { console .log ("订阅者1:" , ws); }); const unsubscribe2 = websocket.subscribe ((ws ) => { console .log ("订阅者2:" , ws); }); unsubscribe1 ();unsubscribe2 ();const user = writable ( { name : "Alice" , age : 25 }, undefined , { equals : (a, b ) => { return JSON .stringify (a) === JSON .stringify (b); }, } ); user.subscribe ((value ) => { console .log ("User changed:" , value); }); user.set ({ name : "Alice" , age : 25 }); user.set ({ name : "Bob" , age : 30 });
Readable Store 1 2 3 4 5 6 7 8 9 10 11 const clock = readable (new Date (), (set ) => { const interval = setInterval (() => { set (new Date ()); }, 1000 ); return () => { clearInterval (interval); }; });
Derived Store 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { writable, derived } from "svelte/store" ;export const todos = writable ([]);export const filter = writable ("all" );export const filteredTodos = derived ([todos, filter], ([$todos, $filter] ) => { switch ($filter) { case "active" : return $todos.filter ((todo ) => !todo.done ); case "completed" : return $todos.filter ((todo ) => todo.done ); default : return $todos; } });
高级特性与技巧 Module 级 Script 深度解析 什么是 Module 级 Script? Svelte 组件中可以有两种<script>标签:
普通 script - 每个组件实例都会执行
module script - 所有组件实例共享,只执行一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <script context ="module" > let instanceCount = 0 ; export function getInstanceCount ( ) { return instanceCount; } </script > <script > import { onMount } from "svelte" ; let id = ++instanceCount; onMount (() => { console .log (`Instance ${id} mounted` ); }); </script > <div > Instance #{id}</div >
核心特性
特性
Module Script
普通 Script
执行时机
模块加载时执行一次
每个实例创建时执行
作用域
所有实例共享
每个实例独立
导出
可以导出函数/变量
不能导出
生命周期
无生命周期钩子
可使用生命周期钩子
响应式
不支持$:语法
支持响应式
最佳实践
用于共享逻辑 - 工具函数、常量、配置
避免频繁更新 - module 级状态变化会影响所有实例
单例模式 - 确保全局唯一性
预加载数据 - SvelteKit 路由级数据加载
导出 API - 为组件提供外部可调用的接口
实例管理 - 追踪和协调多个实例
注意事项 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script context ="module" > export function utils ( ) {} let shared = 0 ; import { something } from "somewhere" ; </script >
决策树:何时使用哪种方案 1 2 3 4 5 6 7 8 9 10 11 需要在组件间共享逻辑? ├─ 是:逻辑是否与特定组件紧密相关? │ ├─ 是:需要访问组件实例吗? │ │ ├─ 是:使用 Module Script ✅ │ │ └─ 否:是否是纯工具函数? │ │ ├─ 是:使用独立JS文件 ✅ │ │ └─ 否:使用 Module Script ✅ │ └─ 否:跨多个不同组件复用吗? │ ├─ 是:使用独立JS文件 ✅ │ └─ 否:使用 Module Script ✅ └─ 否:使用普通 script 标签 ✅
实际建议 使用独立 JS 文件的场景: ✅
纯工具函数 - 完全无状态的辅助函数
1 2 3 export function formatDate (date ) {...}export function formatCurrency (amount ) {...}
跨项目复用 - 多个项目共享的逻辑
1 2 export function validate (data ) {...}
第三方集成 - API 客户端、SDK 封装
1 2 export class ApiClient {...}
配置文件 - 纯数据配置
1 2 3 export const API_BASE_URL = '...' ;export const ROUTES = {...};
使用 Module Script 的场景: ✅
组件级单例 - Modal、Tooltip 等需要协调的组件
实例管理 - 需要追踪所有组件实例
组件特定缓存 - 只在该组件使用的缓存策略
SvelteKit 预加载 - 路由级数据加载(必须)
组件 API 导出 - 暴露组件的控制接口
性能优化 - 需要在组件间共享的昂贵计算结果
组件插槽 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <div class ="card" > <header > <slot name ="header" > Default header</slot > </header > <main > <slot > Default content</slot > </main > <footer > <slot name ="footer" > Default footer</slot > </footer > </div > <Card > <h2 slot ="header" > Custom Header</h2 > <p > This is the main content</p > <button slot ="footer" > Action</button > </Card >
上下文 API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <script > import { setContext } from "svelte" ; import Child from "./Child.svelte" ; const theme = { primary : "#ff3e00" , secondary : "#676778" , }; setContext ("theme" , theme); </script > <Child /> <script > import { getContext } from "svelte" ; import GrandChild from "./GrandChild.svelte" ; const theme = getContext ("theme" ); </script > <div style ="color: {theme.primary}" > <p > Child component</p > <GrandChild /> </div > <script > import { getContext } from "svelte" ; const theme = getContext ("theme" ); </script > <div style ="background: {theme.secondary}" > <p > GrandChild component</p > </div >
动作(Actions) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <script > function longpress (node, duration ) { let timer; function handleMouseDown ( ) { timer = setTimeout (() => { node.dispatchEvent (new CustomEvent ("longpress" )); }, duration); } function handleMouseUp ( ) { clearTimeout (timer); } node.addEventListener ("mousedown" , handleMouseDown); node.addEventListener ("mouseup" , handleMouseUp); node.addEventListener ("mouseleave" , handleMouseUp); return { destroy ( ) { node.removeEventListener ("mousedown" , handleMouseDown); node.removeEventListener ("mouseup" , handleMouseUp); node.removeEventListener ("mouseleave" , handleMouseUp); }, }; } function handleLongPress ( ) { alert ("Long press detected!" ); } </script > <button use:longpress ="{500}" on:longpress ="{handleLongPress}" > Press and hold me </button >
过渡动画 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <script > import { fade, fly, scale } from 'svelte/transition' ; import { quintOut } from 'svelte/easing' ; let visible = true ; let items = ['Apple' , 'Banana' , 'Orange' ]; </script > <button on:click ={() => visible = !visible}> Toggle </button > {#if visible} <div transition:fade ={{ duration: 300 }} class ="box" > Fade transition </div > {/if} {#each items as item, i (item)} <div transition:fly ={{ y: 200 , duration: 300 , delay: i * 100 , easing: quintOut }} > {item} </div > {/each} <style > .box { width : 100px ; height : 100px ; background : #ff3e00 ; margin : 10px ; } </style >
性能优化与最佳实践 组件优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {#each items as item (item.id)} <ItemComponent {item } /> {/each} <script > let expensiveValue; $ : expensiveValue = expensiveCalculation (data); function expensiveCalculation (data ) { return data.reduce ((acc, item ) => acc + item.value , 0 ); } </script >
内存管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script > import { onDestroy } from "svelte" ; let interval; let count = 0 ; onMount (() => { interval = setInterval (() => { count++; }, 1000 ); }); onDestroy (() => { if (interval) { clearInterval (interval); } }); </script >
代码分割 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script > import { onMount } from "svelte" ; let HeavyComponent ; onMount (async () => { const module = await import ("./HeavyComponent.svelte" ); HeavyComponent = module .default ; }); </script > {#if HeavyComponent} <svelte:component this ="{HeavyComponent}" /> {/if}
虚拟 DOM 深度解析:Svelte vs Vue vs React 为什么 Svelte 不需要虚拟 DOM 编译时 vs 运行时 传统框架(React/Vue)的虚拟 DOM 流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 function Counter ( ) { const [count, setCount] = useState (0 ); return ( <div > <button onClick ={() => setCount(count + 1)}>Count: {count}</button > </div > ); }
Svelte 的编译时优化:
1 2 3 4 5 6 7 8 <script > let count = 0 ; </script > <button on:click ={() => count++}> Count: {count} </button >
编译后生成的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 function create_fragment (ctx ) { let button; let t0; let t1; let mounted; let dispose; return { c ( ) { button = element ("button" ); t0 = text ("Count: " ); t1 = text ( ctx[0 ]); }, m (target, anchor ) { insert (target, button, anchor); append (button, t0); append (button, t1); if (!mounted) { dispose = listen (button, "click" , ctx[1 ]); mounted = true ; } }, p (ctx, [dirty] ) { if (dirty & 1 ) { set_data (t1, ctx[0 ]); } }, d (detaching ) { if (detaching) detach (button); mounted = false ; dispose (); }, }; } function click_handler (event ) { count = count + 1 ; }
核心原理对比
特性
React/Vue
Svelte
处理时机
运行时
编译时
DOM 更新
虚拟 DOM + Diff
直接 DOM 操作
性能开销
框架运行时 + 虚拟 DOM
纯 JavaScript
包大小
包含框架代码
仅业务代码
更新策略
批量更新
精确更新
Svelte 的精确更新机制 编译时分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script > let name = "World" ; let count = 0 ; let items = ["a" , "b" , "c" ]; </script > <h1 > Hello {name}!</h1 > <p > Count: {count}</p > <ul > {#each items as item} <li > {item}</li > {/each} </ul >
编译时分析结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function update_name (ctx, dirty ) { if (dirty & 1 ) { set_data (t1, ctx[0 ]); } } function update_count (ctx, dirty ) { if (dirty & 2 ) { set_data (t3, ctx[1 ]); } }
响应式更新机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function make_dirty (component, i ) { if (component.$$ .dirty [0 ] === -1 ) { dirty_components.push (component); schedule_update (); component.$$ .dirty .fill (0 ); } component.$$ .dirty [(i / 31 ) | 0 ] |= 1 << i % 31 ; } function update (component ) { const dirty = component.$$ .dirty ; component.$$ .dirty = [-1 ]; if (dirty[0 ] & 1 ) { update_name (component, dirty); } if (dirty[0 ] & 2 ) { update_count (component, dirty); } }
性能对比分析 内存使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const ReactApp = ( ) => { const [data, setData] = useState (largeDataSet); return <DataList data ={data} /> ; }; <script > let data = largeDataSet; 如何直接更新DOM </script > ;
更新性能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const performanceTest = { react : () => { }, vue : () => { }, svelte : () => { }, };
总结
编译时优化
在构建时就分析出所有可能的更新路径
生成精确的更新代码,无需运行时计算
直接 DOM 操作
跳过虚拟 DOM 的创建和 Diff 过程
直接操作真实 DOM,减少中间层开销
精确更新
零运行时
不需要框架代码在浏览器中运行
生成的代码就是纯 JavaScript
更好的性能
Svelte 编译原理 编译器架构 Svelte 编译器的工作流程分为四个主要阶段:
1 源代码 → 解析(Parse) → 分析(Analyze) → 转换(Transform) → 生成(Generate) → 输出代码
Parse 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import { parse } from 'svelte/compiler' ;const source = ` <script> let count = 0; </script> <button on:click={() => count++}> Count: {count} </button> ` ;const ast = parse (source);console .log (ast);{ html : { type : 'Fragment' , children : [ { type : 'Element' , name : 'button' , attributes : [ { type : 'EventHandler' , name : 'click' , expression : { } } ], children : [ { type : 'Text' , data : 'Count: ' }, { type : 'MustacheTag' , expression : { } } ] } ] }, instance : { type : 'Script' , content : { } } }
Analyze 编译器会进行静态分析,建立依赖图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class DependencyAnalyzer { constructor (ast ) { this .variables = new Map (); this .dependencies = new Map (); } analyze ( ) { this .findVariables (); this .buildDependencyGraph (); this .markDOMDependencies (); } findVariables ( ) { this .variables .set ("count" , { kind : "let" , mutated : true , referenced_in_template : true , }); } buildDependencyGraph ( ) { this .dependencies .set ("doubled" , ["count" ]); } markDOMDependencies ( ) { return { count : [{ type : "text" , node : "button_text" }], }; } }
将 AST 转换为可执行的 JavaScript 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class ComponentTransformer { transform (ast, dependencies ) { return { create : this .generateCreateFunction (ast), update : this .generateUpdateFunction (dependencies), destroy : this .generateDestroyFunction (ast), }; } generateCreateFunction (ast ) { return ` function create_fragment(ctx) { let button; let t0, t1; return { c() { button = element("button"); t0 = text("Count: "); t1 = text(ctx[0]); // ctx[0] = count }, m(target, anchor) { insert(target, button, anchor); append(button, t0); append(button, t1); }, p(ctx, [dirty]) { if (dirty & 1) { // 检查count是否改变 set_data(t1, ctx[0]); } }, d(detaching) { if (detaching) detach(button); } }; } ` ; } }
Generate 最终生成的 JavaScript 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function instance ($$self, $$props, $$invalidate ) { let count = 0 ; const click_handler = ( ) => { $$invalidate(0 , count++, count); }; return [count, click_handler]; } class Component extends SvelteComponent { constructor (options ) { super (); init (this , options, instance, create_fragment, safe_not_equal, {}); } } export default Component ;
响应式编译原理 位运算优化 Svelte 使用位运算来追踪哪些变量发生了变化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const DIRTY_BITS = { count : 1 << 0 , name : 1 << 1 , age : 1 << 2 , email : 1 << 3 , }; let dirty = DIRTY_BITS .count | DIRTY_BITS .age ; if (dirty & DIRTY_BITS .count ) { } if (dirty & DIRTY_BITS .age ) { }
响应式系统底层实现 响应式变量的本质 1 2 3 4 5 6 7 8 9 10 11 12 let count = 0 ;count++; let count = 0 ;function increment ( ) { $$invalidate(0 , (count = count + 1 )); }
$$invalidate 函数原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function $$invalidate (index, value ) { component.$$ .ctx [index] = value; make_dirty (component, index); schedule_update (); return value; } function make_dirty (component, i ) { if (component.$$ .dirty [0 ] === -1 ) { dirty_components.push (component); schedule_update (); component.$$ .dirty .fill (0 ); } component.$$ .dirty [(i / 31 ) | 0 ] |= 1 << i % 31 ; }
更新调度机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 let update_scheduled = false ;const resolved_promise = Promise .resolve ();const dirty_components = [];function schedule_update ( ) { if (!update_scheduled) { update_scheduled = true ; resolved_promise.then (flush); } } function flush ( ) { const seen_callbacks = new Set (); do { while (dirty_components.length ) { const component = dirty_components.shift (); update (component); } } while (dirty_components.length ); update_scheduled = false ; } function update (component ) { if (component.$$ .fragment !== null ) { component.$$ .before_update .forEach (run_callback); const dirty = component.$$ .dirty ; component.$$ .dirty = [-1 ]; component.$$ .fragment .p (component.$$ .ctx , dirty); component.$$ .after_update .forEach (run_callback); } }
响应式语句的依赖追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let firstName = "John" ;let lastName = "Doe" ;$ : fullName = `${firstName} ${lastName} ` ;$ : greeting = `Hello, ${fullName} !` ;$$self.$$ .update = () => { if ($$self.$$ .dirty & 3 ) { $$invalidate(2 , (fullName = `${firstName} ${lastName} ` )); } if ($$self.$$ .dirty & 4 ) { $$invalidate(3 , (greeting = `Hello, ${fullName} !` )); } };
事件系统与 DOM 操作 事件委托优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function listen (node, event, handler, options ) { node.addEventListener (event, handler, options); return () => { node.removeEventListener (event, handler, options); }; } function preventDefault (fn ) { return function (event ) { event.preventDefault (); return fn.call (this , event); }; } function stopPropagation (fn ) { return function (event ) { event.stopPropagation (); return fn.call (this , event); }; } const dispose = listen (button, "click" , preventDefault (handleClick));
DOM 操作优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function insert (target, node, anchor ) { target.insertBefore (node, anchor || null ); } function detach (node ) { node.parentNode .removeChild (node); } function element (name ) { return document .createElement (name); } function text (data ) { return document .createTextNode (data); } function set_data (text, data ) { data = "" + data; if (text.data !== data) { text.data = data; } } function claim_element (nodes, name, attributes ) { for (let i = 0 ; i < nodes.length ; i++) { const node = nodes[i]; if (node.nodeName === name) { return node; } } return element (name); }
SSR 与水合(Hydration) 服务端渲染 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function render (component, options = {} ) { const result = component.render (options.props ); return { html : result.html , css : result.css , head : result.head , }; } class Component { static render (props ) { const { count } = props; return { html : ` <button> Count: ${count} </button> ` , css : { code : ".button { color: red; }" , map : null , }, }; } }
水合过程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function hydrate (component, options ) { const target = options.target ; const anchor = options.anchor ; const nodes = Array .from (target.childNodes ); component.$$ = { ...component.$$ , fragment : component.create_fragment (component.$$ .ctx ), }; component.$$ .fragment .l (nodes); component.$$ .fragment .m (target, anchor); flush (); } function claim_fragment (nodes ) { let i = 0 ; return { l (nodes ) { button = claim_element (nodes, "BUTTON" , {}); const button_nodes = children (button); t0 = claim_text (button_nodes, "Count: " ); t1 = claim_text (button_nodes, ctx[0 ]); button_nodes.forEach (detach); }, }; }
性能优化原理 编译时优化策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <div class ="static" > <p > Static content</p > </div>; const static_content = ` <div class="static"> <p>Static content</p> </div> ` ;$ : result = 2 + 3 ; if (false ) { console .log ("Never runs" ); }
运行时优化技巧 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 {#each items as item (item.id )} <Item {item} /> {/each} function update_each (changed, ctx ) { const items = ctx.items ; for (let i = 0 ; i < items.length ; i++) { const child_ctx = get_each_context (ctx, items, i); if (each_blocks[i]) { each_blocks[i].p (child_ctx, changed); } else { each_blocks[i] = create_each_block (child_ctx); each_blocks[i].c (); each_blocks[i].m (target, anchor); } } } let count = 0 ;const MAX = 100 ;
框架对比 React vs Vue vs Svelte 核心设计理念 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function App ( ) { const [state, setState] = useState (initial); return <View state ={state} /> ; } export default { data ( ) { return { state : initial }; }, template : `<View :state="state" />` } <script> let state = initial; </script> <View {state } />
更新机制对比
框架
检测变化
更新策略
性能特点
React
setState 触发
虚拟 DOM Diff
需要手动优化(memo, useMemo)
Vue
Proxy 拦截
依赖追踪 + 虚拟 DOM
自动收集依赖,精准更新
Svelte
编译时注入
直接 DOM 操作
最小化运行时,极致性能
内存管理对比 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class ReactComponent { render ( ) { return { type : 'div' , props : { children : [...] }, _owner : this , _store : {} }; } } const vueData = new Proxy ({}, { get (target, key ) { dep.depend (); return target[key]; } }); let count = 0 ;
状态管理进阶 复杂状态管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import { writable, derived } from "svelte/store" ;function createAppStore ( ) { const { subscribe, set, update } = writable ({ user : null , theme : "light" , locale : "en" , loading : false , error : null , }); return { subscribe, setUser : (user ) => update ((s ) => ({ ...s, user })), logout : () => update ((s ) => ({ ...s, user : null })), toggleTheme : () => update ((s ) => ({ ...s, theme : s.theme === "light" ? "dark" : "light" , })), setLoading : (loading ) => update ((s ) => ({ ...s, loading })), setError : (error ) => update ((s ) => ({ ...s, error })), clearError : () => update ((s ) => ({ ...s, error : null })), reset : () => set ({ user : null , theme : "light" , locale : "en" , loading : false , error : null , }), }; } export const appStore = createAppStore ();export const isAuthenticated = derived (appStore, ($app ) => $app.user !== null );export const isDarkMode = derived (appStore, ($app ) => $app.theme === "dark" );
中间件模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function createMiddleware (store ) { const { subscribe, set, update } = store; const logger = (action, state ) => { console .log ("[Store]" , action, state); }; const persist = (state ) => { localStorage .setItem ("app-state" , JSON .stringify (state)); }; return { subscribe, set : (value ) => { logger ("SET" , value); persist (value); set (value); }, update : (fn ) => { update ((state ) => { const newState = fn (state); logger ("UPDATE" , newState); persist (newState); return newState; }); }, }; } export const store = createMiddleware (writable ({}));
微前端与组件库开发 Web Components 集成 1 2 3 4 5 6 7 8 9 10 11 <svelte:options tag ="my-button" /> <script > export let variant = "primary" ; export let disabled = false ; </script > <button class ="btn {variant}" {disabled }> <slot /> </button >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 export default { plugins : [ svelte ({ compilerOptions : { customElement : true , }, }), ], };
组件库架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 export { default as Button } from './Button.svelte' ;export { default as Input } from './Input.svelte' ;export { default as Modal } from './Modal.svelte' ;export { default as Tabs } from './Tabs.svelte' ;export const theme = { colors : { primary : '#ff3e00' , secondary : '#676778' , success : '#40b883' , danger : '#e74c3c' }, spacing : { xs : '4px' , sm : '8px' , md : '16px' , lg : '24px' , xl : '32px' } }; <script > import { setContext } from 'svelte' ; import { writable } from 'svelte/store' ; export let theme; const themeStore = writable (theme); setContext ('theme' , themeStore); $ : themeStore.set (theme); </script > <slot />