SvelteKit SSG中如何实现全局卡牌数据的单次获取
问题
我正在使用SvelteKit和adapter-static搭建一款在线卡牌游戏的粉丝站点。由于站点围绕卡牌游戏展开,我需要让卡牌数据能在全站全局可用,这些数据可通过API获取。
我尝试过几种store方案但均未成功:
//错误:配置的目标环境不支持顶级await("chrome87", "edge88", "es2020", "firefox78", "safari14" + 2个覆盖项) export const cards = readable<CardData[]>(await fetch("https://some-api/cards/") .then(response => response.json()) .then(result => result["result"])); //同样无效 export const cards = writable<CardData[]>([]); async function fetchData() { let data = await fetch("https://some-api/cards/") .then(response => response.json()) .then(result => result["result"])); cards.set(data); } fetchData();
目前我在顶层+layout.server.ts中获取数据,并通过setContext存入上下文:
//src/+layout.server.ts import type { CardData } from "$lib/card"; export const prerender = true; export async function load({ fetch, url }) { const response = await fetch("https://some-api/cards/"); const result = (await response.json())["result"] as CardData[]; console.log(`fetch - ${url}`); return { cards: result }; }
<!-- src/+layout.svelte --> <script lang="ts"> import "../app.postcss"; import type { LayoutData } from "./$types"; import { setContext } from "svelte"; export let data: LayoutData; setContext("cards", data.cards); </script> ...
但查看日志后发现,数据会在每个页面都被请求一次:
fetch - http://sveltekit-prerender/releases/2022-10 fetch - http://sveltekit-prerender/about fetch - http://sveltekit-prerender/cards fetch - http://sveltekit-prerender/ fetch - http://sveltekit-prerender/card-usages fetch - http://sveltekit-prerender/cards/u318 ...
由于数据已包含所有必要信息,无需重复请求。我希望能仅获取一次并在全站全局使用。readable store方案仅在目标环境设为esnext时有效,我对此并不满意。请问在SSG模式下还有其他实现方式吗?
解决方案
1. 服务端全局缓存请求结果
在顶层+layout.server.ts中定义一个全局缓存变量,仅在首次加载时请求API,后续页面直接复用缓存数据:
// src/+layout.server.ts import type { CardData } from "$lib/card"; export const prerender = true; // 全局缓存,存储已获取的卡牌数据 let cachedCards: CardData[] | null = null; export async function load({ fetch }) { // 仅当缓存为空时才发起API请求 if (!cachedCards) { const response = await fetch("https://some-api/cards/"); cachedCards = (await response.json())["result"] as CardData[]; console.log("首次获取卡牌数据"); } return { cards: cachedCards }; }
这样不管多少页面渲染,API只会被调用一次,后续直接返回缓存内容,避免重复请求。
2. 构建阶段预获取数据(最适合SSG场景)
利用adapter-static的静态构建特性,在构建前提前拉取卡牌数据并写入本地文件,运行时直接导入使用,完全消除运行时API请求:
步骤1:创建预获取脚本
在项目根目录创建scripts/fetch-cards.js:
// scripts/fetch-cards.js import fs from 'fs/promises'; import fetch from 'node-fetch'; async function fetchAndSaveCards() { const response = await fetch("https://some-api/cards/"); const result = (await response.json())["result"]; // 将数据写入静态文件 await fs.writeFile('src/lib/cards-data.json', JSON.stringify(result)); console.log("卡牌数据已预获取并保存"); } fetchAndSaveCards();
步骤2:配置构建前置脚本
修改package.json,添加prebuild脚本,确保构建前先执行数据预获取:
{ "scripts": { "prebuild": "node scripts/fetch-cards.js", "build": "vite build" } }
步骤3:在代码中导入静态数据
修改+layout.server.ts,直接导入预生成的静态数据文件:
// src/+layout.server.ts import type { CardData } from "$lib/card"; import cardsData from "$lib/cards-data.json"; export const prerender = true; export async function load() { return { cards: cardsData as CardData[] }; }
这种方式完全在构建阶段完成数据获取,运行时无需任何API请求,性能最优,适合纯静态站点。
内容的提问来源于stack exchange,提问作者DVRP
相关产品推荐
相关产品推荐

