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

SvelteKit中@html渲染内容直接访问后消失问题求助

SvelteKit预渲染页面中{@html}渲染富文本闪显后消失的解决方法

问题场景

开发的SvelteKit应用从Hygraph加载富文本数据,出现以下异常:

  • 通过Header组件的导航链接(比如点击/about)跳转页面时,{@html data.content.text.html}渲染的富文本正常显示
  • 直接访问该URL或在页面硬刷新时,这部分富文本会闪显一秒后消失,但直接输出原始HTML字符串{data.content.text.html}却能正常显示

相关代码片段

Header组件导航部分:

<!-- lib/Header.svelte -->
<nav>
  <!-- ... -->
  <a href="/about">About</a>
  <!-- ... -->
</nav>

全局布局设置预渲染:

// routes/+layout.js
export const prerender = true;

页面数据加载逻辑:

// routes/about/+page.js
import { gql } from 'graphql-request'
import { client } from '$lib/graphql-client';
const query = `
  content(where: { slug: "about" }) {
    text {
      html
    }
  }
  biographies {
    text {
      html
    }
  }`;
/** @type {import('./$types').PageLoad} */
export const load = async () => {
  const { content, biographies } = await client.request(query)
  return { content, biographies }
}

页面渲染代码:

<!-- routes/about/+page.svelte -->
<script>
    export let data;
</script>
<!-- other markup -->
<div class="">
    <p class="text-justify">
        {@html data.content.text.html} <!-- 闪显后消失 -->
    </p>
    <p class="text-justify">
        {data.content.text.html} <!-- 正常显示 -->
    </p>
</div>
<!-- other markup -->

原因分析

核心问题出在预渲染(prerender)和客户端 hydration 的不匹配:
开启预渲染时,服务器会提前生成包含{@html}插入内容的静态HTML。但客户端加载页面时,Svelte会进行hydration(将静态DOM同步到虚拟DOM),如果Hygraph返回的HTML存在内联脚本、特殊标签,或者hydration过程中数据出现细微差异,Svelte会判定这部分DOM不属于虚拟DOM范畴,进而将其移除,导致内容消失。而直接输出HTML字符串时,它仅作为普通文本渲染,不会触发DOM节点对比逻辑,因此不会被移除。

解决方案

1. 排查Hygraph返回的HTML内容

先检查富文本的HTML结构:

  • 是否包含内联<script>、<style>标签?这类标签在预渲染和hydration阶段的处理逻辑可能冲突
  • 是否有嵌套过深的DOM结构,或者不符合HTML规范的标签?
    可以先将HTML简化为纯文本+基本标签(比如<p>、<strong>)测试,看是否还会出现消失的情况,逐步定位问题内容。

2. 单独禁用该页面的预渲染

如果全局预渲染是必须的,但该页面的动态内容无法兼容,可以在页面级别覆盖预渲染设置:

// routes/about/+page.js
export const prerender = false; // 覆盖全局配置

// 原有的load函数保持不变

这样页面会改为客户端渲染,避免预渲染和hydration的冲突。

3. 延迟渲染{@html}内容

利用Svelte的onMount钩子,等客户端hydration完成后再插入HTML:

<!-- routes/about/+page.svelte -->
<script>
    export let data;
    import { onMount } from 'svelte';
    let htmlContent = '';

    onMount(() => {
        // 客户端挂载完成后再赋值,避开hydration阶段
        htmlContent = data.content.text.html;
    });
</script>

<!-- other markup -->
<div class="">
    {#if htmlContent}
        <p class="text-justify">
            {@html htmlContent}
        </p>
    {/if}
    <p class="text-justify">
        {data.content.text.html}
    </p>
</div>
<!-- other markup -->

预渲染时这部分内容为空,客户端初始化完成后再渲染,不会被hydration逻辑干扰。

4. 确保数据结构稳定

在load函数中添加数据兜底,避免预渲染和客户端获取的数据出现差异:

// routes/about/+page.js
export const load = async () => {
  const { content, biographies } = await client.request(query)
  // 给数据添加默认值,防止hydration时出现undefined
  return { 
    content: content || { text: { html: '' } }, 
    biographies: biographies || [] 
  }
}

内容的提问来源于stack exchange,提问作者fekioh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 08:17:47