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

如何在SvelteKit中通过SSR实现服务端向前端传递数据?

SvelteKit 服务端传数据的正确实现方式

核心认知纠正

你对handle函数的使用场景存在误解,完全不需要写巨型switch分支给不同页面处理数据。
handle是SvelteKit的全局服务端钩子,只适合处理全站统一逻辑:比如全局鉴权、注入公共用户信息、添加统一响应头、记录访问日志这类跨所有页面的通用操作,从来不是用来给单个页面传递业务数据的载体。

SvelteKit的设计和你之前用Express+Handlebars的开发逻辑是完全对齐的,只是把原来集中写在Express路由块里的「查数据→传模板渲染」逻辑,按文件路由拆分到了对应路径的独立文件里,天然做了逻辑隔离,不会出现所有逻辑堆在一起的问题。

和Express开发模式的对应关系

你之前写Express路由的逻辑大概是这样:

// Express 路由示例
app.get('/blog/:id', async (req, res) => {
  // 查业务数据
  const post = await db.post.findUnique({ where: { id: req.params.id } })
  // 传数据给模板引擎渲染
  res.render('blog/post', { post })
})

app.get('/about', async (req, res) => {
  const siteConfig = await db.config.findFirst()
  res.render('about', { siteConfig })
})

SvelteKit基于文件系统做路由映射,不需要你手动写路由匹配规则:

  • 路径对应的页面模板是同路径下的+page.svelte文件,等价于你之前的.hbs模板文件
  • 同路径下的+page.server.js/+page.server.ts文件,就是你之前写在Express路由里的服务端逻辑块,里面导出的load函数专门负责查当前页面需要的业务数据,返回值会自动被框架传递给对应的+page.svelte组件,不需要手动调用render方法。

实际代码示例

还是以上面的博客详情页、关于页为例:

  1. 博客详情页路径为/blog/[id],对应文件结构:
src/routes/
└── blog/
    └── [id]/
        ├── +page.svelte # 页面模板,等价于之前的post.hbs
        └── +page.server.js # 服务端数据逻辑,等价于Express里的/blog/:id路由处理块

在+page.server.js里写数据查询逻辑:

// 提前在公共位置初始化好数据库连接复用,不要每次请求新建
import db from '$lib/server/db'
import { error } from '@sveltejs/kit'

export async function load({ params }) {
  const post = await db.post.findUnique({ where: { id: params.id } })
  if (!post) throw error(404, '文章不存在')
  // 返回的数据会自动传给同目录下的+page.svelte
  return { post }
}

在+page.svelte里直接取用数据即可:

<script>
  export let data // 接收load函数返回的所有数据
</script>

<h1>{data.post.title}</h1>
<div class="content">{@html data.post.content}</div>
  1. 关于页同理,在src/routes/about/目录下建自己的+page.server.js和+page.svelte,独立写站点配置的查询逻辑,和其他页面完全不耦合。

handle 函数的正确用法

只有处理全站通用逻辑的时候才需要用到handle,举个最常见的全局注入登录用户的例子,写在src/hooks.server.js中:

export async function handle({ event, resolve }) {
  // 从cookie取会话信息,查询当前登录用户
  const sessionToken = event.cookies.get('session')
  if (sessionToken) {
    // 查到的用户存在event.locals上,所有页面的load函数都能直接访问
    event.locals.user = await getUserBySession(sessionToken)
  }
  // 继续执行后续路由匹配、页面load、渲染流程
  return resolve(event)
}

存在event.locals上的公共数据不需要每个页面重复查询,直接在各个页面的load函数里取了用就行。

适配旧开发习惯的几个小提示

  • 数据库连接、Redis连接这类全局资源,和Express项目里一样,在服务端公共目录初始化一次复用即可,不要在每个load函数里重复创建连接
  • 如果是布局层共用的数据(比如导航栏、页脚需要的数据),可以写到对应路径的+layout.server.js里,不用每个子页面重复查询
  • 整个数据传递流程和你之前用res.render传模板变量的逻辑没有本质区别,只是框架帮你完成了路由匹配、数据注入、渲染调度的重复工作,你不需要自己维护路由表和渲染调用逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 11:57:12