SvelteKit{#await}加载状态预览模式失效及Safari兼容问题求助
在SvelteKit中使用{#await}实现加载状态的问题解决
问题描述
刚开始学Svelte,想用{#await}指令模拟服务器数据加载状态,预期页面先显示“Loading data...”,5秒后显示“Hello World”,但实际出现以下问题:
- 执行
npm run preview时,浏览器要等Promise完成才渲染页面; npm run dev模式下只有Chrome能正常显示加载状态,Safari不管哪种模式都不行;- 两种模式下
console.log(data.message)都显示服务器返回的是Promise。
当前使用SvelteKit 2.22。
代码示例
+page.svelte
<script lang="ts"> import type { PageData } from './$types'; let { data }: { data: PageData } = $props(); console.log(data.message); </script> <h1>Await Test</h1> {#await data.message} <p>Loading data...</p> {:then result} <p>{result}</p> {:catch error} <p>Error: {error.message}</p> {/await}
+page.server.ts
import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async () => { const promise = new Promise<string>(async (resolve) => { await new Promise((r) => setTimeout(r, 5000)); resolve('Hello World'); }); return { message: promise }; };
问题原因
SvelteKit在生产构建(preview对应生产产物)时,会自动解析load函数返回的Promise,等它完成后再把数据传给客户端——这是为了优化首屏渲染的SEO和性能,避免客户端再等异步数据。而开发模式为了调试方便,会保留Promise传递,让客户端处理异步状态。Safari的表现差异则是因为它对Promise序列化的处理和Chrome不同。
正确实现方式
要在客户端显示加载状态,得在客户端发起异步请求,不能在服务器load函数里返回未解析的Promise,以下两种方法都可行:
方法1:组件内发起异步请求
修改服务器load函数返回占位数据,组件挂载后再发起异步请求:
// +page.server.ts import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async () => { return { message: null // 返回占位值 }; };
<!-- +page.svelte --> <script lang="ts"> import type { PageData } from './$types'; import { onMount } from 'svelte'; let { data }: { data: PageData } = $props(); let messagePromise: Promise<string> | null = null; onMount(() => { // 客户端模拟异步请求延迟 messagePromise = new Promise<string>((resolve) => { setTimeout(() => { resolve('Hello World'); }, 5000); }); }); </script> <h1>Await Test</h1> {#await messagePromise} <p>Loading data...</p> {:then result} <p>{result}</p> {:catch error} <p>Error: {error.message}</p> {/await}
方法2:调用API路由获取数据
先创建API端点处理异步逻辑,再在组件内请求这个API:
// +server.ts(API端点) import type { RequestHandler } from './$types'; export const GET: RequestHandler = async () => { await new Promise((r) => setTimeout(r, 5000)); return new Response(JSON.stringify({ message: 'Hello World' }), { headers: { 'Content-Type': 'application/json' } }); };
<!-- +page.svelte --> <script lang="ts"> import { onMount } from 'svelte'; let messagePromise: Promise<string> | null = null; onMount(async () => { messagePromise = (async () => { const res = await fetch('/'); // 对应上面的+server.ts路由 const data = await res.json(); return data.message; })(); }); </script> <h1>Await Test</h1> {#await messagePromise} <p>Loading data...</p> {:then result} <p>{result}</p> {:catch error} <p>Error: {error.message}</p> {/await}
额外提示
- 生产模式下SvelteKit自动解析
load里的Promise是默认优化,不要强行绕过,否则会影响首屏性能和SEO; - 如果需要服务器端加载数据但又想显示加载状态,可以考虑用SvelteKit的懒加载组件或者路由级别的加载状态,但上面的两种方法更直接。
内容的提问来源于stack exchange,提问作者user31393851
相关产品推荐
相关产品推荐

