共计 10623 个字符,预计需要花费 27 分钟才能阅读完成。
目录
- 快速认识 Parse
- 安装与引入(NPM / CDN / ESM / CJS)
- 初始化与环境区分(Browser / Node / React / Next.js)
- 基础概念(Class、Object、Pointer、Relation、CLP、ACL、Role、Session)
- 数据读写(CRUD)
- 查询(Query)详解:条件、排序、分页、组合、聚合、distinct
- 关系建模与关联查询
- 用户体系与认证(注册 / 登录 / 匿名 / 第三方)
- 权限与安全(CLP、ACL、Pointer Permissions、useMasterKey、Session Token)
- 文件与图片(Parse.File)
- Cloud Code(云函数 / 触发器 / 定时任务)
- 实时功能(LiveQuery)
- 批量操作与数据迁移
- TypeScript 最佳实践
- 在 React / Next.js / Node 中使用
- 错误处理与调试
- 性能优化与运维建议
- 常见坑与 FAQ
- 速查清单(Cheat Sheet)
- 最小可运行示例(拷贝即用)
1. 快速认识 Parse
Parse 是一个开源 BaaS(Backend as a Service)方案:搭配 Parse Server 与多端 SDK(含 JS)即可快速获得数据存储、用户认证、文件存储、云函数、实时订阅等能力。你可以:
- 自建 Parse Server(Node.js + MongoDB/Postgres)
- 或使用托管服务(例如 Back4App 等)
为什么选 Parse JS?
- 一套 SDK 覆盖浏览器、Node.js、React Native。
- 无需从零写大量增删改查与会话管理代码。
- 既能“无后端”快速起步,也能通过 Cloud Code 编写严谨的后端逻辑。
2. 安装与引入(NPM / CDN / ESM / CJS)
2.1 通过 NPM / Yarn / pnpm
npm install parse
# 或
yarn add parse
# 或
pnpm add parse
2.2 浏览器直接引入(CDN)
<script src="https://unpkg.com/parse/dist/parse.min.js"></script>
此时全局可用 Parse 对象。
2.3 ESM / CJS 导入
// ESM(Node、构建型前端)import Parse from 'parse/dist/parse.min.js';
// 或在 Node 端:import Parse from 'parse/node.js';
// CommonJS(老项目)const Parse = require('parse/node');
提示:在 Node 环境优先使用
parse/node入口,以获得合适的适配(如没有window的环境)。
3. 初始化与环境区分
3.1 浏览器(自建 Parse Server)
<script src="https://unpkg.com/parse/dist/parse.min.js"></script>
<script>
Parse.initialize('YOUR_APP_ID');
Parse.serverURL = 'https://your-parse-server.example.com/parse';
// 如果使用托管,通常还会提供 JavaScript Key:// Parse.initialize('APP_ID', 'JAVASCRIPT_KEY');
// Parse.serverURL = 'https://parseapi.back4app.com/';
// 可选:LiveQuery(如启用)// Parse.liveQueryServerURL = 'wss://your-parse-server.example.com/';
</script>
3.2 Node.js(服务端脚本 /SSR)
import Parse from 'parse/node.js';
Parse.initialize('YOUR_APP_ID');
Parse.serverURL = 'https://your-parse-server.example.com/parse';
// 若在服务端使用 masterKey(谨慎):// Parse.masterKey = 'YOUR_MASTER_KEY';
安全提示 :
masterKey只应出现在受控的服务端环境, 不要 在浏览器或移动端泄露。
3.3 环境区分与多配置
为 dev/staging/prod 分别设置 APP_ID、serverURL、liveQueryServerURL,并使用环境变量注入构建:
Parse.initialize(import.meta.env.VITE_PARSE_APP_ID);
Parse.serverURL = import.meta.env.VITE_PARSE_SERVER_URL;
3.4 在 Next.js 中
- 仅在客户端初始化(比如在
useEffect内或pages/_app.tsx的useEffect),避免 SSR 时访问window。 - 或在 API Routes 中使用
parse/node与masterKey执行受信逻辑。
4. 基础概念
- Class:类似数据库中的表,例如
Book、Author。 - Object:表中的一行(
Parse.Object),有objectId、createdAt、updatedAt。 - Pointer:外键指针(对象引用),实现一对多 / 多对一。
- Relation:多对多关系(
relation('members'))。 - Query:查询构造器(筛选、排序、分页、include 等)。
- User / Role / Session:用户、角色与会话管理。
- CLP(Class Level Permissions):表级权限设置(create/read/update/delete/find 等)。
- ACL(Access Control List):对象级权限(指定某用户 / 角色可读 / 写)。
- Pointer Permissions:基于字段的细粒度访问控制(如“作者只能改自己文章”)。
5. 数据读写(CRUD)
5.1 新建与保存
const Book = Parse.Object.extend('Book');
const book = new Book();
book.set('title', 'Clean Code');
book.set('price', 88);
await book.save();
5.2 读取单条 / 更新 / 删除
// 读取单条
const query = new Parse.Query('Book');
const one = await query.get('OBJECT_ID');
// 更新
one.set('price', 99);
await one.save();
// 删除
await one.destroy();
5.3 批量保存 / 删除
const a = new Parse.Object('Book').set('title', 'A');
const b = new Parse.Object('Book').set('title', 'B');
await Parse.Object.saveAll([a, b]);
// 批量删除:await Parse.Object.destroyAll([a, b]);
6. 查询(Query)详解
6.1 常见条件
const q = new Parse.Query('Book');
q.equalTo('title', 'Clean Code');
q.notEqualTo('status', 'archived');
q.greaterThan('price', 50);
q.greaterThanOrEqualTo('stock', 1);
q.lessThan('price', 200);
q.containedIn('category', ['tech', 'design']);
q.matches('title', /^Clean/i); // 正则
6.2 结果控制
q.select(['title', 'price']); // 只取部分字段
q.include('author'); // 预取指针对象
q.ascending('price'); // 排序
q.limit(20).skip(0); // 分页
const rows = await q.find();
6.3 组合查询
// OR
const q1 = new Parse.Query('Book').lessThan('price', 50);
const q2 = new Parse.Query('Book').equalTo('isOnSale', true);
const or = Parse.Query.or(q1, q2);
const list = await or.find();
// AND(两种做法)// ① 单个查询上叠加多个条件(常用)// ② 或使用 Parse.Query.and(qA, qB) 组合两个查询
6.4 关联匹配
// 子查询:找出作者在“VIP”组内的书
const vipAuthorQ = new Parse.Query('Author').equalTo('isVIP', true);
const bookQ = new Parse.Query('Book');
bookQ.matchesQuery('author', vipAuthorQ);
const vipBooks = await bookQ.find();
6.5 聚合与 distinct(高级)
// distinct(去重某字段值)const q = new Parse.Query('Book');
const categories = await q.distinct('category');
// aggregate(MongoDB 聚合管道)const pipeline = [
{$match: { price: { $gte: 50} } },
{$group: { _id: '$category', avgPrice: { $avg: '$price'} } },
{$sort: { avgPrice: -1} }
];
const stats = await q.aggregate(pipeline);
注意:聚合能力依赖后端数据库与 Parse Server 版本,生产前先在测试库验证。
7. 关系建模与关联查询
7.1 Pointer(指针,一对多 / 多对一)
const author = await new Parse.Object('Author').set('name', 'Bob').save();
const book = new Parse.Object('Book');
book.set('title', 'Patterns');
book.set('author', author); // Pointer
await book.save();
7.2 Relation(多对多)
const group = await new Parse.Object('Group').save();
const rel = group.relation('members');
rel.add([user1, user2]);
await group.save();
7.3 include 与反向查询
// include 预加载指针,避免 N+1
const q = new Parse.Query('Book');
q.include('author');
const list = await q.find();
// 反向:找出某作者的所有书
const q2 = new Parse.Query('Book').equalTo('author', author);
const booksOfAuthor = await q2.find();
8. 用户体系与认证
8.1 注册 / 登录 / 登出
// 注册
const user = new Parse.User();
user.set('username', 'alice');
user.set('password', 's3cret');
user.set('email', 'a@b.com');
await user.signUp();
// 登录
const logged = await Parse.User.logIn('alice', 's3cret');
// 当前用户(前端环境)const current = Parse.User.current();
// 登出
await Parse.User.logOut();
8.2 匿名登录 / 第三方登录(思路)
- 匿名登录:在 Parse Server 端启用 Anonymous Provider,客户端可创建匿名用户,后续可“合并 / 绑定”到正式账号。
- 第三方:自定义 Auth Provider,在 Cloud Code/Server 校验第三方 token,客户端用
linkWith/logInWith完成绑定或登录。
8.3 密码重置与邮箱验证
- 在 Server 端开启相应邮件配置(SMTP),即可使用
Parse.User.requestPasswordReset(email)等能力。
9. 权限与安全
9.1 CLP(Class Level Permissions)
- 控制某个 Class 是否允许 find / get / create / update / delete。
- 生产环境常见策略:关闭匿名写入,只允许已登录用户读取部分表,敏感表仅允许角色写入。
9.2 ACL(对象级权限)
const post = new Parse.Object('Post');
post.set('title', 'Hello');
const acl = new Parse.ACL();
acl.setPublicReadAccess(true); // 公开可读
acl.setWriteAccess(Parse.User.current(), true); // 仅作者可写
post.setACL(acl);
await post.save();
9.3 Pointer Permissions(基于字段)
- 例如让
Post.author指向的用户拥有写权限,可通过指针权限配置达成。
9.4 useMasterKey 与 Session Token
- 在 Cloud Code 或可信服务端,可使用
useMasterKey: true绕过 CLP/ACL(务必谨慎)。 - 在云函数中基于
request.user+request.installationId做鉴权;若要代表某用户操作,可接受其sessionToken。
10. 文件与图片(Parse.File)
// base64 上传
const file = new Parse.File('avatar.png', { base64: base64Data});
await file.save();
// 绑定到对象
const profile = new Parse.Object('Profile');
profile.set('avatar', file);
await profile.save();
// 读取 URL
const url = file.url();
存储位置由 Parse Server 配置决定(本地、S3、GCS 等)。
11. Cloud Code(云函数 / 触发器 /Job)
11.1 云函数定义与调用
cloud/main.js
Parse.Cloud.define('sum', async (req) => {const { a, b} = req.params;
if (typeof a !== 'number' || typeof b !== 'number') throw 'Invalid params';
return a + b;
});
客户端调用
const result = await Parse.Cloud.run('sum', { a: 1, b: 2});
11.2 触发器(校验 / 审计)
Parse.Cloud.beforeSave('Book', async (req) => {
const book = req.object;
if (book.get('price') < 0) throw 'price must be >= 0';
});
11.3 定时任务(Job)
Parse.Cloud.job('rebuildStats', async (req) => {// 周期性汇总统计、清理数据、发送报表等});
12. 实时功能(LiveQuery)
12.1 客户端订阅
const q = new Parse.Query('Message');
q.equalTo('roomId', 'r1');
const sub = await q.subscribe();
sub.on('create', obj => console.log('new:', obj.toJSON()));
sub.on('update', obj => console.log('upd:', obj.toJSON()));
sub.on('delete', obj => console.log('del:', obj.id));
12.2 服务端配置要点
- 启动 LiveQuery Server 并配置
classNames白名单。 - 前端设置
Parse.liveQueryServerURL = 'wss://...'。 - 注意心跳 / 断线重连策略。
13. 批量操作与数据迁移
Parse.Object.saveAll([...])、Parse.Object.destroyAll([...])。- 导入导出:可写脚本在 Node 环境读写 JSON/CSV 并调用 SDK。
- 对历史大表的结构调整,建议:影子表 / 双写、灰度迁移、后台 Job + 进度日志。
14. TypeScript 最佳实践
14.1 为对象建类型
// 定义字段接口
interface BookAttrs {
title: string;
price: number;
author?: Parse.Pointer; // 或具体类型
}
// 泛型扩展 Parse.Object
class Book extends Parse.Object<BookAttrs> {constructor() {super('Book'); }
get title() { return this.get('title'); }
set title(v: string) {this.set('title', v); }
}
Parse.Object.registerSubclass('Book', Book);
14.2 API 类型提示
- 直接安装
parse包即可获得类型声明。 - 对复杂聚合结果定义专用接口增强可读性。
15. 在 React / Next.js / Node 中使用
15.1 React(前端)
import {useEffect, useState} from 'react';
import Parse from 'parse/dist/parse.min.js';
export default function BookList() {const [books, setBooks] = useState([]);
useEffect(() => {Parse.initialize(import.meta.env.VITE_APP_ID);
Parse.serverURL = import.meta.env.VITE_SERVER_URL;
(async () => {const q = new Parse.Query('Book');
q.limit(20);
const rows = await q.find();
setBooks(rows.map(r => r.toJSON()));
})();}, []);
return (
<ul>
{books.map(b => (<li key={b.objectId}>{b.title} - {b.price}</li>
))}
</ul>
);
}
15.2 Next.js(避免 SSR 触发)
- 在客户端组件或
useEffect中初始化 SDK。 - 若需服务端读写,使用 API Route +
parse/node,并在服务端读取机密(如masterKey)。
15.3 Node 脚本(任务 / 迁移)
import Parse from 'parse/node.js';
Parse.initialize(process.env.APP_ID);
Parse.serverURL = process.env.SERVER_URL;
Parse.masterKey = process.env.MASTER_KEY; // 受控环境
const main = async () => {const q = new Parse.Query('Book');
q.greaterThan('price', 50);
const rows = await q.find({useMasterKey: true});
console.log('count:', rows.length);
};
main();
16. 错误处理与调试
16.1 常见错误
- 认证类:用户名已存在、未验证邮箱、密码错误、会话过期。
- 权限类:无权读取 / 写入(检查 CLP/ACL/Pointer Permissions)。
- 查询类:缺索引导致超时、字段名拼写错误、误用正则导致慢查询。
16.2 处理方式
try {await someParseCall();
} catch (err) {
// err.code / err.message(根据 SDK 版本)console.error(err);
}
16.3 调试建议
- 打开 Parse Server 日志(请求、错误、慢查询)。
- 在 Cloud Code 中为关键路径加日志;必要时引入 traceId。
17. 性能优化与运维建议
- 权限先行:生产环境关闭匿名写,敏感表仅角色可写。
- 最小化数据:
select只取必需字段;include仅包含必要指针。 - 善用索引:对高频过滤字段建立索引;避免无索引正则前缀匹配。
- 聚合分层:重统计走 Cloud Job 或预聚合;列表页返回轻量数据。
- 缓存策略:Cloud Code 里可对热点数据做短期缓存(如内存 /Redis)。
- 分环境:dev/staging/prod 独立 DB。
- 备份与迁移:定期备份,迁移走影子表与灰度策略。
18. 常见坑与 FAQ
- 在 Cloud Code 用
Parse.User.current()? 不要。使用req.user(或sessionToken)。 - 触发器没执行? 检查函数名、导出路径、是否返回 Promise/async、Server 日志。
- LiveQuery 收不到事件? 检查服务端 class 白名单、客户端
liveQueryServerURL、网络 / 心跳设置。 - 前端报无权访问? 复核 CLP/ACL/Pointer Permissions;本地用
useMasterKey只是“测通”,生产请走正确权限链。 - Next.js SSR 报
window未定义? 仅在客户端初始化 SDK;服务端请用parse/node。
19. 速查清单(Cheat Sheet)
// 初始化
Parse.initialize('APP_ID');
Parse.serverURL = 'https://your-server/parse';
// 新增
const o = new Parse.Object('Todo');
o.set('title', 'Learn Parse');
await o.save();
// 查询
const q = new Parse.Query('Todo');
q.equalTo('done', false);
q.limit(10).descending('createdAt');
const rows = await q.find();
// 用户
await new Parse.User({username: 'u', password: 'p'}).signUp();
await Parse.User.logIn('u', 'p');
await Parse.User.logOut();
// 文件
const f = new Parse.File('a.txt', { base64: b64});
await f.save();
// 云函数
const x = await Parse.Cloud.run('sum', { a:1, b:2});
// LiveQuery
const sub = await new Parse.Query('Msg').subscribe();
sub.on('create', m => console.log(m.toJSON()));
20. 最小可运行示例(浏览器)
将以下内容保存为
index.html,替换APP_ID与SERVER_URL后直接打开:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Parse JS Quickstart</title>
<script src="https://unpkg.com/parse/dist/parse.min.js"></script>
<style>body{font-family:system-ui,Arial;padding:24px}input{margin:4px}</style>
</head>
<body>
<h1>Parse JS Quickstart</h1>
<p>
<input id="username" placeholder="Username"/>
<input id="email" type="email" placeholder="Email"/>
<input id="password" type="password" placeholder="Password"/>
<button id="signup">Sign Up</button>
</p>
<script>
Parse.initialize('APP_ID');
Parse.serverURL = 'https://your-parse-server.example.com/parse';
document.getElementById('signup').addEventListener('click', async () => {const u = new Parse.User();
u.set('username', document.getElementById('username').value);
u.set('email', document.getElementById('email').value);
u.set('password', document.getElementById('password').value);
try {await u.signUp();
alert('Sign up success:' + u.id);
} catch (e) {alert('Error:' + e.message);
}
});
</script>
</body>
</html>
正文完

