Sapper中动态路由页面为什么在客户端首次加载时显示空白?

小斯羽 阅读 39

折腾了一下午也没解决,我在Sapper的动态路由里写了一个博客详情页,服务器端渲染正常,但直接访问客户端时页面内容全白。

代码是这样的:


<!-- routes/_posts/[slug].svelte -->
<script context="module">
  export async function load({ params }) {
    const post = await fetch(<code>api/posts/${params.slug}</code>).then(res => res.json())
    return { props: { post } }
  }
</script>

<script>
  export let post;
</script>

<h1>{post.title}</h1>
<p>{post.content}</p>

检查了网络请求发现客户端没有触发API调用,控制台也没报错。预渲染时页面能正确显示,但直接访问客户端URL就空白。尝试过加key属性和强制更新都没用,求大神指点!

我来解答 赞 11 收藏
二维码
手机扫码查看
2 条解答
西门海宇
你这代码少了一步关键操作——Sapper客户端运行时不会自动执行 load 函数,除非你在页面组件里显式调用它。标准写法是:在页面组件里用 export let page 接收路由参数,然后手动调用 preload(或者让 Sapper 帮你自动调用,但前提是你的组件结构得对)。

你现在的 load 写在 context="module" 里是对的,但 Sapper 要求页面组件本身得是默认导出的 Svelte 组件,而且不能漏掉 export let page,哪怕你没用到它——这是触发客户端 load 的关键开关。

改法很简单,把你的代码改成这样就行:

<!-- routes/_posts/[slug].svelte -->
<script context="module">
export async function load({ params }) {
const post = await fetch(api/posts/${params.slug}).then(res => res.json())
return { props: { post } }
}
</script>

<script>
export let page
export let post
</script>

<h1>{post.title}</h1>
<p>{post.content}</p>


注意 export let page 这一行不能省,哪怕你不操作它。这是 Sapper 客户端进入页面时判断是否需要执行 load 的依据之一,文档里叫“implicit preload trigger”。

另外顺带一提,你 API 路径里的反引号没闭合,api/posts/${params.slug} 应该写成 api/posts/${params.slug},不过这在问题描述里是被代码标签包裹的,估计是复制时的格式问题,不是你真正漏写的。

如果加了 export let page 还是空白,再检查下 fetch 返回的 post 是不是 null 或 undefined——Sapper 客户端不会自动 fallback,props 里要是没值,模板里直接访问 post.title 就会静默失败(不报错,但页面白屏)。可以加个兜底:

<script>
export let post

if (!post) {
// 可选:加个 loading 或 fallback 提示
console.warn('no post data loaded')
}
</script>


这问题我当年也踩过,以为是 SSR/CSR 差异,其实就差那一个 export let page
点赞 2
2026-02-25 16:06
司徒德丽
这个问题的核心是Sapper的动态路由在客户端首次加载时,服务端返回的JSON数据没有正确处理。直接用这个方案:

<script context="module">
export async function preload({ params }, { session }) {
const res = await this.fetch(<code>api/posts/${params.slug}</code>)
const post = await res.json()

if (res.status === 200) {
return { post }
} else {
this.error(res.status, 'Post not found')
}
}
</script>

<script>
export let post
</script>

{#if post}
<h1>{post.title}</h1>
<p>{post.content}</p>
{:else}
<p>Loading...</p>
{/if}


几点说明:第一,要用preload而不是load,这是Sapper的生命周期钩子,专门处理页面加载前的数据获取。第二,记得加条件渲染,防止数据未加载完成时报错。第三,用this.fetch替代原生fetch,它能保证服务端和客户端请求的一致性。

最后提醒一句,检查下你的api/posts/[slug]接口是不是同时支持GET请求,有时候只写了POST就会出问题。我之前就踩过这坑,调试了一晚上。
点赞 12
2026-02-14 15:13