如何在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方法。
实际代码示例
还是以上面的博客详情页、关于页为例:
- 博客详情页路径为
/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>
- 关于页同理,在
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
相关产品推荐
相关产品推荐

