概述
Solid Query 是 TanStack Query 的官方 SolidJS 适配器,可让你的 Web 应用轻松完成服务端状态(server state)的拉取、缓存、同步与更新。
设计初衷
SolidJS 作为一款快速、响应式且声明式的用户界面构建库,正日益流行。它开箱即用,内置大量功能。createSignal、createStore 等基础原语非常适合管理客户端状态(client state)。与其他 UI 库不同,SolidJS 对异步数据管理有明确的设计主张。createResource API 是 SolidJS 应用中处理服务端状态的优秀基础原语。resource 是一种特殊的信号(signal),可在数据处于加载状态时触发 Suspense 边界。
1import { createResource, ErrorBoundary, Suspense } from 'solid-js'2import { render } from 'solid-js/web'3 4function App() {5 const [repository] = createResource(async () => {6 const result = await fetch('https://api.github.com/repos/TanStack/query')7 if (!result.ok) throw new Error('Failed to fetch data')8 return result.json()9 })10 11 return (12 <div>13 <div>Static Content</div>14 {/* An error while fetching will be caught by the ErrorBoundary */}15 <ErrorBoundary fallback={<div>Something went wrong!</div>}>16 {/* Suspense will trigger a loading state while the data is being fetched */}17 <Suspense fallback={<div>Loading...</div>}>18 <div>{repository()?.updated_at}</div>19 </Suspense>20 </ErrorBoundary>21 </div>22 )23}24 25const root = document.getElementById('root')26 27render(() => <App />, root!)这非常棒!仅需几行代码,你就能从 API 拉取数据,并处理加载与错误状态。但随着应用复杂度提升,你需要更多功能来高效管理服务端状态。这是因为服务端状态与客户端状态截然不同。首先,服务端状态:
- 远程持久化于你无法控制或拥有的位置
- 需通过异步 API 进行拉取与更新
- 具有共享所有权特性,可能在你不知情的情况下被他人修改
- 若未加注意,你的应用中该状态可能变得“过期”
一旦你理解了应用中服务端状态的本质,随之而来的将是更多挑战,例如:
- 缓存……(编程中可能最难实现的功能之一)
- 将多个针对相同数据的请求去重为单个请求
- 在后台更新“过期”的数据
- 判定数据何时“过期”
- 尽快将数据更新反映到 UI 中
- 分页与数据懒加载等性能优化
- 服务端状态的内存管理与垃圾回收
- 使用结构共享对查询结果进行记忆化(memoizing)
此时,Solid Query 就派上用场了。该库封装了 createResource,提供了一组基础原语与工具,助你高效管理服务端状态。它开箱即用、零配置,且能随应用演进灵活定制。
从技术角度看,Solid Query 很可能:
- 帮你移除应用中大量复杂且易被误解的代码,代之以寥寥数行 Solid Query 逻辑
- 提升应用可维护性,使新增功能开发更轻松——你无需再操心如何接入新的服务端状态数据源
- 直接惠及终端用户:让应用体验更快、响应更灵敏
- 潜在地节省带宽并提升内存性能
少说多做,快让我看看代码!
下方示例展示了 Solid Query 最基础、最简洁的用法:拉取 TanStack Query GitHub 项目自身的 GitHub 统计信息:
1import { ErrorBoundary, Suspense } from 'solid-js'2import {3 useQuery,4 QueryClient,5 QueryClientProvider,6} from '@tanstack/solid-query'7 8function App() {9 const repositoryQuery = useQuery(() => ({10 queryKey: ['TanStack Query'],11 queryFn: async () => {12 const result = await fetch('https://api.github.com/repos/TanStack/query')13 if (!result.ok) throw new Error('Failed to fetch data')14 return result.json()15 },16 staleTime: 5 * 60 * 1000, // 5 minutes17 throwOnError: true, // Throw an error if the query fails18 }))19 20 return (21 <div>22 <div>Static Content</div>23 {/* An error while fetching will be caught by the ErrorBoundary */}24 <ErrorBoundary fallback={<div>Something went wrong!</div>}>25 {/* Suspense will trigger a loading state while the data is being fetched */}26 <Suspense fallback={<div>Loading...</div>}>27 {/* 28 The `data` property on a query is a SolidJS resource 29 so it will work with Suspense and transitions out of the box! 30 */}31 <div>{repositoryQuery.data?.updated_at}</div>32 </Suspense>33 </ErrorBoundary>34 </div>35 )36}37 38const root = document.getElementById('root')39const client = new QueryClient()40 41render(42 () => (43 <QueryClientProvider client={client}>44 <App />45 </QueryClientProvider>46 ),47 root!,48)嗯?这似乎比原来多写了好几行代码,却做着同样的事?
确实如此!但这几行代码解锁了一个全新的可能性世界。在上方示例中,你的查询被缓存 5 分钟;这意味着若应用中任意位置的新组件在 5 分钟内使用相同的查询,它将不再重新拉取数据,而是直接复用缓存数据。这只是 Solid Query 开箱即用的众多功能之一。其他功能还包括:
- 自动重新拉取(Automatic Refetching):当查询变为“过期”(依据
staleTime选项判定为过期)时,会在后台自动重新拉取 - 自动缓存(Automatic Caching):查询默认启用缓存,并在整个应用中共享
- 请求去重(Request Deduplication):多个组件可共享同一查询,仅发起一次请求
- 自动垃圾回收(Automatic Garbage Collection):当查询不再需要时,自动执行垃圾回收
- 窗口聚焦时重新拉取(Window Focus Refetching):应用重新获得焦点时,查询自动重新拉取
- 分页(Pagination):内置分页支持
- 请求取消(Request Cancellation):自动取消过时或不需要的请求
- 轮询 / 实时更新(Polling/Realtime):只需一个简单的
refetchInterval选项,即可轻松为查询添加轮询或实时更新能力 - SSR 支持(SSR Support):Solid Query 与服务端渲染(server-side rendering)完美兼容
- 乐观更新(Optimistic Updates):轻松通过乐观更新修改缓存
- 以及更多功能……