SvelteKit 渲染随机道具在服务器和客户端之间是不同的

SvelteKit Rendering a Random Prop is different between server and client

我想在 SvelteKit 中制作一个具有随机参数的组件。问题是当页面在服务器端呈现时与当该页面被水合时,此参数采用的值是不同的。

例如,考虑这个组件:

<script>
    export let t = Math.random() * 90
    export let l = Math.random() * 90
</script>

<div class="box" style="--t: {t}vh; --l: {l}vw;"></div>

<style>
    .box {
        position: fixed;
        top: var(--t); left: var(--l);
        width: 10vw; height: 10vh;
        background-color: black;
        transition: all 1s;
    }
</style>

当页面在服务器上呈现时,tl 取一些随机值,结果作为 HTML 返回给浏览器。但是,一旦页面变得水合,tl 就会呈现不同的值。结果,盒子移动了。

我不想让盒子移动;相反,我希望客户端也使用服务器返回的随机值,这样就不会突然改变样式。如果页面是通过页面内路由器导航的,一切都很好;当页面由服务器呈现时,方框就会移动。

如果我导出一个 load 函数,结果是一样的。 SvelteKit 有没有办法让服务器和客户端就随机值达成一致?

这里有几个选项:

  1. 在 onMount 中设置随机数,以便它们仅在客户端上设置。但是,这将导致 FOUC,因为该框不会被服务器渲染。
<script>
  import { onMount } from 'svelte';

  let t, l;

  onMount(() => {
    t = Math.random() * 90;
    l = Math.random() * 90;
  })
</script>

{#if t && l }
<div class="box" style="--t: {t}vh; --l: {l}vw;"></div>
{/if}

<style>
    .box {
        position: fixed;
        top: var(--t); left: var(--l);
        width: 10vw; height: 10vh;
        background-color: black;
        transition: all 1s;
    }
</style>
  1. 在加载函数中获取的服务器端点内生成随机数。由于 SvelteKit 缓存了加载中的提取结果,因此这应该在客户端和服务器上使用相同的随机数。
// random.json.js
export async function get() {
  return {
    body: {
      t: Math.random() * 90,
      l: Math.random() * 90,
    },
  };
}
<!-- index.svelte -->
<script context="module">
  export async function load({ fetch }) {
    // this will be cached, so it will be the same on client & server
    const result = await fetch('/random.json');
    const { t, l } = await result.json();
    return {
      props: {
        t, l
      }
    }
  }
</script>

<script>
    export let t;
    export let l;
</script>

<div class="box" style="--t: {t}vh; --l: {l}vw;"></div>

<style>
    .box {
        position: fixed;
        top: var(--t); left: var(--l);
        width: 10vw; height: 10vh;
        background-color: black;
        transition: all 1s;
    }
</style>

就我个人而言,我更喜欢第二种选择,因为没有 FOUC。

我认为您可以避免后端调用并使用可预测的伪随机数生成器。

例如; https://github.com/davidbau/seedrandom