Store状态管理 #
一、Store 概述 #
Store 是 Svelte 的跨组件状态管理方案,用于在组件树中共享状态。
1.1 为什么需要 Store #
text
┌─────────────────────────────────────────────────────────────┐
│ 组件通信问题 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Props 传递(深层嵌套) │
│ ┌─────────┐ │
│ │ App │ │
│ │ user │ │
│ └────┬────┘ │
│ ↓ props │
│ ┌─────────┐ │
│ │ Header │ │
│ └────┬────┘ │
│ ↓ props │
│ ┌─────────┐ │
│ │ Nav │ │
│ └────┬────┘ │
│ ↓ props │
│ ┌─────────┐ │
│ │ UserMenu│ ← 需要user │
│ └─────────┘ │
│ │
│ Store 解决方案 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ App │ │ Header │ │ UserMenu│ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ └───────────┴───────────┘ │
│ ↓ │
│ ┌─────────────┐ │
│ │ userStore │ ← 共享状态 │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 Store 契约 #
任何对象只要满足以下契约,就是有效的 Store:
javascript
const store = {
subscribe: (subscriber) => {
subscriber(currentValue);
return unsubscribeFunction;
}
};
二、Writable Store #
2.1 创建 Writable Store #
javascript
import { writable } from 'svelte/store';
const count = writable(0);
const user = writable({
name: 'Alice',
age: 25
});
2.2 使用 Store #
svelte
<script>
import { count } from './stores.js';
function increment() {
count.update(n => n + 1);
}
function setToTen() {
count.set(10);
}
</script>
<button onclick={increment}>
Count: {$count}
</button>
<button onclick={setToTen}>Set to 10</button>
2.3 Store 方法 #
| 方法 | 说明 |
|---|---|
subscribe(fn) |
订阅变化,返回取消订阅函数 |
set(value) |
设置新值 |
update(fn) |
基于当前值更新 |
2.4 自动订阅 ($语法) #
svelte
<script>
import { count } from './stores.js';
</script>
<p>{$count}</p>
<button onclick={() => count.update(n => n + 1)}>+</button>
2.5 完整示例 #
javascript
import { writable } from 'svelte/store';
export const todos = writable([]);
export function addTodo(text) {
todos.update(items => [...items, {
id: Date.now(),
text,
done: false
}]);
}
export function toggleTodo(id) {
todos.update(items => items.map(item =>
item.id === id ? { ...item, done: !item.done } : item
));
}
export function removeTodo(id) {
todos.update(items => items.filter(item => item.id !== id));
}
svelte
<script>
import { todos, addTodo, toggleTodo, removeTodo } from './stores.js';
let newTodo = '';
function handleSubmit(e) {
e.preventDefault();
if (newTodo.trim()) {
addTodo(newTodo.trim());
newTodo = '';
}
}
</script>
<form onsubmit={handleSubmit}>
<input bind:value={newTodo} placeholder="Add todo" />
<button type="submit">Add</button>
</form>
<ul>
{#each $todos as todo}
<li class:done={todo.done}>
<input
type="checkbox"
checked={todo.done}
onchange={() => toggleTodo(todo.id)}
/>
<span>{todo.text}</span>
<button onclick={() => removeTodo(todo.id)}>×</button>
</li>
{/each}
</ul>
三、Readable Store #
3.1 创建 Readable Store #
javascript
import { readable } from 'svelte/store';
export const time = readable(new Date(), (set) => {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return () => clearInterval(interval);
});
3.2 使用示例 #
svelte
<script>
import { time } from './stores.js';
</script>
<p>当前时间: {$time.toLocaleTimeString()}</p>
3.3 只读配置 #
javascript
import { readable } from 'svelte/store';
export const config = readable({
apiUrl: 'https://api.example.com',
version: '1.0.0',
features: ['feature1', 'feature2']
});
四、Derived Store #
4.1 创建 Derived Store #
javascript
import { writable, derived } from 'svelte/store';
const count = writable(0);
export const doubled = derived(count, $count => $count * 2);
export const squared = derived(count, $count => $count * $count);
4.2 多源派生 #
javascript
import { writable, derived } from 'svelte/store';
const firstName = writable('John');
const lastName = writable('Doe');
export const fullName = derived(
[firstName, lastName],
([$firstName, $lastName]) => `${$firstName} ${$lastName}`
);
4.3 异步派生 #
javascript
import { writable, derived } from 'svelte/store';
const userId = writable(1);
export const user = derived(userId, async ($userId, set) => {
set({ loading: true });
try {
const response = await fetch(`/api/users/${$userId}`);
const data = await response.json();
set({ loading: false, data });
} catch (error) {
set({ loading: false, error: error.message });
}
});
4.4 完整购物车示例 #
javascript
import { writable, derived } from 'svelte/store';
export const cart = writable([]);
export const totalItems = derived(cart, $cart =>
$cart.reduce((sum, item) => sum + item.quantity, 0)
);
export const totalPrice = derived(cart, $cart =>
$cart.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
export const discount = derived(totalPrice, $totalPrice =>
$totalPrice > 100 ? $totalPrice * 0.1 : 0
);
export const finalPrice = derived(
[totalPrice, discount],
([$totalPrice, $discount]) => $totalPrice - $discount
);
export function addToCart(product) {
cart.update(items => {
const existing = items.find(i => i.id === product.id);
if (existing) {
return items.map(i =>
i.id === product.id
? { ...i, quantity: i.quantity + 1 }
: i
);
}
return [...items, { ...product, quantity: 1 }];
});
}
export function removeFromCart(id) {
cart.update(items => items.filter(i => i.id !== id));
}
export function updateQuantity(id, quantity) {
cart.update(items =>
items.map(i =>
i.id === id ? { ...i, quantity } : i
)
);
}
五、自定义 Store #
5.1 封装逻辑 #
javascript
import { writable } from 'svelte/store';
function createCount() {
const { subscribe, set, update } = writable(0);
return {
subscribe,
increment: () => update(n => n + 1),
decrement: () => update(n => n - 1),
reset: () => set(0)
};
}
export const count = createCount();
5.2 使用自定义 Store #
svelte
<script>
import { count } from './stores.js';
</script>
<p>{$count}</p>
<button onclick={() => count.increment()}>+</button>
<button onclick={() => count.decrement()}>-</button>
<button onclick={() => count.reset()}>Reset</button>
5.3 带持久化的 Store #
javascript
import { writable } from 'svelte/store';
function persistent(key, initialValue) {
const stored = localStorage.getItem(key);
const value = stored ? JSON.parse(stored) : initialValue;
const store = writable(value);
store.subscribe($value => {
localStorage.setItem(key, JSON.stringify($value));
});
return store;
}
export const todos = persistent('todos', []);
export const settings = persistent('settings', {
theme: 'light',
fontSize: 14
});
5.4 带历史记录的 Store #
javascript
import { writable, derived } from 'svelte/store';
function withHistory(initialValue, maxHistory = 10) {
const { subscribe, set, update } = writable(initialValue);
const history = writable([initialValue]);
const index = writable(0);
function push(value) {
history.update(h => {
const newIndex = h.length;
const newHistory = [...h.slice(0, newIndex), value].slice(-maxHistory);
return newHistory;
});
index.set(history.get().length - 1);
}
return {
subscribe,
set: (value) => {
set(value);
push(value);
},
update: (fn) => {
update(value => {
const newValue = fn(value);
push(newValue);
return newValue;
});
},
undo: () => {
index.update(i => Math.max(0, i - 1));
const h = history.get();
const i = index.get();
set(h[i]);
},
redo: () => {
index.update(i => Math.min(history.get().length - 1, i + 1));
const h = history.get();
const i = index.get();
set(h[i]);
},
canUndo: derived(index, $i => $i > 0),
canRedo: derived([history, index], ([$h, $i]) => $i < $h.length - 1)
};
}
六、Store 绑定 #
6.1 双向绑定 #
svelte
<script>
import { writable } from 'svelte/store';
const name = writable('World');
</script>
<input bind:value={$name} />
<p>Hello {$name}!</p>
6.2 表单绑定 #
svelte
<script>
import { writable } from 'svelte/store';
const form = writable({
email: '',
password: '',
remember: false
});
function handleSubmit() {
console.log($form);
}
</script>
<form onsubmit|preventDefault={handleSubmit}>
<input type="email" bind:value={$form.email} />
<input type="password" bind:value={$form.password} />
<label>
<input type="checkbox" bind:checked={$form.remember} />
Remember me
</label>
<button type="submit">Submit</button>
</form>
七、Store 模式 #
7.1 模块化 Store #
javascript
import { writable, derived } from 'svelte/store';
function createUserStore() {
const { subscribe, set, update } = writable(null);
return {
subscribe,
login: async (credentials) => {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials)
});
const user = await response.json();
set(user);
return user;
},
logout: async () => {
await fetch('/api/logout', { method: 'POST' });
set(null);
},
updateProfile: (data) => {
update(user => ({ ...user, ...data }));
}
};
}
export const user = createUserStore();
7.2 服务层模式 #
javascript
import { writable, derived } from 'svelte/store';
function createApiStore(endpoint) {
const data = writable(null);
const loading = writable(false);
const error = writable(null);
async function fetch(params = {}) {
loading.set(true);
error.set(null);
try {
const query = new URLSearchParams(params).toString();
const response = await fetch(`${endpoint}?${query}`);
const result = await response.json();
data.set(result);
} catch (e) {
error.set(e.message);
} finally {
loading.set(false);
}
}
return {
data: { subscribe: data.subscribe },
loading: { subscribe: loading.subscribe },
error: { subscribe: error.subscribe },
fetch
};
}
export const users = createApiStore('/api/users');
export const products = createApiStore('/api/products');
八、Svelte 5 Runes 与 Store #
8.1 使用 $state 替代简单 Store #
svelte
<script>
let count = $state(0);
let doubled = $derived(count * 2);
</script>
8.2 跨组件共享状态 #
javascript
export const counter = $state(0);
svelte
<script>
import { counter } from './state.js';
</script>
<button onclick={() => counter++}>
Count: {counter}
</button>
8.3 使用 $effect 同步 #
svelte
<script>
import { counter } from './state.js';
$effect(() => {
localStorage.setItem('counter', counter);
});
</script>
九、完整示例:主题切换 #
javascript
import { writable, derived } from 'svelte/store';
const THEME_KEY = 'app-theme';
function createThemeStore() {
const stored = localStorage.getItem(THEME_KEY) || 'light';
const { subscribe, set } = writable(stored);
function updateTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem(THEME_KEY, theme);
set(theme);
}
updateTheme(stored);
return {
subscribe,
toggle: () => {
let current;
const unsub = subscribe(t => current = t);
unsub();
updateTheme(current === 'light' ? 'dark' : 'light');
},
set: updateTheme
};
}
export const theme = createThemeStore();
export const isDark = derived(theme, $theme => $theme === 'dark');
svelte
<script>
import { theme, isDark } from './stores/theme.js';
</script>
<button onclick={() => theme.toggle()}>
{#if $isDark}
🌙 Dark
{:else}
☀️ Light
{/if}
</button>
十、总结 #
| Store 类型 | 用途 |
|---|---|
writable |
可读写状态 |
readable |
只读状态 |
derived |
派生状态 |
| 自定义 Store | 封装业务逻辑 |
Store 使用要点:
- 使用
$语法自动订阅 - 在
onDestroy中手动取消订阅 - 派生 Store 自动追踪依赖
- 自定义 Store 封装业务逻辑
- 合理拆分 Store 模块
最后更新:2026-03-28