在构建纯前端离线会员系统这件事上,核心目标十分明确:我们需要一个基于React Vite、SQL.js和IndexedDB的本地化解决方案,完整支持注册登录、角色权限控制以及自动登录态恢复能力。最关键的是,所有数据均存储在浏览器端,无需依赖任何后端服务。

明确会员系统核心功能与边界
需要明确的是,本系统并非企业级SaaS会员中台,而是一个完全在本地运行、支持注册登录、角色区分和基础权限控制的最小可用系统。所有数据都存放在本地SQLite中,不联网、不依赖服务器——这一精准定位从一开始就决定了后续每一步的技术选型与具体实现路径。
打开AI编程工具(例如AiPy或Codex配合GPT-5.3),第一条指令必须锁定技术路径:基于React Vite + SQLite Wasm + 本地模拟Clerk的技术栈,生成一个纯前端可离线运行的会员系统原型。不调用任何后端API,所有用户数据存入浏览器IndexedDB,使用SQL.js模拟SQLite操作。
这条指令实际上锁死了技术路径:规避Node.js的部署门槛,绕过真实认证流程,直接利用SQL.js在内存中运行数据库逻辑。如果跳过这一步,直接要求“做个会员系统”,AI很可能生成需要npm run dev、配置环境变量、启动Express服务的代码,没有后端基础的用户根本无法运行。
生成并验证基础结构文件
方法一:使用AiPy自动创建工程。在AiPy界面点击“新建项目”,选择“Web App”模板,在需求框中粘贴具体需求:单页应用,首页显示欢迎语和登录入口;登录后跳转会员主页,顶部显示当前用户名和退出按钮;未登录时所有会员专属内容不可见。点击生成,AiPy将在30秒内输出完整的文件树结构,包括src/App.jsx、src/main.jsx、vite.config.js、package.json,并自动执行npm install。看到终端出现“Project ready!”提示后,直接运行npm run dev即可。
方法二:手动补全关键文件。如果页面空白或报错“Cannot find module 'sql-wasm'”,说明AI遗漏了依赖安装。此时在终端执行npm install sql.js @sql.js/sql-wasm,然后打开src/main.jsx,在createRoot前插入import initSqlJs from '@sql.js/sql-wasm'; const SQL = await initSqlJs();——这行代码必须添加,否则后续所有数据库操作都会报undefined。
实现本地注册登录逻辑
第一步,在src/lib下新建auth.js。可以这样引导AI:在src/lib/auth.js中编写一个useAuth自定义Hook,包含login(username, password)和logout()两个函数。login函数需检查用户名是否已在IndexedDB的users表中存在,密码采用明文比对(仅用于本地演示)。登录成功后,将user对象存入localStorage,并触发React状态更新。
第二步,创建users表的初始化逻辑。在App.jsx顶部添加useEffect,首次加载时执行const db = new SQL.Database(); db.run("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT UNIQUE, password TEXT, role TEXT DEFAULT 'member')");——必须包含IF NOT EXISTS,否则每次刷新页面都会报"table users already exists"错误。
第三步,将登录态注入到组件树。使用React Context包装useAuth的返回值,确保任意子组件都能通过useContext(AuthContext)获取currentUser。这一步不可省略,否则Profile页无法显示"欢迎,张三"。
添加角色权限与页面守卫
现在会员主页需要区分普通会员和管理员视图。在AI指令中追加:为users表增加role字段,默认值为'member';在会员主页中,如果currentUser.role === 'admin',则显示“管理后台入口”按钮,点击跳转到/admin/users;否则隐藏该按钮。
生成/admin/users路由时,要求AI同时输出AdminGuard组件:使用Navigate强制重定向,当role !== 'admin'时跳回/403。需要注意——不要让AI生成真实的/admin路由文件夹,全部放在App.jsx内用嵌套路由实现,避免手动建目录时出错。
测试权限逻辑:使用Postman或curl向http://localhost:5173/api/register发送POST请求,body为{"username":"admin","password":"123","role":"admin"},然后使用该账号登录,确认管理按钮是否出现。
集成本地数据库持久化
关闭浏览器再打开,发现用户又变成未登录状态?这是因为localStorage没有与IndexedDB同步。需要修改login函数:登录成功后,不仅存储到localStorage,还要使用IDBKeyval库将user对象存入IndexedDB,键名为'currentUser'。
执行npm install idb-keyval,然后在auth.js顶部导入import { set, get } from 'idb-keyval';。在login函数末尾添加await set('currentUser', user);,在useAuth初始化时添加const savedUser = await get('currentUser');并赋值给state。
这一步完成之后,关闭页面再打开,只要未清除缓存,就能自动恢复登录态——这才是真正可用的本地会员系统雏形。
