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

IntersectionObserver致SvelteKit预渲染失效,GHP动态路由404

问题分析

你的GitHub Pages静态站点中,/news/[newsId]动态路由出现以下问题:

  • 从/news页面点击链接进入/news/123正常
  • 直接访问/news/123返回Svelte客户端404

根源在于预渲染阶段内容未被正确生成:
预渲染时onMount不会执行,你的IntersectionObserver组件导致:

  1. isMounted初始为false,slot内容完全不渲染
  2. intersecting初始为false,即使slot渲染,新闻内容也会被{#if intersecting}隐藏
    Svelte Kit无法检测到动态路由下的实际内容,仅生成[newsId].html模板,而GitHub Pages作为静态主机无法将/news/123映射到模板文件,直接访问时返回fallback的index.html,客户端路由匹配失败显示404。
修改IntersectionObserver组件的关键点

1. 调整初始变量,保证预渲染时内容可见

修改组件内的变量初始化逻辑,让预渲染阶段默认显示内容:

<script lang="ts">
  import { onMount } from 'svelte';

  export let once = false;
  export let top = 0;
  export let bottom = 0;
  export let left = 0;
  export let right = 0;

  // 初始设为true,预渲染时直接显示内容
  let intersecting = true;
  let container: HTMLElement | null = null;
  // 初始设为true,预渲染时直接渲染slot
  let isMounted = true;

  onMount(() => {
    // 客户端挂载后重置状态,恢复监听逻辑
    intersecting = false;

    // --- 原有的IntersectionObserver和scroll监听代码保持不变 ---
    if (!!window && typeof window !== 'undefined' && typeof IntersectionObserver !== 'undefined') {
      const rootMargin = `${bottom}px ${left}px ${top}px ${right}px`;

      const observer = new IntersectionObserver(
        (entries) => {
          intersecting = entries[0].isIntersecting;
          if (intersecting && once && container) {
            observer.unobserve(container);
          }
        },
        { rootMargin }
      );

      if (container) observer.observe(container);
      return () => (container ? observer.unobserve(container) : undefined);
    }

    const handler = () => {
      if (!container) return;
      const bcr = container.getBoundingClientRect();
      intersecting =
        bcr.bottom + bottom > 0 &&
        bcr.right + right > 0 &&
        bcr.top - top < window.innerHeight &&
        bcr.left - left < window.innerWidth;

      if (intersecting && once) {
        window.removeEventListener('scroll', handler);
      }
    };

    window.addEventListener('scroll', handler);
    return () => window.removeEventListener('scroll', handler);
  });
</script>

2. 移除slot的渲染限制

原组件中{#if isMounted}的条件会阻止预渲染时的内容输出,直接改为:

<div bind:this={container}>
  <slot {intersecting} />
</div>

如果需要保留客户端挂载后的逻辑,也可以调整为:

<div bind:this={container}>
  {#if isMounted || typeof window === 'undefined'}
    <slot {intersecting} />
  {/if}
</div>
可选:强制预渲染所有新闻条目

在svelte.config.js中明确指定预渲染条目,确保所有新闻页面都被生成:

import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import { toKebabCase } from './src/lib/utils'; // 引入你的转义函数

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),

  kit: {
    adapter: adapter({
      pages: 'build',
      assets: 'build',
      fallback: 'index.html',
      precompress: false,
      strict: true
    }),
    prerender: {
      entries: async () => {
        // 导入新闻数据,生成所有新闻的路由
        const newsData = await import('./src/lib/data/news.json');
        const newsEntries = newsData.default.map(item => `/news/${item.id}-${toKebabCase(item.title)}`);
        // 加上新闻列表页
        return ['/news', ...newsEntries];
      }
    }
  }
};

export default config;

修改后,预渲染阶段会为每个新闻条目生成单独的静态HTML文件,GitHub Pages能直接响应/news/123的请求,客户端加载后IntersectionObserver仍能正常控制内容显示时机。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 10:47:08