news 2026/9/12 14:08:35

SpacetimeDB Angular 快速入门:用 `spacetime dev --template angular-ts` 在 5 分钟内跑通前后端

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpacetimeDB Angular 快速入门:用 `spacetime dev --template angular-ts` 在 5 分钟内跑通前后端

SpacetimeDB Angular 快速入门:用spacetime dev --template angular-ts在 5 分钟内跑通前后端

【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB

本篇指南以 SpacetimeDB 官方 Angular 快速入门文档为骨架,结合当前仓库中的angular-ts模板源码,带你从零搭建一个「SpacetimeDB 服务端模块 + Angular 前端」的全栈应用。读完你将掌握:如何用一条命令完成本地服务器启动、模块发布、TypeScript 绑定生成与 Angular 开发服务器启动,如何编写表(table)与 reducer(reducer),如何用 SpacetimeDB CLI 直接调用 reducer、执行 SQL 查询和查看模块日志,以及模板中 Angular 连接层(injectSpacetimeDBinjectTableinjectReducer)的底层实现原理。

环境准备

开始前需要确保本机满足以下两个前提条件:

  • Node.js 18+:Angular 应用构建与运行所需运行时环境;
  • SpacetimeDB CLI:提供spacetime devspacetime publishspacetime callspacetime sqlspacetime logs等核心命令,负责初始化模板、发布模块并与本地/云端数据库交互。

两个条件满足后,即可进入下一步创建项目。

创建项目:一条命令完成全链路启动

在任意空目录中执行:

spacetime dev --template angular-ts

这条命令会替你依次完成四件事:

  1. 启动本地 SpacetimeDB 服务器
  2. 发布你的模块(把spacetimedb/目录下的服务端代码编译并发布到本地服务器);
  3. 生成 TypeScript 绑定(生成到src/module_bindings/目录);
  4. 启动 Angular 开发服务器ng serve,默认端口 4200)。

从 CLI 源码看,--template参数由 crates/cli/src/subcommands/dev.rs 解析:它支持「模板 ID 或 GitHub 仓库(owner/repo或 URL)」两种形式,模板 ID 即本项目中的angular-ts。同时该文件注释明确要求所有服务端模板的代码必须放在spacetimedb/目录下,这正是模板目录结构如此设计的原因。

注意:如果当前目录已经存在 SpacetimeDB 项目,--template会被忽略(CLI 会打印Warning: --template option is ignored because a SpacetimeDB project already exists.),因此该命令应在全新目录中执行。

也可以手动初始化

如果你想更精细地控制初始化过程,也可以使用spacetime init命令手动选择模板:

spacetime init --template angular-ts

初始化完成后,再分别执行模块发布与前端启动:

spacetime publish --module-path spacetimedb --server local npm install npm run dev

模板的 package.json 中预设了这些常用脚本:

  • dev:运行node scripts/dev.mjs,先为前端生成本地环境配置,再启动ng serve
  • buildng build生产构建;
  • spacetime:publish:local:发布到本地服务器(--server local);
  • spacetime:publish:发布到主云服务器(--server maincloud);
  • spacetime:generate:用 CLI 重新生成 TypeScript 绑定(spacetime generate --lang typescript)。

打开应用

开发服务器启动后,浏览器访问http://localhost:4200即可看到运行中的应用。

页面会显示:

  • 连接状态(Connected / Disconnected,绿色/红色);
  • 一个输入框和「Add Person」按钮;
  • 一个「People」列表,实时展示person表中的所有行。

模板自带了一个已连接到 SpacetimeDB 的基础 Angular 应用,你可以在页面上直接添加名字并实时看到列表更新——这一切的背后是订阅机制在起作用,详见下文「Angular 前端如何与数据库联动」。

项目结构解析

模板初始化后,你的项目(以my-spacetime-app/为例)包含服务端与客户端两部分代码:

