如何在Sapper应用中为每个路由仅请求一次GraphQL数据
解决Sapper页面GraphQL数据重复请求的方案
这个需求很合理——既要保证每个页面只获取自己需要的数据,又不想用户来回跳转时重复发起冗余请求。结合Sapper的preload特性和Svelte store,我们可以用一个页面数据缓存store来实现这个目标,具体步骤如下:
1. 创建页面数据缓存Store
首先我们需要一个专门的store来缓存各个页面的数据,它要能判断数据是否存在、读取缓存、写入缓存。创建stores/pageDataCache.js文件:
import { writable } from 'svelte/store'; function createPageDataCache() { const { subscribe, set, update } = writable({}); return { subscribe, // 同步获取指定页面的缓存数据 get: (pageKey) => { let currentCache; // 用立即执行的subscribe同步拿到当前store值 subscribe(cache => currentCache = cache)(); const entry = currentCache[pageKey]; // 可选:加过期判断,这里默认1小时过期 if (entry && entry.expires > Date.now()) { return entry.data; } // 过期就删除无效缓存 if (entry) { update(cache => { delete cache[pageKey]; return cache; }); } return null; }, // 写入缓存,支持设置过期时间(毫秒) set: (pageKey, data, ttl = 3600000) => { update(cache => ({ ...cache, [pageKey]: { data, expires: Date.now() + ttl } })); }, // 判断指定页面是否有有效缓存 has: (pageKey) => { return !!this.get(pageKey); } }; } export const pageDataCache = createPageDataCache();
2. 在路由preload中使用缓存
Sapper的preload函数会在页面加载前执行,而且在服务端和客户端都会运行。我们可以利用这一点,在客户端优先读取缓存,没有缓存时再请求API;服务端则直接请求(保证SSR能拿到最新数据,服务端的store是每个请求独立的,不会出现缓存共享问题)。
以routes/about.svelte为例:
<script context="module"> import { pageDataCache } from '../stores/pageDataCache'; // 假设这是你封装的GraphQL请求函数 import { fetchAboutPageData } from '../lib/graphql-client'; export async function preload(page) { // 用页面完整路径作为缓存key,比如'/about'或者'/blog/my-post'(动态路由也适用) const pageKey = page.path; // 客户端环境下先检查缓存 if (typeof window !== 'undefined') { const cachedData = pageDataCache.get(pageKey); if (cachedData) { return { pageData: cachedData }; } } // 没有缓存则发起API请求 const freshData = await fetchAboutPageData(); // 将新数据存入缓存 pageDataCache.set(pageKey, freshData); return { pageData: freshData }; } </script> <script> // 接收preload传递的数据 export let pageData; </script> <!-- 页面内容,直接使用pageData即可 --> <h1>{pageData.title}</h1> <div>{@html pageData.content}</div>
3. 动态路由的适配
如果是带参数的动态路由(比如/blog/[slug].svelte),直接用page.path作为缓存key就可以,因为它已经包含了参数(比如/blog/hello-world),不同参数的页面会自动对应不同的缓存条目,完全不需要额外处理。
关键注意点
- 服务端缓存逻辑:服务端的store是每个请求单独实例化的,所以服务端不会出现缓存复用的情况,每个请求都会重新获取最新数据,这符合SSR的正确性要求。
- 缓存过期机制:上面的store加入了默认1小时的过期时间,你可以根据业务需求调整TTL(比如设置为5分钟或者一天),避免用户看到太旧的数据。
- 缓存清理:如果需要主动清理某个页面的缓存(比如后台更新了页面内容),可以调用
pageDataCache.set(pageKey, null)来删除对应条目,下次访问时就会重新请求。
这样就能完美实现你的需求:每个页面只请求一次数据,用户重新访问时客户端复用缓存,服务端保证数据新鲜,同时不会出现冗余请求。
内容的提问来源于stack exchange,提问作者Clive Portman
相关产品推荐
相关产品推荐

