SvelteKit 将数据从服务器传递到浏览器

SvelteKit Pass Data From Server to Browser

我正在尝试将数据从服务器传递到客户端以更快地加载我的应用程序并防止多次调用数据库。

通过获取

SvelteKit 是通过 fetch 函数实现的。如果您有一个允许自定义获取的端点,那就太好了。但如果你不这样做呢?

Firebase 是没有自定义 fetch 函数的完美示例。

Cookies

我想我可以使用 cookie,但是当我设置 cookie 时,它​​只会打印 'undefined' 并且永远不会设置。

<script lang="ts" context="module">
    import Cookies from 'js-cookie';
    import { browser } from '$app/env';
    import { getResources } from '../modules/resource';

    export async function load() {

        if (browser) {

            // working code would use JSON.parse
            const c = Cookies.get('r');
            return {
                props: {
                    resources: c
                }
            };
        } else {

            // server
            const r = await getResources();

            // working code would use JSON.stringify
            Cookies.set('resources', r);

            // no cookies were set?
            console.log(Cookies.get());
            return {
                props: {
                    resources: r
                }
            };
        }
    }
</script>

所以我的代码加载正确,然后在加载浏览器 load 功能时消失...

确定有可行的方法来做到这一点吗?

J

您需要使用将 cookie 注入服务器响应的处理程序(因为加载函数不会向浏览器公开请求或 headers,我相信它们仅用于加载 props)。此处示例:https://github.com/sveltejs/kit/blob/59358960ff2c32d714c47957a2350f459b9ccba8/packages/kit/test/apps/basics/src/hooks.js#L42

https://kit.svelte.dev/docs/hooks#handle

export async function handle({ event, resolve }) {
    event.locals.user = await getUserInformation(event.request.headers.get('cookie'));

    const response = await resolve(event);
    response.headers.set('x-custom-header', 'potato');
    response.headers.append('set-cookie', 'name=SvelteKit; path=/; HttpOnly');


    return response;
}

仅供参考:此功能仅在 11 天前在 @sveltejs/kit@1.0.0-next.267 中添加:https://github.com/sveltejs/kit/pull/3631

无需使用 fetch!

你可以随心所欲地获取数据!

<script context="module">
    import db from '$/firebaseConfig'

    export async function load() {
        const eventref = db.ref('cats/whiskers');
        const snapshot = await eventref.once('value');
        const res = snapshot.val();

        return { props: { myData: res.data } } // return data under `props` key will be passed to component
    }
</script>

<script>
    export let myData //data gets injected into your component
</script>


<pre>{JSON.stringify(myData, null, 4)}</pre>

这是一个关于如何使用 axios 获取数据的快速演示,同样的原则适用于 firebase:https://stackblitz.com/edit/sveltejs-kit-template-default-bpr1uq?file=src/routes/index.svelte


如果您只想在服务器上加载数据,您应该使用“端点”(https://kit.svelte.dev/docs/routing#endpoints)

所以 Rich Harris 的 official 答案似乎是使用和休息 api 端点和获取。

routes/something.ts

import { getFirebaseDoc } from "../modules/posts";

export async function get() {
    return {
        body: await getFirebaseDoc()
    };
}

routes/content.svelte

export async function load({ fetch }) {
  const res = await fetch('/resources');
  if (res.ok) {
    return {
      props: { resources: await res.json() }
    };
  }
  return {
    status: res.status,
    error: new Error()
  };
}

当我谈到 here 时,这似乎是无关紧要的和有问题的,但它似乎也是唯一的方法。

J

我的解决方案可能会解决这个问题,特别是对于那些使用(例如:laravel_session)的人来说,实际上在你的情况下,如果你想在每个端点上加载时保留 cookie 数据。

你应该做的是创建一个接口来在每次 api() 调用时传递事件

interface ApiParams {
    method: string;
    event: RequestEvent<Record<string, string>>;
    resource?: string;
    data?: Record<string, unknown>;
}

现在我们需要修改默认的sveltekit api(),提供整个事件。

// localhost:3000/users
export const get: RequestHandler = async (event) => {
    const response = await api({method: 'get', resource: 'users', event});
    // ...
});

在你的 api() 函数中,设置你的 event.locals 但一定要更新你的 app.d.ts

// app.d.ts
declare namespace App {
    interface Locals {
        r: string;
    }
    //...
}

// api.ts
export async function api(params: ApiParams) {
    // ...
    params.event.locals.r = response.headers.get('r')
});

最后,更新您的 hooks.ts

/** @type {import('@sveltejs/kit').Handle} */
export const handle: Handle = async ({ event, resolve }) => {
    const cookies = cookie.parse(event.request.headers.get('cookie') || '');
    const response = await resolve(event);

    if (!cookies.whatevercookie && event.locals.r) {
        response.headers.set(
            'set-cookie',
            cookie.serialize('whatevercookie', event.locals.r, {
                path: '/',
                httpOnly: true
            })
        );
    }
    
    return response;
});

参考我的项目: