多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

使用 SpacetimeDB astro-ts 模板:5 分钟搭建 Astro SSR + 实时 WebSocket 应用

使用 SpacetimeDB astro-ts 模板:5 分钟搭建 Astro SSR + 实时 WebSocket 应用 使用 SpacetimeDB astro-ts 模板5 分钟搭建 Astro SSR 实时 WebSocket 应用【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB本文以 SpacetimeDB 官方仓库中的astro-ts模板templates/astro-ts为主线完整演示如何从零创建、运行并理解一个「服务端渲染快照 实时交互客户端」混合架构的 Astro 应用包括spacetime dev一键启动、table与reducer的模块编写、client:load水合、server:defer服务器岛屿以及用 SpacetimeDB CLI 直接调用 reducer 和查询数据。读完本文你将掌握把 SpacetimeDB 数据库接入任意 AstroTypeScript前端项目的完整实战路径。前置条件在开始之前需要准备两个基础环境Node.js 18Astro 6、React 18 及spacetimedbTypeScript SDK 均运行其上。SpacetimeDB CLI用于创建项目、发布模块、生成绑定和执行 SQL/调用 reducer。模板中的spacetime dev命令会在本地同时启动 SpacetimeDB 服务器、发布模块、生成 TypeScript 绑定并拉起 Astro 开发服务器因此 CLI 是整条链路的关键入口。CLI 的安装方式与版本要求可参见仓库内文档如 crates/cli/README.md以及spacetime --help。创建你的项目在任意目录执行spacetime dev --template astro-ts该命令会做四件事启动本地 SpacetimeDB 服务器将模板中的 SpacetimeDB 模块发布到本地服务器根据模块定义自动生成 TypeScript 绑定src/module_bindings/启动 Astro 开发服务器。以astro-ts为模板生成的项目其客户端package.json中预置了与本地开发强相关的脚本见 templates/astro-ts/package.json{ scripts: { dev: astro dev, build: astro build, preview: astro preview, start: node ./dist/server/entry.mjs, generate: pnpm --dir spacetimedb install cargo run -p gen-bindings -- --out-dir src/module_bindings --module-path spacetimedb prettier --write src/module_bindings, spacetime:generate: spacetime generate --lang typescript --out-dir src/module_bindings --module-path spacetimedb, spacetime:publish:local: spacetime publish --module-path spacetimedb --server local, spacetime:publish: spacetime publish --module-path spacetimedb --server maincloud } }其中值得注意的几点astro.config.mjs使用output: server与astrojs/node的standalone模式见 templates/astro-ts/astro.config.mjs这是模板同时支持 SSR 快照与服务器岛屿的配置基础模块目录spacetimedb/自身的package.json提供spacetime build与spacetime publish两个命令见 templates/astro-ts/spacetimedb/package.json说明模块代码独立于客户端构建spacetime:publish:local与spacetime:publish分别对应发布到本地服务器与 SpacetimeDB 主云maincloud可用于从开发环境切换到云端部署。打开你的应用启动成功后浏览器访问http://localhost:4321即可看到应用运行。模板在客户端读取环境变量时遵循「服务端优先、客户端兜底」的规则见 templates/astro-ts/src/env.d.ts 的类型声明环境变量作用域默认值SPACETIMEDB_HOST服务端SSR 快照ws://localhost:3000SPACETIMEDB_DB_NAME服务端SSR 快照astro-tsPUBLIC_SPACETIMEDB_HOST客户端浏览器水合ws://localhost:3000PUBLIC_SPACETIMEDB_DB_NAME客户端浏览器水合astro-ts因此根目录下的.env.local可以同时配置应用的两侧带PUBLIC_前缀的变量会被 Astro 暴露给浏览器端不带前缀的变量只在服务端渲染阶段生效。服务端连接代码见 templates/astro-ts/src/lib/spacetimedb-server.tsconst HOST import.meta.env.SPACETIMEDB_HOST ?? import.meta.env.PUBLIC_SPACETIMEDB_HOST ?? ws://localhost:3000; const DB_NAME import.meta.env.SPACETIMEDB_DB_NAME ?? import.meta.env.PUBLIC_SPACETIMEDB_DB_NAME ?? astro-ts;注意SPACETIMEDB_HOST/SPACETIMEDB_DB_NAME默认连接ws://localhost:3000这正是spacetime dev启动的本地服务器默认端口若修改了服务器端口或数据库名需同步调整环境变量。探索项目结构spacetime dev --template astro-ts生成的项目同时包含服务端模块代码与 Astro 客户端代码my-astro-app/ ├── spacetimedb/ # 你的 SpacetimeDB 模块 │ └── src/ │ └── index.ts # SpacetimeDB 模块逻辑表 reducer ├── src/ │ ├── components/ │ │ ├── PersonList.tsx # 实时列表组件订阅 调用 reducer │ │ ├── SpacetimeApp.tsx # React 水合入口client:load │ │ └── DeferredPeopleSnapshot.astro # server:defer 服务器岛屿 │ ├── lib/ │ │ └── spacetimedb-server.ts # SSR 阶段读取快照的服务端逻辑 │ ├── module_bindings/ # 自动生成的类型与绑定勿手改 │ ├── layouts/ │ │ └── Layout.astro │ ├── pages/ │ │ └── index.astro │ └── styles/ │ └── global.css └── package.json各部分的职责分工如下spacetimedb/src/index.ts模块源码定义数据表table与写入逻辑reducer是生成绑定和服务器行为的唯一事实来源src/module_bindings/由 CLI 自动生成的 TypeScript 绑定。仓库中该目录已包含index.ts、add_reducer.ts、person_table.ts、say_hello_reducer.ts、types.ts五个文件且文件头明确标注「自动生成、手改不生效」见 templates/astro-ts/src/module_bindings/add_reducer.ts应始终通过修改spacetimedb/src/index.ts后重新生成来变更src/lib/spacetimedb-server.ts仅在服务端运行用于在 SSR 阶段建立临时连接、订阅person表并返回初始快照src/components/*页面 UI 与实时交互组件。绑定层与模块的对应关系可以从生成的src/module_bindings/index.ts中看到它通过__schema/__reducers汇总person表与add、say_hello两个 reducer并导出tables、reducers、DbConnection、SubscriptionBuilder等类型安全 API见 templates/astro-ts/src/module_bindings/index.ts。理解表table与 reducer打开spacetimedb/src/index.ts查看模块代码完整实现见 templates/astro-ts/spacetimedb/src/index.tsimport { schema, table, t } from spacetimedb/server; const spacetimedb schema({ person: table( { public: true }, { name: t.string(), } ), }); export default spacetimedb; export const add spacetimedb.reducer( { name: t.string() }, (ctx, { name }) { ctx.db.person.insert({ name }); } ); export const sayHello spacetimedb.reducer(ctx { for (const person of ctx.db.person.iter()) { console.info(Hello, ${person.name}!); } console.info(Hello, World!); });表table用于存储数据通过table(options, columns)声明第一个参数{ public: true }将该表设为公开任何客户端都可以订阅并读取第二个参数定义列结构这里只有一个name: t.string()字符串列将表名作为schema({ ... })的键即生成person表。reducer是唯一允许写入数据库的函数其签名由参数对象与执行体组成add接收{ name: t.string() }参数通过ctx.db.person.insert({ name })插入一行sayHello不接收参数用ctx.db.person.iter()遍历表中所有人逐行输出日志最后再输出一句Hello, World!reducer 默认的命名转换规则是模块源码中驼峰命名的sayHello在 CLI 调用时对应下划线形式say_hello下文 CLI 测试会实际用到。此外模板模块还预置了三个生命周期钩子源码中均已给出但内容为空init模块首次发布时调用、onConnect每个新客户端连接时调用、onDisconnect客户端断开时调用可用于埋点、初始化种子数据等场景。对person表与addreducer 的绑定可以在生成的 templates/astro-ts/src/module_bindings/person_table.ts 与 templates/astro-ts/src/module_bindings/add_reducer.ts 中核对——它们把模块中的t.string()一一对应为客户端 SDK 的__t.string()类型。理解 Astro SSR 与实时水合的混合渲染模板的核心亮点是「混合渲染模型」关键代码分散在三处服务端首次渲染SSR 快照templates/astro-ts/src/pages/index.astro 的 frontmatter 在请求阶段直接await fetchPeople()拿到person表的初始数据并作为initialPeopleprop 传入页面保证首屏 HTML 就包含数据、不需要等待 WebSocket--- import { fetchPeople } from ../lib/spacetimedb-server; let initialPeople []; let initialLoadNotice: string | null null; try { initialPeople await fetchPeople(); } catch (error) { console.error(Failed to fetch initial data from SpacetimeDB:, error); initialLoadNotice The first server-rendered snapshot is unavailable right now. The live client will still connect when SpacetimeDB is ready.; } --- SpacetimeApp client:load initialPeople{initialPeople} /fetchPeople()的实现见 templates/astro-ts/src/lib/spacetimedb-server.ts做了三件关键的事用DbConnection.builder().withUri(HOST).withDatabaseName(DB_NAME)建立临时连接通过subscriptionBuilder().onApplied(...)订阅person表待订阅首次应用后取出conn.db.person.iter()数据、断开连接并resolve设置 10 秒超时与onConnectError回调连接失败时优雅降级——这正是index.astro中initialLoadNotice提示文案的来源。客户端水合client:loadtemplates/astro-ts/src/components/SpacetimeApp.tsx 在浏览器端以client:load立即水合创建持久化的连接构建器并用SpacetimeDBProvider包裹子树const HOST import.meta.env.PUBLIC_SPACETIMEDB_HOST ?? ws://localhost:3000; const DB_NAME import.meta.env.PUBLIC_SPACETIMEDB_DB_NAME ?? astro-ts; const TOKEN_KEY ${HOST}/${DB_NAME}/auth_token;该组件还演示了身份令牌的本地持久化连接成功后通过onConnect(_conn, identity, token)把 token 写入localStorage下次水合时用getStoredToken()恢复避免重复登录。实时订阅与写入templates/astro-ts/src/components/PersonList.tsx 使用spacetimedb/react提供的高层 HookuseSpacetimeDB()获取当前连接与连接状态useTable(tables.person)订阅person表返回[people, isLoading]表内任何增删都会实时反映到组件useReducer(reducers.add)拿到类型安全的addreducer 调用器表单提交时执行addPersonReducer({ name: trimmedName })。组件用isHydrated连接成功且订阅首次加载完成区分两种数据来源水合完成前展示initialPeople服务端快照水合完成后切换为people实时订阅数据const displayPeople isHydrated ? people : initialPeople;综合起来整个页面呈现「首屏服务端渲染 HTML 水合后 WebSocket 实时更新」的体验首次请求拿到的是渲染好的数据快照之后每次 reducer 写入都通过订阅通道实时推送无需刷新页面。理解 Astro 服务器岛屿server islands模板还额外演示了一个 Astro 特有的模式——服务器岛屿server:defer用来在不影响主实时客户端流程的前提下展示独立的服务端渲染快照templates/astro-ts/src/components/DeferredPeopleSnapshot.astro 是一个纯 Astro 组件frontmatter 中直接await fetchPeople()并在组件内渲染渲染时刻renderedAt与人员列表templates/astro-ts/src/pages/index.astro 通过DeferredPeopleSnapshot server:defer引入它同时用具名插槽提供fallback占位内容DeferredPeopleSnapshot server:defer div slotfallback classserver-island-fallback h3Fetching a fresh server snapshot./h3 p Astro has already rendered the page shell. This placeholder will be replaced with the server island response once the snapshot is ready. /p /div /DeferredPeopleSnapshot行为差异在于主页面index.astro在首次请求时同步获取快照属于首屏关键路径而server:defer的岛屿在页面外壳先渲染、随后由 Astro 通过独立请求获取并替换fallback插槽因此不会拖慢主页面首屏适合放置延迟加载也无妨的补充内容。在astro.config.mjs的output: server模式下这两种渲染路径页面级 SSR 与独立服务器岛屿请求都由astrojs/nodestandalone 适配器支撑。用 CLI 测试应用打开一个新终端并进入项目目录即可绕过 UI直接用 SpacetimeDB CLI 调用 reducer、查询数据、查看日志cd my-spacetime-app # 调用 add reducer 插入一个人 spacetime call add Alice # 查询 person 表 spacetime sql SELECT * FROM person name --------- Alice # 调用 sayHello 向所有人打招呼 spacetime call say_hello # 查看模块日志 spacetime logs 2025-01-13T12:00:00.000000Z INFO: Hello, Alice! 2025-01-13T12:00:00.000000Z INFO: Hello, World!几个实战要点reducer 名称转换模块里是驼峰sayHelloCLI 调用时使用下划线形式say_helloadd无需转换。参数传递spacetime call add Alice中Alice对应模块中{ name: t.string() }参数字符串参数直接以位置形式传入。SQL 查询spacetime sql走 SpacetimeDB 的 SQL 接口底层实现在仓库的 crates/sql-parser 与 crates/query 等 crate 中返回的是标准的表格化输出。日志查看sayHello中的console.info会出现在模块日志流中spacetime logs可实时查看。由于spacetime dev默认启动的是本地服务器上述命令无需额外指定--server参数若连接的是 maincloud则需要按 CLI 文档补充服务器参数。下一步参考仓库中的其他官方模板如 templates/chat-react-ts、templates/basic-ts对比不同框架接入 SpacetimeDB 的写法深入阅读 TypeScript SDK 的实现与测试sdks/typescript/src、sdks/typescript/tests掌握useTable、useReducer、订阅构建器与查询构建器的完整 API修改spacetimedb/src/index.ts添加更多表、索引、约束与 reducer 后运行pnpm spacetime:generate或spacetime generate重新生成客户端绑定体验改模块 → 生成绑定 → 前端即时获得类型安全 API的开发闭环需要部署到云端时使用pnpm spacetime:publish将模块发布到 maincloud并相应调整PUBLIC_SPACETIMEDB_HOST/PUBLIC_SPACETIMEDB_DB_NAME环境变量。【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表