SolidJS + Vite 构建 InstantDB 进阶应用:从安装部署到认证、实时查询、Presence 与多人群聊完整实战
2026/9/24 17:09:36 网站建设 项目流程
  • 后端
  • 数据库

【免费下载链接】instant

Instant is the best backend for AI-coded apps. You get auth, permissions, storage, presence, and streams — everything you need to ship apps your users will love.

项目地址:https://gitcode.com/gh_mirrors/inst/instant
点击查看免费下载

本文以仓库中的 SolidJS + Vite 进阶示例 为骨架,系统讲解一个基于 SolidJS、Vite 与 InstantDB 的完整实战项目:包括依赖安装、开发服务器、生产构建与静态部署流程,并深入源码剖析其认证、响应式数据查询、事务写入、在线 Presence、房间话题(Topics)聊天与打字指示器等进阶能力。读完本文,你将掌握如何从零跑通并理解一个"开箱即用的实时全栈"SolidJS 应用,以及每个核心能力对应的 InstantDB API 与代码实现位置。

一、示例项目一览与目录结构

solidjs-vite-advanced是仓库examples目录下基于 Solid CLI 生成的进阶示例,与基础版 examples/solidjs-vite 相比,它一次性演示了 InstantDB 的多数核心能力:

  • 邮箱魔法码(Magic Code)认证与登录状态机;
  • useQuery响应式查询与transact事务写入;
  • Rooms 房间机制下的 Presence(在线状态)与 Topics(实时消息);
  • 打字指示器、连接状态监控与本地设备 ID 等进阶 API。

完整目录结构如下:

examples/solidjs-vite-advanced/ ├── index.html # Vite 入口 HTML(挂载 #root) ├── package.json # 脚本与依赖定义 ├── vite.config.ts # Vite 配置(含 Tailwind v4 插件) ├── tsconfig.json # TypeScript 配置 ├── src/ │ ├── index.tsx # 应用入口(render + solid-devtools) │ ├── App.tsx # 根组件与认证状态机 │ ├── instant.schema.ts # 数据模型(entities / links / rooms) │ ├── instant.perms.ts # 权限规则(当前为空规则) │ ├── lib/db.ts # InstantDB 客户端初始化 │ └── components/ │ ├── Login.tsx # 魔法码登录 │ ├── StatusBar.tsx # 连接状态 / 本地 ID │ ├── PresenceBar.tsx # 在线成员列表 │ ├── TodoList.tsx # 响应式查询 + 事务 │ └── ChatPanel.tsx # 实时聊天 + 打字指示器

依赖方面,package.json 同时引入了@instantdb/core(数据建模与查询内核)、@instantdb/solidjs(SolidJS 适配层)与solid-js本体,构建工具链为 Vite 7 +vite-plugin-solid+ Tailwind CSS v4(通过@tailwindcss/vite插件接入),并启用了solid-devtools开发调试。其中@instantdb/solidjs@instantdb/core的源码分别位于 client/packages/solidjs 与 client/packages/core,是本文后续 API 讲解的底层依据。

二、环境准备与依赖安装

示例项目(README 及锁文件说明)默认使用 pnpm 维护依赖,这也是仓库中同时存在pnpm-lock.yaml的原因。README 明确说明:锁文件只是历史遗留,任何包管理器均可正常工作;一旦克隆模板后,该锁文件可以安全移除。

若依赖版本需要更新,README 给出的维护命令是:

pnpm up -Lri

其中-L表示升级到最新版本、-r表示递归处理工作区、-i为交互式选择模式。日常安装依赖则三种包管理器任选其一:

npm install # 或 pnpm install # 或 yarn install

安装完成后,还需要为本项目提供 InstantDB 应用 ID。客户端初始化代码 db.ts 通过import.meta.env.VITE_INSTANT_APP_ID读取该值,因此需要在项目根目录创建.env文件并写入:

VITE_INSTANT_APP_ID=你的应用ID

该值来自 Instant 控制台创建应用后生成的app-id,Vite 会将其注入到import.meta.env供客户端使用。

