本节摘要:
createResource把异步请求声明为响应式资源:fetcher 是请求函数,来源信号决定何时重新请求,Suspense与ErrorBoundary分走加载与失败两态。本节对照 React 生态的两条老路——useEffect 手写三态与请求库接管——画出资源加载的完整时序,并交代与 store 组合的落地写法。
为什么 Solid 敢把"请求"做成一个内置原语?因为在细粒度响应式模型里,异步数据和普通信号没有本质区别:数据到了就写入,订阅它的绑定自动更新。createResource 只是把"何时发起、何时重发、请求中、失败了"这四件事也纳入了同一个模型。React 里这三态管理为什么长成一堆样板?因为加载状态本质上是"会随时间变化的组件状态",只能塞进 useState 加 useEffect 的循环里。
import { createSignal, createResource } from "solid-js"; import { Suspense } from "solid-js"; import { ErrorBoundary } from "solid-js"; function UserList() { const [keyword, setKeyword] = createSignal(""); // 来源信号变化 → 自动重新执行 fetcher;来源不变则结果被缓存复用 const [users, { refetch, loading }] = createResource(keyword, async (kw) => { const res = await fetch(`/api/users?q=${kw}`); if (!res.ok) throw new Error("加载失败"); return res.json(); }); return ( <> <input value={keyword()} onInput={e => setKeyword(e.currentTarget.value)} /> <button onClick={() => refetch()}>强制刷新</button> <ErrorBoundary fallback={<p>出了点问题,稍后再试</p>}> <Suspense fallback={<p>加载中…</p>}> <ul>{users()?.map(u => <li>{u.name}</li>)}</ul> </Suspense> </ErrorBoundary> </> ); }
逐个部位拆解。createResource 的首参是来源信号(可以省略,表示只请求一次);第二参 fetcher 拿到来源值发起请求,抛错即进入错误态。返回值读取端 users() 在请求中会挂起——所谓挂起,是让最近的 Suspense 边界显示 fallback;成功后返回数据;失败后由 ErrorBoundary 接住。refetch 强制重发,loading 是不想用 Suspense 时的手动判断。三态管理没有消失,但从"每处请求各写一遍"收敛为"声明一次,边界接管"。
对照 React 的两条老路。手写三态:useState 存数据、存 loading、存 error,useEffect 里发请求、写依赖数组、处理组件卸载后的 setState 警告——这套样板每个团队都封装过,封装质量参差。请求库路线(React Query、SWR):把三态、缓存、重试、失效全部接管,工程上成熟,代价是引入库心智与包体积,且 Suspense 模式在 React 里长期处于渐进适配状态。Solid 的 createResource 介于两者之间:内置、声明式、覆盖三态与缓存键,但重试策略、滚动恢复、分页缓存这些重活仍要自己长——或者交给社区绑定。公平地说:它解决的是"请求与响应式模型的对齐",不是"请求的全部工程学"。
资源读回来往往要进 store(比如列表页数据要支撑细粒度行更新)。标准组合:
import { createStore, reconcile } from "solid-js/store"; const [rows, setRows] = createStore([]); const [data] = createResource(() => { fetch("/api/rows").then(r => r.json()).then(json => { setRows(reconcile(json)); // 深度对齐:只广播真正变化的路径 }); }); // 界面直接消费 store,享受路径级更新 <For each={rows}>{row => <RowItem row={row} />}</For>
reconcile 在这里的角色值得强调(2.3 节讲过它的机制):刷新拿到全新 JSON,直接 setRows(json) 会让所有路径都判定变化;包一层 reconcile,值未变的字段保持安静。数据层与视图层的细粒度就此打通——资源负责"何时取",store 负责"怎么存",控制流负责"怎么摆"。
💡 迁移判据:React 项目里"React Query 的 useQuery 用得越深(乐观更新、离线缓存),迁移成本越高;只用了基础查询的,createResource 加一层薄封装即可对齐。别为对齐而对齐,先数数真正用到的特性。
数据层就绪。下一节盘点 React 时代的设计模式——哪些原样保留,哪些变形,哪些可以删除。