my-spacetime-app/ ├── spacetimedb/ # 你的 SpacetimeDB 模块 │ └── src/ │ └── index.ts # 服务端逻辑(表与 reducer 定义) ├── src/ # Angular 前端 │ └── app/ │ ├── app.component.ts │ ├── app.config.ts │ └── module_bindings/ # 自动生成的类型 ├── angular.json └── package.json

对照当前仓库中的真实模板(templates/angular-ts),完整的目录还要更丰富一些:

  • spacetimedb/src/index.ts:服务端模块入口,定义表与 reducer(下文详述);
  • src/app/app.component.ts:Angular 根组件,负责 UI 渲染与 reducer 调用;
  • src/app/app.config.ts:应用级配置,通过provideSpacetimeDB注入数据库连接;
  • src/environments/environment.ts:默认连接配置(主机与数据库名);
  • src/module_bindings/:自动生成的绑定层,包含tablesreducersDbConnectionSubscriptionBuilder等类型化 API;
  • scripts/dev.mjs:开发脚本,负责生成本地环境变量文件;
  • angular.jsonpackage.jsontsconfig*.json:Angular 工程配置。

一个值得注意的细节:src/module_bindings/下的文件是自动生成的,每个文件头部都有一行显式声明——THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.(此文件由 SpacetimeDB 自动生成,对它的修改不会被保存,请直接修改模块源码)。这意味着你不要手工编辑绑定文件,改表结构/ reducer 签名时只需修改spacetimedb/src/index.ts,然后重新生成绑定即可。

前后端各自的编辑入口

  • 服务端:编辑spacetimedb/src/index.ts,添加表(tables)和 reducer;
  • 客户端:编辑src/app/app.component.ts构建你的 UI。

理解表(table)与 reducer(reducer)

打开spacetimedb/src/index.ts,可以看到模块的核心代码(与仓库中 templates/angular-ts/spacetimedb/src/index.ts 一致):

import { schema, table, t } from 'spacetimedb/server'; const spacetimedb = schema({ person: table( { public: true }, { name: t.string(), } ), }); export default spacetimedb; export const init = spacetimedb.init(_ctx => { // Called when the module is initially published }); export const onConnect = spacetimedb.clientConnected(_ctx => { // Called every time a new client connects }); export const onDisconnect = spacetimedb.clientDisconnected(_ctx => { // Called every time a client disconnects }); 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!'); });

表:存储数据的地方

schema({...})定义模块的数据库模式。模板中定义了一张person表:

  • table({ public: true }, {...}):第一个参数是表配置,public: true表示该表对客户端公开(客户端可以订阅并读取);第二个参数是列定义,name: t.string()声明了一列字符串类型的name

表负责存储数据,是数据库的基本容器。

reducer:唯一的数据写入方式

spacetimedb.reducer(...)定义 reducer——一种服务端函数。Reducer 是修改数据的唯一方式,客户端不能直接写表,只能通过调用 reducer 间接写入。

模板提供了两个 reducer:

  • add:接收一个name参数,向person表插入一行。参数通过{ name: t.string() }进行类型声明,函数体内通过ctx.db.person.insert({ name })执行插入;
  • sayHello:遍历person表中的每一行,用console.info打印问候语。ctx.db.person.iter()返回迭代器,逐行访问所有数据。

此外,模板还展示了三个生命周期钩子(这在原文档中未展开,但仓库源码中确实存在):

  • spacetimedb.init(_ctx => {...}):模块首次发布时调用;
  • spacetimedb.clientConnected(_ctx => {...}):每次有新客户端连接时调用;
  • spacetimedb.clientDisconnected(_ctx => {...}):每次有客户端断开时调用。

你可以在这些钩子里编写初始化数据、在线状态广播等逻辑。

用 CLI 测试:调用 reducer、查询与看日志

打开一个新的终端,进入项目目录,用 SpacetimeDB CLI 直接与本地数据库交互:

cd my-spacetime-app # 调用 add reducer,插入一个名叫 Alice 的人 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!

四个命令逐一说明:

命令作用
spacetime call add Alice调用 reducer。位置参数按 reducer 声明顺序传入,Alicename的值;注意 reducer 名sayHello在 CLI 中写作蛇形命名say_hello
spacetime sql "SELECT * FROM person"用标准 SQL 查询表数据
spacetime call say_hello调用无参 reducer,触发遍历与日志输出
spacetime logs实时查看模块的console.info日志输出

这一轮操作完整验证了「写入 → 查询 → 计算 → 观测」的全链路:数据通过add写入,通过 SQL 读到,通过sayHello遍历并打日志,最终在logs中确认结果。

Angular 前端如何与数据库联动

模板的核心价值在于:Angular 前端与 SpacetimeDB 的连接层已经帮你写好了。打开 templates/angular-ts/src/app/app.config.ts,可以看到应用通过 Angular 依赖注入配置数据库连接:

import { ApplicationConfig, provideBrowserGlobalErrorListeners, } from '@angular/core'; import { provideSpacetimeDB } from 'spacetimedb/angular'; import { DbConnection, ErrorContext } from '../module_bindings'; import { Identity } from 'spacetimedb'; import { environment } from '../environments/environment'; const HOST = environment.SPACETIMEDB_HOST; const DB_NAME = environment.SPACETIMEDB_DB_NAME; const onConnect = (_conn: DbConnection, identity: Identity, token: string) => { localStorage.setItem('auth_token', token); console.log( 'Connected to SpacetimeDB with identity:', identity.toHexString() ); }; const onDisconnect = () => { console.log('Disconnected from SpacetimeDB'); }; const onConnectError = (_ctx: ErrorContext, err: Error) => { console.log('Error connecting to SpacetimeDB:', err); }; export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), provideSpacetimeDB( DbConnection.builder() .withUri(HOST) .withDatabaseName(DB_NAME) .withToken(localStorage.getItem('auth_token') || undefined) .onConnect(onConnect) .onDisconnect(onDisconnect) .onConnectError(onConnectError) ), ], };

