升级到Parse 3.1版本的技术要点与开发者指南
为您的应用程序提供更多功能,使用 Parse Server 3.1
介绍
Parse 社区最近发布了版本 3.1 的 Parse Server. 此次更新清理了云代码语法:它更容易利用 es6 的 async 和 await 结构。
此外,与使用 Parse 相关的一些特性被去掉,例如,云函数简单地返回一个 Promise,而不是在响应对象上使用 错误 或 成功 消息。
您可以在 Back4App 的仪表板上轻松升级您的应用程序。本指南将演示如何升级您的代码以利用 3.1 的新功能。
要遵循本指南,欢迎您查看提供的 示例项目。
这是由 John Considine, K-Optional 的首席开发人员撰写的客座教程。
目标
- 将您的 Back4App Parse Server 更新到 3.1,并相应地迁移您的云代码。
先决条件
要完成本教程,您需要: ° 一个现有的使用 Parse Server 2.x 的 Back4App 应用程序 ° 请遵循 创建新应用教程 以了解如何在 Back4App 上创建应用。
更改摘要
最显著的变化如下:
1. 云代码与 Parse-SDK 2.x 一起运行。
之前,云代码与 Parse-SDK 1.x 一起运行。随着 Parse Server 3.1,它运行 Parse-SDK 2.x。
查看 Parse SDK 发布 以更好地理解这意味着什么。这个主要版本的提升主要涉及错误修复。它还添加了 containedBy 和 includeAll 查询方法,以及使用 includes 获取对象的能力。
2. 聚合更新
自 Parse 2.7.1 起,您可以在查询上使用 aggregate 方法。这使您能够更好地利用底层数据库。现在, aggregate 方法 的语法在 Parse.Query 上已更新。
您可以使用两个阶段执行查询: match 和 group 阶段。
之前,您不需要在管道对象中包含 pipeline 键。由于底层 API,您现在必须明确包含 pipeline 键。其值应为一个或两个阶段的数组,包含 group 和 match.
查看 Parse 官方文档以获取更具体的示例。
3. 底层优化
一些底层优化已经完成。例如,Parse LiveQuery 将获取类级权限 (CLPs) 以及数据,以防止双重数据库访问。
4. Parse 重置电子邮件
当请求密码重置邮件时,即使该邮件未被保存,服务器也会返回成功。此外,当用户的电子邮件被重置时,密码重置令牌会过期。
5. 云触发器更新
通过此版本,您可以在同一对象之间共享数据,beforeSave 和 afterSave 触发器。例如:
1 Parse.Cloud.beforeSave('Comment', async request => {
2 request.context = {
3 foo: 'bar'
4 };
5 });
6
7 Parse.Cloud.afterSave('Comment', async request => {
8 console.log(request.context.foo); //Bar
9 });您可以通过点击查看官方 Parse 3.1 更新日志中的 Parse Server 变更 这里.
6. LiveQuery 改进
Parse LiveQuery 客户端允许您订阅查询,并在服务器发送更新时接收更新。传统查询由客户端执行一次,因此这对于消息传递等情况非常有帮助。
使用 Back4App,您还可以 利用这项技术。
随着 3.x 的发布,Parse 社区改善了 LiveQuery 的系统 ACLs。
您现在可以将会话令牌传递给实时查询的订阅方法,Parse 服务器将仅管理返回该用户可以访问的结果。例如,用户可能对某些‘消息’具有读/写访问权限,但并非所有消息。
1 let query = new Parse.Query('Message');
2 // you can get session token with
3 // Parse.User.current().getSessionToken() when logged in
4 let subscription = client.subscribe(query, sessionToken); 上述代码将自动订阅用户可以访问的所有消息,减轻了您查询特定消息的责任。
更改的主要部分涉及云代码的处理。有关此内容,请参阅下面的迁移指南。
1 - 对齐技术基础
我们将以一个使用2.x版本的示例云项目开始。这样我们可以导航到适当的语法更改。你可以看到 该代码库 作为这个示例项目的参考。
如果你熟悉async和await,你可以跳过这一部分。
Javascript中异步代码的演变大致如下:
任何现代Javascript应用程序都可能使用这三者。回调函数涉及将一个函数作为参数传递给另一个函数。这个第二个函数可以在某个时刻执行第一个函数。
1 // Callbacks:
2 // executes callback function after waiting 100 milliseconds
3 setTimeout(function() {
4 alert('My callback function');
5 }, 100);回调是必不可少的,但在链接多个回调时可能会显得笨拙。具体来说,嵌套多个层次可能难以阅读,错误处理也很困难。因此在ES2015中引入了Promise。
1 // promises
2 // executes several promises in a row with no significant nesting
3 const myPromise = new Promise(function(resolve, reject) {
4 setTimeout(function() {
5 if (Math.random() < 0.2) reject(new Error('Random failure!'));
6 resolve('Finished');
7 }, 100);
8 });
9
10 // Executes this promise 4 times and catches errors involved
11 myPromise
12 .then(() => myPromise)
13 .then(() => myPromise)
14 .then(() => myPromise)
15 .then(() => myPromise)
16 .catch(e => console.log(e));Promise提高了异步编程的可读性。它们还使管道更加明确。但在 ES2017 中,使用async / await构造取得了更大的进步。您的代码现在可以等待Promise的结果,而无需依赖then / catch块(这也可能变得难以阅读)。
1 // Using the definition of myPromise from the above code:
2 async function foo() {
3 try {
4 let result = await myPromise;
5 result = await myPromise;
6 result = await myPromise;
7 } catch (e) {
8 console.log(e);
9 }
10 }也许对于一个非常简单的例子,这看起来并不比普通的promise更优雅。但等待异步函数的结果往往正是我们想要做的。因此,它确实优化了我们代码的可读性。
对 async/await 的支持
如前所述,async/await 被包含在 ECMAScript 2017 规范 (es8)。对于服务器代码,版本控制几乎不是问题,因为您可以更新到 支持这些特性的 Node.js 版本. 请放心,Back4App 的环境支持最近的稳定版本。对于浏览器代码,像 Babel 的转译器将生成与使用 async / await 的 es2016 兼容的代码,并在现代浏览器中工作。
2 - 以不同的方式思考您的代码
Cloud Code 的主要变化涉及开发者所做的与库所做的之间的区别。以前,您会显式管理响应。由于大多数 Cloud Code 将异步执行 - 进行数据库查询和写入 - 返回一个 Promise 更有意义,从而减少样板代码。
Cloud 函数的直觉是编写服务器端代码时涉及的设置和配置最小化。此版本体现了这一理念;在重构和创建新函数时请记住这一点。
为了展示 Cloud Code 函数在 Parse Server 3.1 中的工作原理,我们重写了一个来自迁移前版本的功能性 Cloud Code 示例。您可以通过点击 这里. 相同的 Cloud Code 函数在 Parse 3.1 中如下所示。
1 // Cloud Code BEFORE migration
2 // Full code found in link above
3 const POST = 'Post';
4 Parse.Cloud.define('posts:get', function(request, response) {
5 // needs a post ID
6 return new Parse.Query(POST)
7 .get(request.params.id, { useMasterKey: true })
8 .then(post => {
9 response.success(post);
10 })
11 .catch(e => {
12 response.error({ message: e.message });
13 });
14 });3 - 添加所有异步标记
任何使用 await 的函数必须声明为 async 修饰符。这个简单的重构将把 async 附加到所有云函数上。它还将用 箭头函数 替换它们,因为在这种情况下它们更简洁(也是更新后的官方 Parse 指南所使用的)。
1 // Snippet of step 2 code refactoring. See full code
2 // here in the link at the top of this step
3 const POST = 'Post';
4 const COMMENT = 'Comment';
5
6 Parse.Cloud.define('posts:get', async (request) => {
7 // Needs a post ID
8 return new Parse.Query(POST)
9 .get(request.params.id, { useMasterKey: true });
10 } 您的代码在重构后将看起来像 这个 重构后
到目前为止没有什么疯狂的。在下一步中,我们将为这个变化获得物有所值。
4 - 移除对响应的引用,使用 await
您的代码看起来像 这样 在这个重构之后
这一步有点棘手。我们需要:
- 删除对‘response’变量的所有引用,改为返回一个promise
- 在多个查询/保存函数的情况下,等待响应
查看评论创建方法以了解这是如何完成的
1 // Snippet of step 3 code refactoring. See full code
2 // here in the link at the top of this step
3 Parse.Cloud.define('comment:create', async request => {
4 // Post should have text and should have a user and a post id
5 if (!request.user) {
6 throw new Error('unauthenticated!');
7 }
8
9 if (!request.params.text) {
10 throw new Error('A comment needs text!');
11 }
12 if (!request.params.post_id) {
13 throw new Error('A comment needs a post!');
14 }
15
16 // Get the post
17
18 const post = await new Parse.Query(POST).get(request.params.post_id, {
19 useMasterKey: true
20 });
21 return new Parse.Object(COMMENT, {
22 text: request.params.text,
23 user: request.user,
24 post: post
25 }).save(null, { useMasterKey: true });
26 });注意:
- 现在抛出一个JavaScript错误,而不是调用response.error。Parse将处理将其转换为响应的过程。
- 删除‘帖子’或‘评论’涉及先获取对象,然后销毁它。通过使用‘await’,销毁方法可以在块外访问保存的对象。
这完成了迁移所需的所有重构。也就是说,如果你做到这一步,恭喜你!你的代码将在 Back4App Parse 3.1 上运行!
5 - 高级技巧(可选)
以下更改是可选的,但可以显著缩短你的代码。我发现它们减少了样板代码。
你可能注意到很多手动检查参数或经过身份验证的用户。这些确保不会发生奇怪的行为。例如,我们已经决定我们的‘Post’对象需要文本,因此如果没有传递‘text’参数,该对象将会在没有它的情况下被保存。防止这种情况的一种方法是检查是否传递了文本。
但是手动检查可能会耗时且不优雅。在这一部分,我们利用 对象解构 来隐式完成这些检查。
如果你不知道什么是解构,你应该查看上面的文档。但简而言之,它允许你用非常简洁的语法将对象的属性转换为变量。
1 // This
2 var obj = {
3 hi: true,
4 bye: false
5 };
6 var hi = obj.hi;
7 var bye = obj.bye;
8
9 // Is equivalent to this:
10 var obj = {
11 hi: true,
12 bye: false
13 };
14 var { hi, bye } = obj;
15
16 console.log(hi);
17 // true
18 console.log(bye);
19 // false解构赋值比手动赋值更简洁。它还允许你即时声明参数变量,这很好:
1 Parse.Cloud.define('posts:get', async ({ params: { id } }) => {
2 // id is declared
3 });当与 es2015 语法结合使用时,我们可以优化我们的参数检查。
我们可以这样做:
1 // Iterates through object's keys. Makes sure, for each key, the value is set
2 const AssertParams = parameter_obj => {
3 for (var key of Object.keys(parameter_obj)) {
4 if (typeof parameter_obj[key] === 'undefined')
5 throw new Error(`Missing parameter ${key}`);
6 }
7 };
8
9 var obj = {
10 hi: true,
11 bye: false
12 };
13 var { hi, undef, bye } = obj; // undef will be undefined
14 var check_1 = { hi, bye };
15 var check_2 = { hi, undef };
16
17 // check = { hi : true, no : undefined }
18 AssertParams(check_1); // passes
19 AssertParams(check_2); // throws error所以对于我们的 Parse 代码,我们可以这样做:
1 // Snippet of advanced code refactoring. See full code
2 // here in the link at the top of this step
3 Parse.Cloud.define('posts:delete', async ({ user, params: { id } }) => {
4 // Makes sure user is authenticated, and id parameter is passed
5 AssertParams({ user, id });
6 const post = await new Parse.Query(POST).get(id, {
7 useMasterKey: true
8 });
9 return post.destroy({ useMasterKey: true });
10 });如果这让你感到畏惧,不用担心。这个 完整代码 示例可能会有所帮助。
简而言之,‘AssertParams’ 是一个实用函数,用于在值未定义时抛出错误。我们可以通过结合对象解构和 es2015 对象初始化来一次性检查参数。
这消除了八到九个手动检查,这在一段时间后会变得不太美观。
6 - 在 Back4App 上升级
一旦您迁移了代码,您还必须做两件事才能在 Back4App 上运行它。
- 升级您的 Back4App 服务器版本
- 上传您的新云代码。
我之前简要提到过第一步。您需要做的就是登录 Back4App 并进入您应用的仪表板。从那里,只需在左侧选择“服务器设置”,然后在“管理 Parse 服务器”卡上点击“设置”按钮。然后您可以选择 Parse Server 3.1.1 单选按钮并点击“保存”。
最后但同样重要的是,您可以通过 Back4App CLI, 或使用手动上传通过您的仪表板上传您的代码。如果您需要更多关于此步骤的信息,请参见 本指南。
关于发布新版本的说明
最后,重要的是要注意这些变化是 重大。在 2.x Parse Server 上使用我们编写的代码将会失败,而在 3.1 服务器上使用 2.x 代码也会失败。因此,您必须确保在上传升级后的 Cloud Code 时立即升级您的 Back4App 解析版本。如果您有很多用户,并且担心代码上传和版本升级稍微不同步,您可以这样做。
1 const serverVersion =
2 Parse.CoreManager.get('VERSION') === 'js1.11.1' ? 'OLD' : 'NEW';
3
4 if (serverVersion === 'NEW') {
5 Parse.Cloud.define('posts:get', async ({ params: { id } }) => {
6 AssertParams({ id });
7 // Needs a post ID
8 return new Parse.Query(POST).get(id, {
9 useMasterKey: true
10 });
11 });
12 } else if (serverVersion === 'OLD') {
13 // Old definition here
14 }这段代码动态地确定版本并根据此定义 Cloud 函数。上传后,您将更改为 3.1,然后可以重新上传删除旧部分的代码。最初包含检查可以确保您的代码不会崩溃。由于您可以在几秒钟内升级和上传,因此通常没有必要。
结论
在本指南中,我们简单演示了如何将您的 Cloud Code 迁移到 Parse 3.1 版本。记得在进行这些更改后将您的 Back4App 版本提升到 3.1。
同样重要的是,本指南演示了 Parse 3.1 所利用的改进语法。我们的重构将代码库从约 160 行减少到约 90 行,并使其更易读。对于实际应用,这将带来回报。