三、开发运行:dev 与热更新

README 与 package.json 中定义了四个脚本:

命令作用
npm run dev(或npm start启动 Vite 开发服务器,支持热更新
npm run build生产构建,产物输出到dist目录
npm run serve本地预览dist构建产物(vite preview

在项目目录下执行:

npm run dev

Vite 会启动开发服务器,浏览器打开http://localhost:3000即可看到应用。开发模式下对源码的编辑会即时反映到页面(热模块替换)。需要留意的是,index.tsx 在开发环境会校验#root元素是否存在——如果index.html中漏写了该挂载点或id拼写错误,会直接抛出 "Root element not found" 的明确报错。

另外,开发模式下入口还引入了solid-devtools(index.tsx),配合浏览器扩展可对 Solid 响应式系统进行组件树与信号调试。

四、生产构建与静态部署

执行:

npm run build

Vite 会以生产模式打包 Solid 应用:代码被压缩(minified)、产物文件名带内容哈希(hashes),并输出到项目根目录的dist文件夹。构建完成后,可用npm run serve(即vite preview)在本地验证产物行为。

README 明确指出部署方式:dist目录部署到任意静态托管服务即可(如 Netlify、Surge、Now 等主流静态平台)。由于应用是纯前端 + InstantDB 托管后端的架构,无需自建服务器,静态托管天然适合。这也是 InstantDB 类 BaaS 应用最常见的上线路径。

五、项目骨架:入口与认证状态机

应用入口 index.tsx 非常简单:引入全局样式、注册 solid-devtools、然后render(() => <App />, root)

根组件 App.tsx 展示了 SolidJS 处理异步认证状态的经典模式——用<Switch>/<Match>组合db.useAuth()返回的三种状态做分支渲染:

const auth = db.useAuth(); <Switch> <Match when={auth().isLoading}> <p>Loading...</p> </Match> <Match when={auth().error}> <p>Auth error: {auth().error?.message}</p> </Match> <Match when={auth().user}> <AuthenticatedApp /> </Match> <Match when={true}> <Login /> </Match> </Switch>

未登录时渲染<Login />;登录成功(auth().user存在)后进入AuthenticatedApp,展示用户邮箱、退出按钮,以及StatusBarPresenceBarTodoListChatPanel四个功能面板(App.tsx)。这种"信号驱动 + 模式匹配"的写法正是 SolidJS 细粒度响应式的直观体现。

六、初始化 InstantDB 客户端

所有功能的入口是 db.ts,它只做了三件事:

import { init, id, tx } from "@instantdb/solidjs"; import schema from "../instant.schema"; export { id, tx }; export const db = init({ appId: import.meta.env.VITE_INSTANT_APP_ID!, schema, useDateObjects: true, }); export const chatRoom = db.room("chat", "lobby");

要点解析:

  • init@instantdb/solidjs暴露的初始化函数,内部与@instantdb/core的客户端实现对接;
  • appId来自环境变量,!断言保证非空;
  • schema传入上一步定义的数据模型,使查询获得完整的 TypeScript 类型推导;
  • useDateObjects: true让时间字段以Date对象形式返回(默认可能为时间戳数字);
  • idtx是事务写入的辅助工具:id()生成新的随机实体 ID,tx.xxx[id()]构造写入语句;
  • db.room("chat", "lobby")声明一个房间引用——chat对应 schema 中定义的房间类型,lobby是具体房间名,后续 Presence 与 Topics 都基于它。

七、数据建模与权限规则

instant.schema.ts 使用@instantdb/corei.schema定义了完整的类型安全数据模型:

实体(entities)

const _schema = i.schema({ entities: { $files: i.entity({ path: i.string().unique().indexed(), url: i.string(), }), $users: i.entity({ email: i.string().unique().indexed().optional(), imageURL: i.string().optional(), type: i.string().optional(), }), todos: i.entity({ createdAt: i.number(), done: i.boolean(), text: i.string(), }), }, ... });

$files$users是 Instant 内建实体(文件与用户),todos是应用自定义实体。i.string().unique().indexed()这类链式约束会同时作用于服务端校验与本地类型;.optional()表示字段可缺省。

关联(links)

links: { $usersLinkedPrimaryUser: { forward: { on: "$users", has: "one", label: "linkedPrimaryUser", onDelete: "cascade" }, reverse: { on: "$users", has: "many", label: "linkedGuestUsers" }, }, },

这里定义了一条 $users 到自身的关联:正向linkedPrimaryUser(一对一,删除时级联),反向linkedGuestUsers(一对多)。

房间(rooms)

rooms: { chat: { presence: i.entity({ nickname: i.string(), color: i.string(), }), topics: { chat: i.entity({ message: i.string(), sender: i.string(), timestamp: i.number(), }), }, }, },

rooms.chat.presence描述加入该房间的用户携带的在线状态数据(昵称、颜色);rooms.chat.topics.chat描述该房间内名为chat的话题消息结构(内容、发送者、时间戳)。这与 PresenceBar.tsx 与 ChatPanel.tsx 的字段一一对应。

模型末尾通过type _AppSchema = typeof _schema; interface AppSchema extends _AppSchema {}导出AppSchema,为查询提供更友好的 IntelliSense 提示。

权限规则:instant.perms.ts 当前是空规则模板(satisfies InstantRules),文件内注释给出了典型写法示例,例如对posts实体允许所有人查看、仅拥有者可增删改:

posts: { allow: { view: "true", create: "isOwner", update: "isOwner", delete: "isOwner", }, bind: {"isOwner": "auth.id != null && auth.id == data.ownerId"}, },

权限规则在服务端强制执行,是生产环境必须补齐的一环。

八、实现邮箱魔法码认证

Login.tsx 完整演示了 InstantDB 的两步魔法码认证流程。

第一步:发送验证码。调用db.auth.sendMagicCode({ email }),成功后记录目标邮箱并切换界面(Login.tsx):

await db.auth.sendMagicCode({ email: email() }); setSentTo(email());

第二步:校验验证码登录。调用db.auth.signInWithMagicCode({ email, code })完成登录(Login.tsx):

await db.auth.signInWithMagicCode({ email: sentTo(), code: code(), });

两步都包裹在 try/catch 中,错误信息取自err.body?.message || err.message。界面用<Show when={sentTo()}>在两个表单(输入邮箱 → 输入验证码)间切换,并提供"Use a different email"返回重试。登录态一旦建立,根组件App.tsxauth().user信号会自动更新并切换到主界面,无需手动刷新。

九、响应式查询与事务写入:TodoList

TodoList.tsx 是 InstantDB 核心数据能力的浓缩演示。

响应式查询const state = db.useQuery({ todos: {} })返回一个 Solid 信号,state().data.todosstate().isLoadingstate().error会随远端数据自动更新(TodoList.tsx)。本地列表按createdAt升序排序后渲染。

事务写入:新增、切换完成态、删除分别对应三种transact用法(TodoList.tsx):

// 新增:id() 生成新 ID,update 写入字段 db.transact( tx.todos[id()].update({ text, done: false, createdAt: Date.now(), }), ); // 更新:按已有 ID 更新字段 db.transact(tx.todos[todoId].update({ done: !currentDone })); // 删除 db.transact(tx.todos[todoId].delete());

id()tx均从 db.ts 再导出,保证与客户端使用同一套底层实现。事务是离线优先的:即便网络瞬时不可用,写入也会在本地排队并在恢复后同步。

一次性查询:面板底部还演示了db.queryOnce({ todos: {} })——与useQuery的持续订阅不同,queryOnce只取一次快照并返回 Promise,适合按钮触发的一次性读取(TodoList.tsx)。

十、多人实时会话:Presence 在线状态

PresenceBar.tsx 展示如何让进入同一房间的客户端彼此感知。

同步自己的状态db.rooms.useSyncPresence(chatRoom, { nickname, color })把当前用户信息(邮箱前缀昵称 + 由邮箱哈希决定的头像颜色)发布到房间(PresenceBar.tsx)。

订阅他人状态db.rooms.usePresence(chatRoom, { keys: ["nickname", "color"] })返回peers对象,遍历后渲染为彩色昵称徽章;presence().isLoading用于加载态提示(PresenceBar.tsx)。所有进入chat/lobby房间的客户端都会实时出现在彼此的在线列表中,客户端离线后其 Presence 自动过期,无需手动清理。

十一、实时聊天:Topics 消息与打字指示器

ChatPanel.tsx 是房间机制最完整的用例,包含三个配合使用的 API。

发布消息(Topic)const publishChat = db.rooms.usePublishTopic(chatRoom, "chat")chat话题发布消息(ChatPanel.tsx)。值得注意的语义是Topics 不会回显给发送者自己,因此发送方需在本地追加自己的消息,代码注释与实现都明确标注了这一点(ChatPanel.tsx)。

订阅消息db.rooms.useTopicEffect(chatRoom, "chat", (event) => {...})注册回调,收到远端消息后追加到本地信号并滚动到底部(ChatPanel.tsx):

db.rooms.useTopicEffect(chatRoom, "chat", (event, _peer) => { setMessages((prev) => [...prev, event as ChatMessage]); scrollToBottom(); });

打字指示器db.rooms.useTypingIndicator(chatRoom, "chat-input", { timeout: 2000, stopOnEnter: true })监听输入框事件,自动向房间广播"正在输入"状态,timeout控制多久未输入后自动取消,stopOnEnter表示回车发送时立即停止(ChatPanel.tsx)。typing.active()返回正在输入的其他用户列表,配合typing.inputProps.onKeyDown/onBlur挂载到输入框,即可在界面下方显示"xxx is typing..."提示(ChatPanel.tsx)。

十二、连接状态与本地设备标识

StatusBar.tsx 展示了三个诊断类 API。

连接状态db.useConnectionStatus()返回实时连接状态,代码中定义了五种颜色映射(StatusBar.tsx):

状态值含义
connecting正在连接(黄色)
opened连接已建立(蓝色)
authenticated已认证(绿色)
closed连接已关闭(灰色)
errored连接出错(红色)

本地设备 IDdb.useLocalId("device")以信号形式提供持久化的本地标识(刷新后保持不变),同文件还演示了异步版本await db.getLocalId("device")(StatusBar.tsx)。

当前认证信息await db.getAuth()以 Promise 方式获取当前登录用户,与useAuth()的信号式 API 互补,适合在事件处理器中按需读取(StatusBar.tsx)。

十三、延伸学习与相关源码位置

  • 本示例使用的@instantdb/solidjs适配层源码位于 client/packages/solidjs,其底层查询、事务与建模内核在 client/packages/core,useQuerytransacti.schema等 API 的实现与测试都可在这两个包内找到;
  • 更基础、聚焦单一功能的对照示例可看 examples/solidjs-vite;
  • 权限规则的完整语法与绑定(bind)示例见 instant.perms.ts 内的注释说明。

结语

solidjs-vite-advanced虽然只是一个模板级示例,却覆盖了 InstantDB 生产应用的核心拼图:类型安全的数据建模、魔法码认证、响应式查询与离线优先事务、基于 Rooms 的 Presence 与 Topics 实时会话、打字指示器以及连接诊断 API。结合 README 的安装、开发、构建与静态部署四步流程,你可以直接以此为蓝本,在 SolidJS 生态中快速搭建带实时协作能力的完整应用。

  • 后端
  • 数据库

【免费下载链接】instant

Instant is the best backend for AI-coded apps. You get auth, permissions, storage, presence, and streams — everything you need to ship apps your users will love.

项目地址:https://gitcode.com/gh_mirrors/inst/instant
点击查看免费下载
上一篇:WeMod 2小时限制怎么破?Wand-Enhancer 免费解锁 Pro 功能(附完整 GitHub Actions 构建教程)
下一篇:QtScrcpy完整指南:免费的Android投屏控制软件,1分钟把手机镜像到电脑

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询