小程序是一个全新的跨平台移动应用平台,小游戏是小程序的一个类目,在小程序的基础上开放了游戏相关的 API。LeanCloud 为小程序提供一站式后端云服务,为你免去服务器维护、证书配置等繁琐的工作,大幅降低你的开发和运维成本。本文说明了如何在小程序与小游戏中使用 LeanCloud 提供的各项服务。
QQ 小程序兼容微信小程序的 API,概念与用法也相似。如果没有特殊说明,本文将不再区分微信小程序与 QQ 小程序。
我们在小程序上实现了 LeanTodo 应用。在这个 Demo 中你可以看到:
你可以通过微信或 QQ 扫描以下二维码进入 Demo。 Demo 的源码与运行说明请参考 https://github.com/leancloud/leantodo-weapp。
如果你不需要进行真机调试可以暂时跳过这一步(可在开发者工具的 详情 > 项目设置 中勾选不校验安全域名、TLS 版本以及 HTTPS 证书)。
要使用 LeanCloud 的结构化对象存储、文件存储、用户系统等功能,需要使用 LeanCloud 存储 SDK。存储 SDK 的安装与初始化请请参阅《JavaScript SDK 安装指南》中对应平台的说明。
安装存储 SDK 后即可在 app.js 中初始化应用:
app.js
// 获取 AV 命名空间的方式根据不同的安装方式而异,这里假设是通过手动导入文件的方式安装的 SDK const AV = require('./libs/av-core-min.js'); const adapters = require('./libs/leancloud-adapters-weapp.js'); AV.setAdapters(adapters); AV.init({ appId: '{{appid}}', appKey: '{{appkey}}', // 请将 xxx.example.com 替换为你的应用绑定的自定义 API 域名 serverURLs: "https://xxx.example.com", });
要使用 LeanCloud 的即时通讯服务实现聊天等功能,需要使用 LeanCloud 即时通讯 SDK。即时通讯 SDK 是与存储 SDK 独立的 SDK,我们在单独的 即时通讯 章节介绍其安装与初始化的步骤。
所有的结构化对象存储 API 都能正常使用,详细的用法请参考 JavaScript 数据存储开发指南。
AV.Object 实例是一些携带很多信息与方法的对象,而小程序的存放渲染用数据的 data 字段需要的是 JSON 类型的数据,因此我们需要将 AV.Object 实例处理为 JSON 数据后再设置给 data。
AV.Object
data
以 LeanTodo Demo 中的 Todo 列表为例,要实现「将 AV.Query 查询结果 Todo 列表中的 content 与 done 字段展示为列表」的功能,我们可以定义一个 getDataForRender 来做上面说的「处理」:
AV.Query
Todo
content
done
getDataForRender
const getDataForRender = todo => ({ content: todo.get('content'), done: todo.get('done') }); Page({ data: { todos: [] }, onReady() { new AV.Query('Todo') .find() .then(todos => this.setData({ todos: todos.map(getDataForRender) })) .catch(console.error); } });
AV.Object 提供了一个 #toJSON() 方法以 JSON 的形式返回其携带的有效信息。因此如果不考虑渲染性能,getDataForRender 可以简化为:
#toJSON()
const getDataForRender = todo => todo.toJSON();
使用 #toJSON() 会比手动 pick 需要的数据带来更多的性能消耗。这是因为小程序的 data 在逻辑层与渲染层之间是通过序列化后的字符串格式通讯的,过大的 data 结构会造成渲染耗时过久。因此对于结构复杂的 AV.Object,特别是如果是一个列表,手动 pick 需要的数据设置为 data 是一种常见的优化方法。
当然,每次 setData 时遇到不同结构中的 AV.Object 都要进行这样的处理会让代码充斥噪音,你可以使用各种技巧对此进行优化。这里分享一个 Demo 中使用的一个统一对 setData 的对象进行「处理」的 utility 方法 jsonify:
setData
jsonify
const isPlainObject = target => target && target.toString() == '[object Object]' && Object.getPrototypeOf(target) == Object.prototype; const _jsonify = target => { if (target && typeof target.toJSON === 'function') return target.toJSON(); if (Array.isArray(target)) return target.map(_jsonify); return target; }; const jsonify = target => isPlainObject(target) ? Object.keys(target).reduce( (result, key) => ({ ...result, [key]: _jsonify(target[key]) }), {} ) : _jsonify(target);
jsonify 能正确的处理 AV.Object、AV.Object 数组以及其他类型的数据。使用时可以简单的在所有的 setData 之前对数据调用一次 jsonify 方法:
this.setData(jsonify({ todos, // AV.Object list user, // AV.Object }));
值得注意的是从 AV.Object 到 JSON 数据的处理是不可逆的,如果在之后还需要用到查询结果的 AV.Object,我们可以将其保存到 Page 实例上:
Page({ // todos 存放的是 AV.Object 列表,后续可以这些对象进行操作(比如调用其 save 方法),不参与渲染 todos: [], data: { // data.todo 存放的是 JSON 数据,供 WXML 页面渲染用 todos: [] }, onReady() { new AV.Query('Todo') .find() .then(todos => { this.todos = todos; this.setData(jsonify({ todos }); }) .catch(console.error); }, saveAll() { // 可以在这里获取到 this.todos 进行操作 return AV.Object.saveAll(this.todos) } });
你可能会在某些过时的文档或者 Demo 中看到直接使用 this.setData() 将 AV.Object 对象设置为当前页面的 data 的用法。我们 不再推荐这种用法。这是 SDK 针对小程序做的特殊「优化」,利用了小程序渲染机制中的一个未定义行为,目前已知在使用了自定义组件(usingComponents)时这种用法会失效。
this.setData()
usingComponents
在小程序中,可以将用户相册或拍照得到的图片上传到 LeanCloud 服务器进行保存。首先通过 wx.chooseImage 方法选择或拍摄照片,得到本地临时文件的路径,然后按照下面的方法构造一个 AV.File 将其上传到 LeanCloud:
wx.chooseImage
AV.File
wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success: function(res) { var tempFilePath = res.tempFilePaths[0]; // 使用本地临时文件的路径构造 AV.File new AV.File('file-name', { blob: { uri: tempFilePath, }, }) // 上传 .save() // 上传成功 .then(file => console.log(file.url())) // 上传发生异常 .catch(console.error); } });
上传成功后可以通过 file.url() 方法得到服务端的图片 url。
file.url()
关于文件存储更详细的用法请参考 JavaScript 数据存储开发指南 · 文件。
小程序中提供了登录 API 来获取微信的用户登录状态,应用可以访问到用户的昵称、性别等基本信息。但是如果想要保存额外的用户信息,如用户的手机号码、收货地址等,或者需要在其他平台使用该用户登录,则需要使用 LeanCloud 的用户系统。
SDK 提供了一系列小程序特有的用户相关的 API,适用于不同的使用场景:
AV.User.loginWithMiniApp
AV.User#loginWithMiniApp
AV.User#associateWithMiniApp
以上 API 均使用微信 Adapters 的 AuthInfo 进行登录。AuthInfo 可通过 Adapters 的 getAuthInfo 函数获取。若未提供 AuthInfo ,API 会使用默认参数调用 getAuthInfo 函数自动获取。
AuthInfo
getAuthInfo
下面我们以微信平台为例讨论不同场景下的使用方式。
LeanCloud 的用户系统支持一键使用微信用户身份登录。要使用一键登录功能,需要先设置小程序的 AppID 与 AppSecret:
这样你就可以在应用中使用 AV.User.loginWithMiniApp() 方法来使用当前用户身份登录了。
AV.User.loginWithMiniApp()
AV.User.loginWithMiniApp().then(user => { this.globalData.user = user; }).catch(console.error);
使用一键登录方式登录时,LeanCloud 会将该用户的小程序 openid 与 session_key 等信息保存在对应的 user.authData.lc_weapp 属性中,你可以在控制台的 _User 表中看到:
openid
session_key
user.authData.lc_weapp
_User
{ "authData": { "lc_weapp": { "session_key": "2zIDoEEUhkb0B5pUTzsLVg==", "expires_in": 7200, "openid": "obznq0GuHPxdRYaaDkPOHk785DuA" } } }
如果用户是第一次使用此应用,调用登录 API 会创建一个新的用户,你可以在 控制台 > 数据存储 > 结构化数据 中的 _User 表中看到该用户的信息,如果用户曾经使用该方式登录过此应用(存在对应 openid 的用户),再次调用登录 API 会返回同一个用户。
用户的登录状态会保存在客户端中,可以使用 AV.User.current() 方法来获取当前登录的用户,下面的例子展示了如何为登录用户保存额外的信息:
AV.User.current()
// 假设已经通过 AV.User.loginWithMiniApp() 登录 // 获得当前登录用户 const user = AV.User.current(); // 调用小程序 API,得到用户信息 wx.getUserInfo({ success: ({userInfo}) => { // 更新当前用户的信息 user.set(userInfo).save().then(user => { // 成功,此时可在控制台中看到更新后的用户信息 this.globalData.user = user; }).catch(console.error); } });
authData 默认只有对应用户可见,开发者可以使用 masterKey 在云引擎中获取该用户的 openid 与 session_key 进行支付、推送等操作。详情的示例请参考 支付。
authData
小程序的登录态(session_key)存在有效期,可以通过 wx.checkSession() 方法检测当前用户登录态是否有效,失效后可以通过调用 AV.User.loginWithMiniApp() 重新登录。
wx.checkSession()
微信开放平台使用 unionid 来区分用户的唯一性,也就是说同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 unionid 都是同一个,而 openid 会是多个。如果你想要实现多个小程序之间,或者小程序与使用微信开放平台登录的应用之间共享用户系统的话,则需要使用 unionid 登录。
要在小程序中使用 unionid 登录,请先确认已经在 微信开放平台 绑定了该小程序
在小程序中有很多途径可以 获取到 unionid。不同的 unionid 获取方式,接入 LeanCloud 用户系统的方式也有所不同。
若小程序已在微信开放平台绑定,server 端能静默地获取到用户的 unionid 并用 unionid + openid 进行匹配登录,只需在获取 AuthInfo 时指定参数 preferUnionId 为 true 即可。
preferUnionId
adapters.getAuthInfo({ preferUnionId: true, }).then(authInfo => { return AV.User.loginWithMiniApp(authInfo); });
使用 unionid 登录后,用户的 authData 中会增加 _weixin_unionid 一项(与 lc_weapp 平级):
_weixin_unionid
lc_weapp
{ "authData": { "lc_weapp": { "session_key": "2zIDoEEUhkb0B5pUTzsLVg==", "expires_in": 7200, "openid": "obznq0GuHPxdRYaaDkPOHk785DuA", "unionid": "ox7NLs5BlEqPS4glxqhn5kkO0UUo" }, "_weixin_unionid": { "uid": "ox7NLs5BlEqPS4glxqhn5kkO0UUo" } } }
用 unionid + openid 登录时,会按照下面的步骤进行用户匹配:
authData._weixin_unionid.uid
unionid
authData.lc_weapp
authData.lc_weapp.openid
不管匹配的过程是如何的,最终登录用户的 authData 都会是上面这种结构。
LeanTodo Demo 便是使用这种方式登录的,如果你已经关注了其关联的公众号(搜索 AVOSCloud,或通过小程序关于页面的相关公众号链接访问),那么你在登录后会在 LeanTodo Demo 的 设置 - 用户 页面看到当前用户的 authData 中已经绑定了 unionid。
需要注意的是:
如果用户符合上述静默获取 unionid 的条件,但没有指定 preferUnionId,那么该次登录不会使用 unionid 登录,但仍然会将获取到的 unionid 作为一般字段写入该用户的 authData.lc_weapp 中。此时用户的 authData 会是这样的:
{ "authData": { "lc_weapp": { "session_key": "2zIDoEEUhkb0B5pUTzsLVg==", "expires_in": 7200, "openid": "obznq0GuHPxdRYaaDkPOHk785DuA", "unionid": "ox7NLs5BlEqPS4glxqhn5kkO0UUo" } } }
如果开发者自行获得了用户的 unionid(例如通过解密 wx.getUserInfo 获取到的用户信息),可以在小程序中调用 AV.User.mergeUnionId() 将 unionid 合并到 AuthInfo 中,完成登录授权:
AV.User.mergeUnionId()
adapters.getAuthInfo().then(authInfo => { authInfo = AV.User.mergeUnionId(authInfo, unionid, { asMainAccount: true }); return AV.User.loginWithMiniApp(authInfo); });
如果开发者希望更灵活的控制小程序的登录流程,也可以自行在服务端实现 unionid 与 openid 的获取,然后调用通用的第三方 unionid 登录接口指定平台为 lc_weapp 来登录:
const unionid = ''; const authData = { openid: '', session_key: '' }; const platform = 'lc_weapp'; AV.User.loginWithAuthDataAndUnionId(authData, platform, unionid, { asMainAccount: true }).then(console.log, console.error);
相对上面提到的一些 Weapp 相关的登录 API,loginWithAuthDataAndUnionId 是更加底层的第三方登录接口,不依赖小程序运行环境,因此这种方式也提供了更高的灵活度:
AV.User.loginWithAuthDataAndUnionId
sessionToken
这种用法的另一种常见场景是关联同一个开发者帐号下的第二个小程序。
因为一个 LeanCloud 应用默认关联一个微信小程序(对应的平台名称是 lc_weapp),使用小程序系列 API 的时候也都是默认关联到 authData.lc_weapp 字段上。如果想要接入第二个小程序,则需要自行获取到 unionid 与 openid,然后将其作为一个新的第三方平台登录。这里同样需要用到 AV.User.loginWithAuthDataAndUnionId 方法,但与关联内置的小程序平台(lc_weapp)有一些不同:
platform
uid
这里我们以新的平台 weapp2 为例:
weapp2
const unionid = ''; const openid = ''; const authData = { uid: openid, session_key: '' }; const platform = 'weapp2'; AV.User.loginWithAuthDataAndUnionId(authData, platform, unionid, { asMainAccount: true }).then(console.log, console.error);
如果一个用户已经登录,现在通过某种方式获取到了其 unionid(一个常见的使用场景是用户完成了支付操作后在服务端通过 getPaidUnionId 得到了 unionid)希望与之关联,可以在小程序中使用 AV.User#associateWithWeappWithUnionId():
AV.User#associateWithWeappWithUnionId()
const user = AV.User.current(); // 获取当前登录用户 adapters.getAuthInfo().then(authInfo => { authInfo = AV.User.mergeUnionId(authInfo, unionid, { asMainAccount: true }); return user.associateWithMiniApp(authInfo); });
上述的登录 API 对接的是小程序的用户系统,所以使用这些 API 创建的用户无法直接在小程序之外的平台上登录。如果需要使用 LeanCloud 用户系统提供的其他登录方式,如用手机号验证码登录、邮箱密码登录等,在小程序登录后设置对应的用户属性即可:
// 小程序登录 AV.User.loginWithMiniApp().then(user => { // 设置并保存手机号 user.setMobilePhoneNumber('13000000000'); return user.save(); }).then(user => { // 发送验证短信 return AV.User.requestMobilePhoneVerify(user.getMobilePhoneNumber()); }).then({ // 用户填写收到短信验证码后再调用 AV.User.verifyMobilePhone(code) 完成手机号的绑定 // 成功后用户的 mobilePhoneVerified 字段会被置为 true // 此后用户便可以使用手机号加动态验证码登录了 }).catch(console.error);
验证手机号码功能要求在「控制台 > 内建账户 > 设置」中启用「从客户端注册或更新手机号时,向注册手机号码发送验证短信」。
如果你的应用已经在使用 LeanCloud 的用户系统,或者用户已经通过其他方式注册了你的应用(比如在 Web 端通过用户名密码注册),可以通过在小程序中调用 AV.User#associateWithMiniApp() 来关联已有的账户:
AV.User#associateWithMiniApp()
// 首先,使用用户名与密码登录一个已经存在的用户 AV.User.logIn('username', 'password').then(user => { // 将当前的微信用户与当前登录用户关联 return user.associateWithMiniApp(); }).catch(console.error);
要使用 LeanCloud 的即时通讯服务实现聊天等功能,需要使用 LeanCloud 即时通讯 SDK。
请参阅《JavaScript SDK 安装指南》中对应平台的说明。
安装 SDK 后即可在 app.js 中初始化应用:
// Realtime 类获取的方式根据不同的安装方式而异,这里假设是通过手动导入文件的方式安装的 SDK const { Realtime, setAdapters } = require('./libs/im.min.js'); const adapters = require('./libs/leancloud-adapters-weapp.js'); setAdapters(adapters); const realtime = new Realtime({ appId: '{{appid}}', appKey: '{{appkey}}', // 请将 xxx.example.com 替换为你的应用绑定的自定义 API 域名 server: "https://xxx.example.com", });
需要特别注意的是,小程序对 WebSocket 连接的数量是有限制的,因此推荐的用法是初始化 Realtime 一次,挂载到全局的 App 实例上,然后在所有需要的时候都使用这个 Realtime 实例。
Realtime
// app.js const { Realtime, setAdapters } = require('./libs/im.min.js'); const adapters = require('./libs/leancloud-adapters-weapp.js'); setAdapters(adapters); const realtime = new Realtime({ appId: '{{appid}}', appKey: '{{appkey}}', // 请将 xxx.example.com 替换为你的应用绑定的自定义 API 域名 server: "https://xxx.example.com", }); App({ realtime: realtime, // ... }); // some-page.js const realtime = getApp().realtime;
即时通讯 SDK 的详细用法请参考 即时通讯开发指南。
要在小程序中使用即时通讯 SDK 的富媒体消息插件,有一些额外的约束:
libs
leancloud-storage.js
leancloud-realtime.js
leancloud-realtime-plugin-typed-messages.js
在 app.js 中依次加载 leancloud-storage.js、leancloud-realtime.js 和 leancloud-realtime-plugin-typed-messages.js。
const AV = require('./libs/leancloud-storage.js'); const IM = require('./libs/leancloud-realtime.js'); const initPlugin = require('./libs/leancloud-realtime-plugin-typed-messages.js'); const { Realtime, setAdapters } = IM; const { TypedMessagesPlugin, ImageMessage } = initPlugin(AV, IM);
AV.setAdapters(adapters); setAdapters(adapters); // 初始化存储 SDK AV.init({ appId: '{{appid}}', appKey: '{{appkey}}', serverURLs: "https://xxx.example.com", }); // 初始化即时通讯 SDK const realtime = new Realtime({ appId: '{{appid}}', appKey: '{{appkey}}', plugins: [TypedMessagesPlugin], // 注册富媒体消息插件 server: "https://xxx.example.com", }); // 请将 xxx.example.com 替换为你的应用绑定的自定义 API 域名
富媒体消息的用法请参考 即时通讯开发指南 - 富媒体消息。
使用即时通讯 SDK,一个常见的需求是将 Conversation 与 Message 类型的数据绑定到视图层进行渲染。这里会遇到一些与结构化数据存储 SDK 一样的问题,其解决方案与最佳实践请参考结构化数据存储的 数据绑定 章节(Conversation 与 Message 都实现了 #toJSON 方法,上文中介绍的 jsonify 方法同样适用于Conversation 与 Message 实例)。
Conversation
Message
#toJSON
利用云引擎服务,我们能轻松的接入各类平台的支付功能。在这里我们以微信小程序中使用微信支付作为示例。
在开始之前,请确保已经在微信小程序后台开启了「微信支付」功能,然后按照下面的步骤配置云引擎环境变量:
WEIXIN_APPID
WEIXIN_MCHID
WEIXIN_PAY_SECRET
WEIXIN_NOTIFY_URL
https://your-domain/weixin/pay-callback
your-domain
首先确认本机已经安装 Node.js 运行环境和 LeanCloud 命令行工具,然后执行下列指令下载示例项目:
$ git clone https://github.com/leancloud/weapp-pay-getting-started.git $ cd weapp-pay-getting-started
安装依赖:
npm install
登录并关联应用:
lean login lean switch
启动项目:
lean up
之后你就可以在 localhost:3001 调试云函数了。
示例项目中与支付直接相关代码有三部分:
order.js
place
cloud.js
order
routers/weixin.js
pay-callback
SUCCESS
请根据你的业务需要修改代码。参考文档:
完成开发后部署到预备环境(若无预备环境则直接部署到生产环境):
lean deploy
客户端完成一次支付需要分两步:
wx.requestPayment
AV.Cloud.run('order').then((data) => { data.success = () => { // 支付成功 }); data.fail = ({ errMsg }) => { // 错误处理 }); wx.requestPayment(data); }).catch(error => { // 错误处理 })
客户端的示例代码参见 Demo 打赏功能。参考文档:
请前往 控制台 > 数据存储 > 文件 > 设置 配置你自己的文件域名。
如果你的应用启用并配置了 Web 安全域名,你可能会 catch 到 Access denied by api domain white list 异常,请将提示的域名添加至应用的 Web 安全域名列表。
Access denied by api domain white list
例如新建一个名为「Todo」的表,上传数据成功后进入控制台查看,其表名称显示为像 i、u 这样的单个字母。这是因为真机上代码会被压缩,解决办法是在创建 Class 后向 SDK 注册该 Class 的名字:AV.Object.register(Todo, 'Todo');。
AV.Object.register(Todo, 'Todo');
如果在微信 / QQ 小程序中使用 LeanCloud 时遇到问题,欢迎通过我们的 论坛 进行反馈。
在微信小程序(游戏)与 QQ 小程序(游戏)中使用 LeanCloud
小程序是一个全新的跨平台移动应用平台,小游戏是小程序的一个类目,在小程序的基础上开放了游戏相关的 API。LeanCloud 为小程序提供一站式后端云服务,为你免去服务器维护、证书配置等繁琐的工作,大幅降低你的开发和运维成本。本文说明了如何在小程序与小游戏中使用 LeanCloud 提供的各项服务。
QQ 小程序兼容微信小程序的 API,概念与用法也相似。如果没有特殊说明,本文将不再区分微信小程序与 QQ 小程序。
Demo
我们在小程序上实现了 LeanTodo 应用。在这个 Demo 中你可以看到:
你可以通过微信或 QQ 扫描以下二维码进入 Demo。 Demo 的源码与运行说明请参考 https://github.com/leancloud/leantodo-weapp。
准备工作
创建应用
配置域名白名单
如果你不需要进行真机调试可以暂时跳过这一步(可在开发者工具的 详情 > 项目设置 中勾选不校验安全域名、TLS 版本以及 HTTPS 证书)。
安装与初始化 SDK
要使用 LeanCloud 的结构化对象存储、文件存储、用户系统等功能,需要使用 LeanCloud 存储 SDK。存储 SDK 的安装与初始化请请参阅《JavaScript SDK 安装指南》中对应平台的说明。
安装存储 SDK 后即可在
app.js
中初始化应用:要使用 LeanCloud 的即时通讯服务实现聊天等功能,需要使用 LeanCloud 即时通讯 SDK。即时通讯 SDK 是与存储 SDK 独立的 SDK,我们在单独的 即时通讯 章节介绍其安装与初始化的步骤。
结构化对象存储
所有的结构化对象存储 API 都能正常使用,详细的用法请参考 JavaScript 数据存储开发指南。
数据绑定
AV.Object
实例是一些携带很多信息与方法的对象,而小程序的存放渲染用数据的data
字段需要的是 JSON 类型的数据,因此我们需要将AV.Object
实例处理为 JSON 数据后再设置给data
。以 LeanTodo Demo 中的 Todo 列表为例,要实现「将
AV.Query
查询结果Todo
列表中的content
与done
字段展示为列表」的功能,我们可以定义一个getDataForRender
来做上面说的「处理」:AV.Object
提供了一个#toJSON()
方法以 JSON 的形式返回其携带的有效信息。因此如果不考虑渲染性能,getDataForRender
可以简化为:使用
#toJSON()
会比手动 pick 需要的数据带来更多的性能消耗。这是因为小程序的data
在逻辑层与渲染层之间是通过序列化后的字符串格式通讯的,过大的data
结构会造成渲染耗时过久。因此对于结构复杂的AV.Object
,特别是如果是一个列表,手动 pick 需要的数据设置为data
是一种常见的优化方法。当然,每次
setData
时遇到不同结构中的AV.Object
都要进行这样的处理会让代码充斥噪音,你可以使用各种技巧对此进行优化。这里分享一个 Demo 中使用的一个统一对setData
的对象进行「处理」的 utility 方法jsonify
:jsonify
能正确的处理AV.Object
、AV.Object
数组以及其他类型的数据。使用时可以简单的在所有的setData
之前对数据调用一次jsonify
方法:值得注意的是从
AV.Object
到 JSON 数据的处理是不可逆的,如果在之后还需要用到查询结果的AV.Object
,我们可以将其保存到 Page 实例上:你可能会在某些过时的文档或者 Demo 中看到直接使用
this.setData()
将AV.Object
对象设置为当前页面的 data 的用法。我们 不再推荐这种用法。这是 SDK 针对小程序做的特殊「优化」,利用了小程序渲染机制中的一个未定义行为,目前已知在使用了自定义组件(usingComponents
)时这种用法会失效。文件存储
在小程序中,可以将用户相册或拍照得到的图片上传到 LeanCloud 服务器进行保存。首先通过
wx.chooseImage
方法选择或拍摄照片,得到本地临时文件的路径,然后按照下面的方法构造一个AV.File
将其上传到 LeanCloud:上传成功后可以通过
file.url()
方法得到服务端的图片 url。关于文件存储更详细的用法请参考 JavaScript 数据存储开发指南 · 文件。
用户系统
小程序中提供了登录 API 来获取微信的用户登录状态,应用可以访问到用户的昵称、性别等基本信息。但是如果想要保存额外的用户信息,如用户的手机号码、收货地址等,或者需要在其他平台使用该用户登录,则需要使用 LeanCloud 的用户系统。
SDK 提供了一系列小程序特有的用户相关的 API,适用于不同的使用场景:
AV.User.loginWithMiniApp
AV.User#loginWithMiniApp
AV.User#associateWithMiniApp
以上 API 均使用微信 Adapters 的
AuthInfo
进行登录。AuthInfo
可通过 Adapters 的getAuthInfo
函数获取。若未提供AuthInfo
,API 会使用默认参数调用getAuthInfo
函数自动获取。下面我们以微信平台为例讨论不同场景下的使用方式。
一键登录
LeanCloud 的用户系统支持一键使用微信用户身份登录。要使用一键登录功能,需要先设置小程序的 AppID 与 AppSecret:
这样你就可以在应用中使用
AV.User.loginWithMiniApp()
方法来使用当前用户身份登录了。使用一键登录方式登录时,LeanCloud 会将该用户的小程序
openid
与session_key
等信息保存在对应的user.authData.lc_weapp
属性中,你可以在控制台的_User
表中看到:如果用户是第一次使用此应用,调用登录 API 会创建一个新的用户,你可以在 控制台 > 数据存储 > 结构化数据 中的
_User
表中看到该用户的信息,如果用户曾经使用该方式登录过此应用(存在对应openid
的用户),再次调用登录 API 会返回同一个用户。用户的登录状态会保存在客户端中,可以使用
AV.User.current()
方法来获取当前登录的用户,下面的例子展示了如何为登录用户保存额外的信息:authData
默认只有对应用户可见,开发者可以使用 masterKey 在云引擎中获取该用户的openid
与session_key
进行支付、推送等操作。详情的示例请参考 支付。小程序的登录态(
session_key
)存在有效期,可以通过wx.checkSession()
方法检测当前用户登录态是否有效,失效后可以通过调用AV.User.loginWithMiniApp()
重新登录。使用 unionid
微信开放平台使用 unionid 来区分用户的唯一性,也就是说同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 unionid 都是同一个,而 openid 会是多个。如果你想要实现多个小程序之间,或者小程序与使用微信开放平台登录的应用之间共享用户系统的话,则需要使用 unionid 登录。
要在小程序中使用 unionid 登录,请先确认已经在 微信开放平台 绑定了该小程序
在小程序中有很多途径可以 获取到 unionid。不同的 unionid 获取方式,接入 LeanCloud 用户系统的方式也有所不同。
一键登录时静默获取 unionid
若小程序已在微信开放平台绑定,server 端能静默地获取到用户的 unionid 并用 unionid + openid 进行匹配登录,只需在获取
AuthInfo
时指定参数preferUnionId
为 true 即可。使用 unionid 登录后,用户的 authData 中会增加
_weixin_unionid
一项(与lc_weapp
平级):用 unionid + openid 登录时,会按照下面的步骤进行用户匹配:
authData._weixin_unionid.uid
)的用户,则会直接作为这个用户登录,并将所有信息(openid
、session_key
、unionid
等)更新到该用户的authData.lc_weapp
中。authData.lc_weapp.openid
)的用户,则会直接作为这个用户登录,并将所有信息(session_key
、unionid
等)更新到该用户的authData.lc_weapp
中,同时将unionid
保存到authData._weixin_unionid.uid
中。session_key
、unionid
等)更新到该用户的authData.lc_weapp
中,同时将unionid
保存到authData._weixin_unionid.uid
中。不管匹配的过程是如何的,最终登录用户的
authData
都会是上面这种结构。LeanTodo Demo 便是使用这种方式登录的,如果你已经关注了其关联的公众号(搜索 AVOSCloud,或通过小程序关于页面的相关公众号链接访问),那么你在登录后会在 LeanTodo Demo 的 设置 - 用户 页面看到当前用户的
authData
中已经绑定了 unionid。需要注意的是:
preferUnionId
也不会使用 unionid 登录。如果用户符合上述静默获取 unionid 的条件,但没有指定
preferUnionId
,那么该次登录不会使用 unionid 登录,但仍然会将获取到的 unionid 作为一般字段写入该用户的authData.lc_weapp
中。此时用户的authData
会是这样的:通过其他方式获取 unionid 后登录
如果开发者自行获得了用户的 unionid(例如通过解密 wx.getUserInfo 获取到的用户信息),可以在小程序中调用
AV.User.mergeUnionId()
将 unionid 合并到AuthInfo
中,完成登录授权:通过其他方式获取 unionid 与 openid 后登录
如果开发者希望更灵活的控制小程序的登录流程,也可以自行在服务端实现 unionid 与 openid 的获取,然后调用通用的第三方 unionid 登录接口指定平台为
lc_weapp
来登录:相对上面提到的一些 Weapp 相关的登录 API,loginWithAuthDataAndUnionId 是更加底层的第三方登录接口,不依赖小程序运行环境,因此这种方式也提供了更高的灵活度:
AV.User.loginWithAuthDataAndUnionId
来登录。AV.User.loginWithAuthDataAndUnionId
登录,成功后得到登录用户的sessionToken
后返回给客户端,客户端再使用该sessionToken
直接登录。关联第二个小程序
这种用法的另一种常见场景是关联同一个开发者帐号下的第二个小程序。
因为一个 LeanCloud 应用默认关联一个微信小程序(对应的平台名称是
lc_weapp
),使用小程序系列 API 的时候也都是默认关联到authData.lc_weapp
字段上。如果想要接入第二个小程序,则需要自行获取到 unionid 与 openid,然后将其作为一个新的第三方平台登录。这里同样需要用到AV.User.loginWithAuthDataAndUnionId
方法,但与关联内置的小程序平台(lc_weapp
)有一些不同:platform
openid
保存为uid
(内置的微信平台做了特殊处理可以直接用openid
而这里是作为通用第三方 OAuth 平台保存因此需要使用标准的uid
字段)。这里我们以新的平台
weapp2
为例:获取 unionid 后与现有用户关联
如果一个用户已经登录,现在通过某种方式获取到了其 unionid(一个常见的使用场景是用户完成了支付操作后在服务端通过 getPaidUnionId 得到了 unionid)希望与之关联,可以在小程序中使用
AV.User#associateWithWeappWithUnionId()
:启用其他登录方式
上述的登录 API 对接的是小程序的用户系统,所以使用这些 API 创建的用户无法直接在小程序之外的平台上登录。如果需要使用 LeanCloud 用户系统提供的其他登录方式,如用手机号验证码登录、邮箱密码登录等,在小程序登录后设置对应的用户属性即可:
验证手机号码功能要求在「控制台 > 内建账户 > 设置」中启用「从客户端注册或更新手机号时,向注册手机号码发送验证短信」。
绑定现有用户
如果你的应用已经在使用 LeanCloud 的用户系统,或者用户已经通过其他方式注册了你的应用(比如在 Web 端通过用户名密码注册),可以通过在小程序中调用
AV.User#associateWithMiniApp()
来关联已有的账户:即时通讯
要使用 LeanCloud 的即时通讯服务实现聊天等功能,需要使用 LeanCloud 即时通讯 SDK。
安装与初始化
请参阅《JavaScript SDK 安装指南》中对应平台的说明。
安装 SDK 后即可在
app.js
中初始化应用:需要特别注意的是,小程序对 WebSocket 连接的数量是有限制的,因此推荐的用法是初始化
Realtime
一次,挂载到全局的 App 实例上,然后在所有需要的时候都使用这个Realtime
实例。即时通讯 SDK 的详细用法请参考 即时通讯开发指南。
富媒体消息
要在小程序中使用即时通讯 SDK 的富媒体消息插件,有一些额外的约束:
libs
目录,并将文件重命名为leancloud-storage.js
。libs
目录,并将文件重命名为leancloud-realtime.js
。leancloud-realtime-plugin-typed-messages.js
,移动到libs
目录。在
app.js
中依次加载leancloud-storage.js
、leancloud-realtime.js
和leancloud-realtime-plugin-typed-messages.js
。app.js
中初始化应用:富媒体消息的用法请参考 即时通讯开发指南 - 富媒体消息。
数据绑定
使用即时通讯 SDK,一个常见的需求是将
Conversation
与Message
类型的数据绑定到视图层进行渲染。这里会遇到一些与结构化数据存储 SDK 一样的问题,其解决方案与最佳实践请参考结构化数据存储的 数据绑定 章节(Conversation
与Message
都实现了#toJSON
方法,上文中介绍的jsonify
方法同样适用于Conversation
与Message
实例)。支付
利用云引擎服务,我们能轻松的接入各类平台的支付功能。在这里我们以微信小程序中使用微信支付作为示例。
配置
在开始之前,请确保已经在微信小程序后台开启了「微信支付」功能,然后按照下面的步骤配置云引擎环境变量:
WEIXIN_APPID
:小程序 AppIdWEIXIN_MCHID
:微信支付商户号WEIXIN_PAY_SECRET
:微信支付 API 密钥(微信商户平台 - 账户设置 - API安全 - 密钥设置)WEIXIN_NOTIFY_URL
:https://your-domain/weixin/pay-callback
,其中your-domain
是第二步中绑定的云引擎域名查看示例
服务端开发
首先确认本机已经安装 Node.js 运行环境和 LeanCloud 命令行工具,然后执行下列指令下载示例项目:
安装依赖:
登录并关联应用:
启动项目:
之后你就可以在 localhost:3001 调试云函数了。
示例项目中与支付直接相关代码有三部分:
order.js
:对应 Order 表,定义了部分字段的 getter/setter,以及place
方法用于向微信 API 提交订单。cloud.js
:其中定义了名为order
的云函数,这个云函数会获取当前用户的openid
,以其身份创建了一个 1 分钱的 order 并下单,最后返回签名过的订单信息。routers/weixin.js
:其中定义了pay-callback
的处理函数,当用户支付成功后微信调用这个 URL,这个函数将对应的订单状态更新为SUCCESS
。请根据你的业务需要修改代码。参考文档:
完成开发后部署到预备环境(若无预备环境则直接部署到生产环境):
客户端开发
客户端完成一次支付需要分两步:
order
的云函数下单,返回签名过的订单信息。wx.requestPayment
),传入上一步返回的订单信息,发起支付。客户端的示例代码参见 Demo 打赏功能。参考文档:
FAQ
配置 download 合法域名时显示「该域名因违规被禁止设置。」
请前往 控制台 > 数据存储 > 文件 > 设置 配置你自己的文件域名。
Access denied by api domain white list
如果你的应用启用并配置了 Web 安全域名,你可能会 catch 到
Access denied by api domain white list
异常,请将提示的域名添加至应用的 Web 安全域名列表。小程序真机上传数据时,控制台存储中显示的 Class 表名被压缩为单个字母。
例如新建一个名为「Todo」的表,上传数据成功后进入控制台查看,其表名称显示为像 i、u 这样的单个字母。这是因为真机上代码会被压缩,解决办法是在创建 Class 后向 SDK 注册该 Class 的名字:
AV.Object.register(Todo, 'Todo');
。反馈
如果在微信 / QQ 小程序中使用 LeanCloud 时遇到问题,欢迎通过我们的 论坛 进行反馈。