svas

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

svas — Svelte Async Stores

svas — Svelte 异步存储

Stores that fetch lazily, cache, revalidate, and persist. Every store emits one of three types:
ts
type Maybe<T, E extends Error = Error> = null | T | E
//   null  → no data yet (loading / cleared / not fetched)
//   T     → resolved value
//   E     → fetch error (errors are values, never thrown)
ts
import {
  value, values, collection,           // stores
  ok, ensure, having, awaited, once,   // guards & awaiting
  combined, sync, Async,               // compose & render
  type Maybe
} from 'svas'
这类存储支持延迟获取、缓存、重新验证与持久化功能。每个存储会输出以下三种类型之一:
ts
type Maybe<T, E extends Error = Error> = null | T | E
//   null  → 尚无数据(加载中 / 已清空 / 未获取)
//   T     → 已解析的值
//   E     → 获取错误(错误作为值返回,不会抛出)
ts
import {
  value, values, collection,           // stores
  ok, ensure, having, awaited, once,   // guards & awaiting
  combined, sync, Async,               // compose & render
  type Maybe
} from 'svas'

Shared lifecycle

通用生命周期

Every store fetches on first subscribe, then auto-revalidates. Common options:
OptionMeaning
get
fetcher returning
Promise<T | Error>
revalidate
ms before re-fetch (default
300_000
;
Infinity
disables)
persist
mirror into local storage under this key
session
use
sessionStorage
instead of
localStorage
stale
keep previous value visible while revalidating (default
false
)
bind
Readable<unknown | null>
— reset/clear store when bound store is
null
(tie data to a user)
default
initial value when nothing is persisted
每个存储会在首次订阅时获取数据,之后自动重新验证。通用配置项如下:
配置项说明
get
返回
Promise<T | Error>
的获取函数
revalidate
重新获取数据的间隔毫秒数(默认
300_000
;设为
Infinity
则禁用)
persist
将数据镜像存储到localStorage中,使用该配置项作为键名
session
使用
sessionStorage
替代
localStorage
stale
重新验证时保留之前的值可见(默认
false
bind
Readable<unknown | null>
— 当绑定的存储值为
null
时重置/清空当前存储(用于将数据与用户关联)
default
无持久化数据时的初始值

Stores

存储类型

All stores implements Readable interface from svelte/store.
Use
persist
whenever it makes sense to persist the data, it will vastly speed up the initial load. If store contains user-bound data, use
bind
to reset the store when the user is logged out.
所有存储都实现了svelte/store中的Readable接口。
只要适合持久化数据,就使用
persist
配置项,它能大幅提升初始加载速度。如果存储包含与用户绑定的数据,使用
bind
配置项在用户登出时重置存储。

value<T>
— one async value

value<T>
— 单个异步值

Writable<T | null>
. For single fetched values, auth tokens, transient UI flags.
ts
const me = value<User>({ get: () => api.me(), persist: 'user' })
const seen = value<number>({ persist: 'seen', default: 0 }) // transient/local-only
Methods:
set(v)
,
update(fn)
,
extract(): T | null
(sync read),
sync()
(revalidate if stale).
Writable<T | null>
类型。适用于单个获取的值、认证令牌、临时UI标记等场景。
ts
const me = value<User>({ get: () => api.me(), persist: 'user' })
const seen = value<number>({ persist: 'seen', default: 0 }) // 临时/仅本地存储
方法:
set(v)
update(fn)
extract(): T | null
(同步读取)、
sync()
(若数据过期则重新验证)。

values<T>
— keyed cache

values<T>
— 键值缓存

A map of independent
Readable<Maybe<T>>
lifecycles, one per key.
ts
const products = values<Product>({ get: (id) => api.product(id), stale: true, persist: 'products' })

const one = products.get('42')      // Readable<Maybe<Product>>, fetches if missing
products.set('42', next)            // write by key
products.extract('42')              // Product | null, sync
Methods:
get(key, { fetch? })
·
set(key, v, { stash? })
·
reset(key)
(restore stashed value) ·
extract(key)
·
delete(key)
·
clear()
.
Extra options:
permanent
(don't revalidate persisted entries on startup).
stash: true
on
set
remembers the prior persisted value so
reset(key)
can roll back — use for optimistic updates.
一个由独立
Readable<Maybe<T>>
生命周期组成的映射,每个键对应一个生命周期。
ts
const products = values<Product>({ get: (id) => api.product(id), stale: true, persist: 'products' })

const one = products.get('42')      // Readable<Maybe<Product>>,不存在时会获取数据
products.set('42', next)            // 按键写入数据
products.extract('42')              // Product | null,同步读取
方法:
get(key, { fetch? })
·
set(key, v, { stash? })
·
reset(key)
(恢复暂存的值)·
extract(key)
·
delete(key)
·
clear()
额外配置项:
permanent
(启动时不对持久化条目进行重新验证)。调用
set
时设置
stash: true
会记住之前的持久化值,以便
reset(key)
可以回滚——适用于乐观更新场景。

collection<T>
— list of
Identifiable
items

collection<T>
— 可识别项列表

Readable<Maybe<T[]>>
. Items must have
{ id: string }
. Pass a
values
store to also observe items individually via
get(id)
.
ts
const todos = collection<Todo>({
  get: () => api.todos(),
  values: values<Todo>(), // enables todos.get(id) / extract(id)
  stale: true, 
  persist: 'todos', 
  bind: session
})
Methods:
subscribe
·
add(item)
·
set(item, { add? })
(replace by id;
add
inserts if absent) ·
update(id, fn, opts?)
·
delete(id)
·
replace(items)
·
get(id)
/
extract(id)
(require
values
) ·
sync(): this
·
fetch(): Promise<this>
.
Mutations mirror into the
values
store, so components subscribed via
get(id)
update without re-fetching.
Readable<Maybe<T[]>>
类型。列表项必须包含
{ id: string }
属性。传入一个
values
存储后,还可以通过
get(id)
单独观察每个项。
ts
const todos = collection<Todo>({
  get: () => api.todos(),
  values: values<Todo>(), // 启用 todos.get(id) / extract(id)
  stale: true, 
  persist: 'todos', 
  bind: session
})
方法:
subscribe
·
add(item)
·
set(item, { add? })
(按id替换;若不存在则
add
会插入)·
update(id, fn, opts?)
·
delete(id)
·
replace(items)
·
get(id)
/
extract(id)
(需要传入
values
存储)·
sync(): this
·
fetch(): Promise<this>
变更会同步到
values
存储中,因此通过
get(id)
订阅的组件无需重新获取数据即可更新。

Guards & extraction

守卫与提取

Never use
?.
on a
Maybe<T>
— an
Error
has no domain properties. Narrow first.
ts
ok($store)          // boolean type guard: narrows Maybe<T> → T (excludes null | Error)
ensure(store)       // sync read, THROWS if null/Error — use when value must exist now (e.g. unsafe net requests in response to user action)
store.extract(key?) // sync read, T | null (ignores errors) — never throws
ts
if (ok($todos)) $todos.length        // typed as T
items.filter((i) => ok(i.account) && !i.account.deleted)   // ✅ guard before access
切勿在
Maybe<T>
类型上使用
?.
操作符——Error对象没有领域属性。请先进行类型收窄。
ts
ok($store)          // boolean类型守卫:将Maybe<T>收窄为T(排除null | Error)
ensure(store)       // 同步读取,若为null/Error则抛出错误——适用于当前必须存在值的场景(例如响应用户操作的非安全网络请求)
store.extract(key?) // 同步读取,返回T | null(忽略错误)——永远不会抛出错误
ts
if (ok($todos)) $todos.length        // 类型为T
items.filter((i) => ok(i.account) && !i.account.deleted)   // ✅ 访问前先进行守卫检查

Awaiting

等待处理

ts
await having(store)   // first non-null value; REJECTS on Error — services needing auth
await awaited(store)  // first non-null value; returns Error as a value (never rejects)
await once(store, (v) => v === 'ready')   // first value satisfying a condition
ts
await having(store)   // 获取第一个非null值;若遇到Error则拒绝——适用于需要认证的服务
await awaited(store)  // 获取第一个非null值;将Error作为值返回(永远不会拒绝)
await once(store, (v) => v === 'ready')   // 获取第一个满足条件的值

Compose & render

组合与渲染

<Async>
— render a
Maybe

<Async>
— 渲染Maybe类型

svelte
<Async store={todos}>
  {#snippet awaited(todos)}
    <!-- here todos are T[] -->
    {#each todos as t}<Row {t} />{/each}
  {/snippet}
  {#snippet waiting()}<Spinner />{/snippet}   <!-- optional; default loader otherwise -->
  {#snippet error(e)}<Err {e} />{/snippet}    <!-- optional; default error UI otherwise -->
</Async>
Props:
store
(required),
awaited
snippet (required), optional
waiting
/
error
snippets,
silent
(suppress default loader/error chrome). Combine with
combined
to await several at once.
svelte
<Async store={todos}>
  {#snippet awaited(todos)}
    <!-- 此处todos为T[]类型 -->
    {#each todos as t}<Row {t} />{/each}
  {/snippet}
  {#snippet waiting()}<Spinner />{/snippet}   <!-- 可选;否则使用默认加载器 -->
  {#snippet error(e)}<Err {e} />{/snippet}    <!-- 可选;否则使用默认错误UI -->
</Async>
属性:
store
(必填)、
awaited
代码片段(必填),可选的
waiting
/
error
代码片段,
silent
(禁用默认加载器/错误界面)。可与
combined
结合使用以同时等待多个存储。

combined(...stores)

combined(...stores)

One
Maybe
from many: tuple when all resolve, first
Error
, else
null
. Spreadable.
svelte
<Async store={combined(account, todos)}>
  {#snippet awaited([account, todos])}
    <!-- here account is U and todos is T[] -->
    <Profile {account} />
    {#each todos as t}<Row {t} />{/each}
  {/snippet}
</Async>
将多个存储合并为一个
Maybe
:当所有存储都解析完成时返回元组,若遇到第一个Error则返回该Error,否则返回null。支持展开操作。
svelte
<Async store={combined(account, todos)}>
  {#snippet awaited([account, todos])}
    <!-- 此处account为U类型,todos为T[]类型 -->
    <Profile {account} />
    {#each todos as t}<Row {t} />{/each}
  {/snippet}
</Async>

sync(store, item, { delete? })

sync(store, item, { delete? })

Conflict-free update.
T must implement interface Comparable { id: string; _version: number;_deleted?: number | null }
Merge a versioned item into a
collection
or
value
, respecting
_version
; removes on
_deleted
(unless
delete: false
). For applying server/realtime events.
ts
events.on('todos.sync', (todo) => sync(todos, todo))
无冲突更新。
T必须实现Comparable接口:{ id: string; _version: number;_deleted?: number | null }
将带版本号的项合并到
collection
value
存储中,遵循
_version
规则;当存在
_deleted
时移除该项(除非
delete: false
)。适用于应用服务器/实时事件场景。
ts
events.on('todos.sync', (todo) => sync(todos, todo))

Rules

规则

  • Errors are values — fetchers and services return
    T | Error
    ; check
    instanceof Error
    , never
    try/catch
    for expected failures.
  • Guard before access
    ok()
    /
    ensure()
    /
    extract()
    , never
    ?.
    on
    Maybe<T>
    .
  • One store per source — derive everything else; don't duplicate fetched data into local state.
  • Lazy by design — a store does nothing until subscribed;
    $store
    ,
    <Async>
    ,
    get
    ,
    having
    , or
    .subscribe
    triggers it.
  • 错误是值 — 获取函数和服务返回
    T | Error
    ;通过
    instanceof Error
    进行检查,切勿对预期的失败使用
    try/catch
  • 访问前先守卫 — 使用
    ok()
    /
    ensure()
    /
    extract()
    ,切勿在
    Maybe<T>
    上使用
    ?.
  • 每个数据源对应一个存储 — 其他所有数据都从该存储派生;不要将获取到的数据复制到本地状态中。
  • 设计为延迟加载 — 存储在被订阅前不会执行任何操作;
    $store
    <Async>
    get
    having
    .subscribe
    会触发它。

Advanced

进阶内容

For derived
Maybe
stores with live subscriptions/cleanup, realtime event wiring, linked/enriched entities, and optimistic update patterns, see references/patterns.md.
如需了解带有实时订阅/清理、实时事件连接、关联/增强实体以及乐观更新模式的派生
Maybe
存储,请查看references/patterns.md