关键点解读:

  • provideSpacetimeDB(...):来自spacetimedb/angular包,把连接注册为全局 provider;
  • DbConnection.builder():来自自动生成的绑定(templates/angular-ts/src/module_bindings/index.ts),是带模块类型信息的连接构建器;
  • .withUri(HOST).withDatabaseName(DB_NAME):指定服务器地址与数据库名;
  • .withToken(...):从localStorage读取auth_token实现会话恢复——首次连接成功后 token 会被存到localStorage(见onConnect),刷新页面后无需重新认证;
  • onConnect/onDisconnect/onConnectError:连接生命周期回调,分别处理连接成功、断开与失败。

根组件:响应式订阅与 reducer 调用

再看 templates/angular-ts/src/app/app.component.ts,UI 逻辑非常简洁,完全建立在 Angular 的响应式注入之上:

import { Component } from '@angular/core'; import { injectSpacetimeDB, injectTable, injectReducer, } from 'spacetimedb/angular'; import { tables, reducers } from '../module_bindings'; @Component({ selector: 'app-root', template: `...`, }) export class App { protected conn = injectSpacetimeDB(); protected people = injectTable(tables.person); private addReducer = injectReducer(reducers.add); protected name = ''; addPerson(event: Event) { event.preventDefault(); if (!this.name.trim() || !this.conn().isActive) return; // Call the add reducer this.addReducer({ name: this.name }); this.name = ''; } }

三个注入函数各司其职:

  • injectSpacetimeDB():返回连接状态信号(signal),conn().isActive表示当前是否已连接,UI 据此显示绿/红状态并禁用输入框与按钮;
  • injectTable(tables.person):订阅person表的变更,返回响应式查询结果,people().rows.lengthpeople().rows在模板中直接渲染;当表数据变化时,页面会自动更新——这正是 SpacetimeDB 订阅机制带来的实时能力;
  • injectReducer(reducers.add):返回一个可调用的 reducer 函数,this.addReducer({ name: this.name })即向服务端发起一次 reducer 调用。

自动生成的绑定层做了什么

在 templates/angular-ts/src/module_bindings/index.ts 中可以看到绑定的完整面貌:它从spacetimedb包导入底层运行时,再基于模块 schema 生成tablesSchemareducersSchemaproceduresSchema,最终导出:

  • tables:表引用集合,兼作查询构建器(query builder);
  • reducers:reducer 访问器映射;
  • DbConnection/DbConnectionBuilder:带模块类型信息的连接 API;
  • SubscriptionBuilder:订阅构建器;
  • 一系列类型定义(EventContextReducerEventContextErrorContext等)。

比如person表的行类型由 templates/angular-ts/src/module_bindings/person_table.ts 生成:

export default __t.row({ name: __t.string(), });

addreducer 的参数类型由 templates/angular-ts/src/module_bindings/add_reducer.ts 生成:

export default { name: __t.string(), };

这些文件是类型安全的关键:前端调用 reducer、读取表数据时都有完整的 TypeScript 类型推导,编译期即可捕获字段拼写错误与类型不匹配。

本地环境配置与scripts/dev.mjs的运行机制

Angular 的 esbuild 构建器不会像 Vite 那样自动读取.env文件,因此模板用 templates/angular-ts/scripts/dev.mjs 做了一个巧妙的桥接:

  1. 解析.env.local(如果存在)与process.env
  2. 按优先级解析两个关键变量:SPACETIMEDB_DB_NAME(数据库名)和SPACETIMEDB_HOST(服务器地址)——优先取spacetime dev注入的process.env,其次取.env.local中的VITE_SPACETIMEDB_DB_NAME/VITE_SPACETIMEDB_HOST,最后回退到 templates/angular-ts/src/environments/environment.ts 中的默认值(https://maincloud.spacetimedb.comangular-ts);
  3. 生成src/environments/environment.local.ts
  4. 通过 angular.json 中的fileReplacements配置,在 development 构建时用environment.local.ts替换默认的environment.ts
  5. 最后以npx ng serve启动 Angular 开发服务器。

默认配置中SPACETIMEDB_HOST指向https://maincloud.spacetimedb.com(主云服务器);而当你使用spacetime dev时,CLI 会注入本地服务器地址,连接目标自动切换为本地。手动执行npm run dev而不设置环境变量时,应用则会尝试连接主云服务器。

下一步进阶

  • 阅读 TypeScript SDK 参考文档,掌握spacetimedb包的完整 API(查询构建器、订阅、事务与错误处理等);
  • 尝试在spacetimedb/src/index.ts中添加新表(如message)、新 reducer(如sendMessage),然后重新运行spacetime generatespacetime dev自动刷新绑定;
  • person表改为非公开(public: false),观察前端订阅行为的变化,理解表可见性与权限控制;
  • 参考仓库中的其他 Angular 系模板(如 templates/react-ts、templates/nextjs-ts),对比不同框架接入 SpacetimeDB 的异同。

【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 14:08:18

基于Arnold与Logistic的MATLAB图像加解密及传输仿真解析

简介:基于MATLAB的图像加解密及传输仿真课程设计资源包,面向电子信息、通信工程、计算机等专业在校生,可用于课程设计、大作业或毕业设计选题。资源针对图像加密、解密及传输链路仿真需求,提供完整可运行的MATLAB源码、标准测试图…

作者头像 李华
网站建设 2026/9/12 14:04:34

Qt 5.14.2 aarch64静态交叉编译:从工具链到部署全攻略

干过嵌入式Linux开发的同行,多半都碰到过这个场景:交付给客户的ARM工控板上,系统裁剪得很干净,没有包管理器,甚至没有网络,却得跑一个带界面的Qt程序。这时候拿着动态链接的Qt往板子上一扔,缺库…

作者头像 李华