SvelteKit页面加载问题:如何实现服务端完整加载后再渲染?
我发现SvelteKit中.js文件的load()函数以SSR方式执行,但即便使用.server.js,页面还是会在加载未完全完成时就渲染,出现异常动画。目前我通过onMount()设置isLoading状态,在页面完全加载前隐藏页面作为临时解决办法,代码如下:
// 加载遮罩 let isLoading = true onMount(() => { isLoading = false })
完整实现代码示例:
<script> // Svelte import {onMount} from "svelte" import moment from "moment" import axios from "axios" // 组件 import HomeHeader from "./home/HomeHeader.svelte" import HomeNewsMain from "./home/HomeNewsMain.svelte" import HomeNewsLatest from "./home/HomeNewsLatest.svelte" import HomeCards from "./home/HomeCards.svelte" import LoadingFull from "$lib/components/LoadingFull.svelte" import Skew_LightGray_White from "$lib/assets/Skew_LightGray_White.png" import {GmsStore} from "$lib/stores/GmsStore.js" // 导航栏 import {NavbarStore} from "$lib/stores/NavbarStore" NavbarStore.set({ ...$NavbarStore, currentTheme: "white", }) // 广告 import {AdsStore} from "$lib/stores/AdsStore.js" AdsStore.set(true) // 获取数据 export let data // 加载遮罩 let isLoading = true onMount(() => { isLoading = false }) </script> <svelte:head> <title >{$GmsStore?.title ? `${$GmsStore.title} - ` : ""}{$GmsStore?.header?.home.title ? $GmsStore?.header?.home.title : ""} </title> <meta name="description" content={$GmsStore?.header?.home.desc ? $GmsStore?.header?.home.desc : ""} /> </svelte:head> {#if !isLoading} <main> <HomeHeader /> <HomeNewsMain /> <HomeNewsLatest data={data.news} /> <img src={Skew_LightGray_White} class="w-full h-[120px]" /> <HomeCards /> </main> {:else} <LoadingFull /> {/if}
有没有更规范的处理方式?比如像PHP那样让服务端完成所有加载后再返回页面?
1. 统一在load函数中完成所有数据预取
SvelteKit的load函数(.js/.server.js)本身就是服务端执行、完成数据获取后再生成HTML的,但如果组件内部存在客户端侧独立数据请求(比如用axios/fetch拉取数据),服务端渲染的HTML只会包含骨架内容,客户端 hydration 时才会补全数据,进而导致异常动画。
正确的做法是把所有需要预渲染的数据请求都移到load函数中:
// +page.server.js export async function load({ fetch }) { // 将组件内的请求逻辑迁移到这里 const newsRes = await fetch('/api/news'); const news = await newsRes.json(); // 可添加更多数据请求,比如卡片数据 const cardsRes = await fetch('/api/cards'); const cards = await cardsRes.json(); return { news, cards }; }
服务端会将这些数据注入到页面HTML中,客户端直接通过export let data接收,无需重复请求。
2. 关闭流式渲染,实现全内容就绪后返回
SvelteKit默认开启流式渲染,即服务端会先返回部分HTML,再逐步发送剩余内容。如果要实现类似PHP的“全内容就绪后一次性返回”,可在load函数中关闭流式:
// +page.server.js export async function load({ fetch }) { const news = await fetchAndProcessNews(); // 自定义数据处理逻辑 const cards = await fetchAndProcessCards(); return { stream: false, // 关闭流式渲染 news, cards }; }
设置后服务端会等待所有数据处理完成,生成完整HTML后再返回给浏览器,避免提前渲染部分内容。
3. 优化客户端Hydration的加载状态控制
如果必须保留部分客户端侧逻辑,不要用onMount控制加载状态——onMount在hydration完成后才执行,此时页面已开始渲染。可结合SvelteKit的browser变量判断环境,配合数据就绪状态控制:
import { browser } from '$app/environment'; export let data; // 仅在客户端且数据未就绪时显示加载 let isLoading = browser && !data;
不过这种情况仍优先推荐将数据请求迁移到load函数。
4. 使用内置+loading.svelte组件(页面切换场景)
如果是页面切换时的加载需求,可在对应路由目录下创建+loading.svelte组件,SvelteKit会自动在load函数执行期间显示该组件,无需手动管理isLoading状态。
内容的提问来源于stack exchange,提问作者ioiofadhil

