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
相关产品推荐
相关产品推荐

