23.05-SvelteKit集成Hono

要点

  • SvelteKit 内置的 +page.server.ts+layout.server.ts 可以在服务端直接调用 Hono RPC
  • 客户端使用 Svelte 的 load 函数获取数据
  • SvelteKit 的表单 actions 可以替代传统的 API 调用
  • 配合 Svelte Query 可以实现类似 React Query 的效果

内容

1. SvelteKit 的数据加载模式

SvelteKit 有两种数据加载方式:

服务端加载(推荐):在 +page.server.ts 中获取数据,直接传递给页面组件

客户端加载:在浏览器中使用 fetch 或 Svelte Query

2. 服务端加载(推荐)

2.1 创建 RPC 客户端

// src/lib/api.ts
import { hc } from 'hono/client'
import type { AppType } from '@api/src/index'
 
export const api = hc<AppType>(import.meta.env.VITE_API_URL)

2.2 在 +page.server.ts 中使用

// src/routes/users/[id]/+page.server.ts
import type { PageServerLoad } from './$types'
import { api } from '$lib/api'
 
export const load: PageServerLoad = async ({ params }) => {
  const res = await api.api.users[':id'].$get({
    param: { id: params.id },
  })
 
  if (!res.ok) {
    throw new Error('Failed to fetch user')
  }
 
  return {
    user: await res.json(),
  }
}
<!-- src/routes/users/[id]/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types'
  export let data: PageData
</script>
 
<h1>{data.user.name}</h1>
<p>{data.user.email}</p>

3. 客户端加载

如果需要客户端加载,使用 Svelte 的 onMount

<!-- src/components/UserProfile.svelte -->
<script lang="ts">
  import { onMount } from 'svelte'
  import { api } from '$lib/api'
 
  export let userId: string
 
  let user: any = null
  let loading = true
  let error: Error | null = null
 
  onMount(async () => {
    try {
      const res = await api.api.users[':id'].$get({
        param: { id: userId },
      })
 
      if (!res.ok) {
        throw new Error('Failed to fetch')
      }
 
      user = await res.json()
    } catch (err) {
      error = err as Error
    } finally {
      loading = false
    }
  })
</script>
 
{#if loading}
  <div>加载中...</div>
{:else if error}
  <div>加载失败</div>
{:else if user}
  <h1>{user.name}</h1>
  <p>{user.email}</p>
{/if}

4. 使用 Svelte Query

安装 Svelte Query:

npm install @tanstack/svelte-query

配置:

// src/routes/+layout.ts
import { QueryClient } from '@tanstack/svelte-query'
 
export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5,
    },
  },
})
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { QueryClientProvider } from '@tanstack/svelte-query'
  import { queryClient } from './layout.ts'
</script>
 
<QueryClientProvider client={queryClient}>
  <slot />
</QueryClientProvider>

使用:

<!-- src/components/UserProfile.svelte -->
<script lang="ts">
  import { createQuery } from '@tanstack/svelte-query'
  import { api } from '$lib/api'
 
  export let userId: string
 
  const query = createQuery({
    queryKey: () => ['user', userId],
    queryFn: async () => {
      const res = await api.api.users[':id'].$get({
        param: { id: userId },
      })
      if (!res.ok) throw new Error('Failed to fetch')
      return res.json()
    },
  })
</script>
 
{#if $query.isLoading}
  <div>加载中...</div>
{:else if $query.error}
  <div>加载失败</div>
{:else if $query.data}
  <h1>{$query.data.name}</h1>
  <p>{$query.data.email}</p>
{/if}

5. 表单 Actions

SvelteKit 的表单 actions 可以替代传统的 API 调用:

// src/routes/chat/+page.server.ts
import type { Actions } from './$types'
import { api } from '$lib/api'
 
export const actions: Actions = {
  sendMessage: async ({ request }) => {
    const formData = await request.formData()
    const message = formData.get('message') as string
 
    const res = await api.api.chat.$post({
      json: { message },
    })
 
    if (!res.ok) {
      return { success: false, error: '发送失败' }
    }
 
    const data = await res.json()
    return { success: true, reply: data.reply }
  },
}
<!-- src/routes/chat/+page.svelte -->
<script lang="ts">
  import { enhance } from '$app/forms'
  import type { ActionData } from './$types'
  export let form: ActionData
</script>
 
<form method="POST" action="?/sendMessage" use:enhance>
  <input name="message" placeholder="输入消息" />
  <button type="submit">发送</button>
</form>
 
{#if form?.success}
  <p>回复:{form.reply}</p>
{:else if form?.error}
  <p>错误:{form.error}</p>
{/if}

6. 小结

SvelteKit 集成 Hono API 的关键点:

  1. 服务端加载(推荐):在 +page.server.ts 中调用 RPC,数据直接传递给页面
  2. 客户端加载:使用 onMount 或 Svelte Query
  3. 表单 Actions:SvelteKit 特有的表单处理方式
  4. 类型安全:RPC 客户端提供完整的类型推导

一句话带走:

SvelteKit 的服务端加载模式是最简洁的集成方式,数据在 +page.server.ts 中获取,直接传递给页面组件。