You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 03:15:31