- 云函数云数据库日期比较怎么写?
需求很简单,把当天(国庆当天)订单所有金额合计一下,调试不出来,查阅了论坛都提到数据库聚合日期比较没法比呀: [代码]const res = await db.collection([代码][代码]'orders'[代码][代码])[代码][代码] [代码][代码].aggregate()[代码][代码] [代码][代码].addFields({[代码][代码] [代码][代码]matched: $.and([$.gt([[代码][代码]'$addTime'[代码][代码], [代码][代码]new[代码] [代码]Date([代码][代码]'10/1/2019'[代码][代码])]), $.lt([[代码][代码]'$addTime'[代码][代码], [代码][代码]new[代码] [代码]Date([代码][代码]'10/2/2019'[代码][代码])])])[代码][代码] [代码][代码]})[代码][代码] [代码][代码].match({[代码][代码] [代码][代码]sellerId: openid,[代码][代码] [代码][代码]status: $.neq(-1),[代码][代码] [代码][代码]matched: [代码][代码]true[代码][代码] [代码][代码]})[代码][代码] [代码][代码].group({[代码][代码] [代码][代码]_id: [代码][代码]null[代码][代码],[代码][代码] [代码][代码]totalFinalSum: $.sum([代码][代码]'$finalSum'[代码][代码])[代码][代码] [代码][代码]})[代码][代码] [代码][代码].end()[代码]
2019-10-01 - 小程序如何导入excel数据
今天晚上完网课后,闲来无事,就开始寻思怎么优化我的在线答题小程序,今晚的优化点就是: 通过小程序上传excel,并解析,导入对应的数据 第一步: 选择会话记录中的文件 https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.chooseMessageFile.html 由于微信提供本地上传文件的api,仅仅支持图片这一种类型,所以对于其他类型文件只能通过选择会话记录中的文件。 第二步 选择了文件之后,把文件上传到云开发的云存储里面 https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/storage/uploadFile/client.uploadFile.html 第三步 通过云函数读取上传到云存储的文件,解析,然后落库 用到了node-xlsx库 https://www.npmjs.com/package/node-xlsx 这部分代码后期我从项目中抽离出来,写个代码片段给大家吧,需要的滴滴 代码片段如下 https://developers.weixin.qq.com/s/tbWcpEmR7ngx
2020-04-20 - 小程序云函数可以同步微信公众号的历史文章(不是永久素材)么?
网上查了一下,大家写的同步微信公众号的文章,我以为就是群发的历史文章,试着实现了一下,对接后获取的数据,其实是就是素材里的图文,还没发布的那种图文 所以想问下,是否有接口支持获取公众号历史文章? 谢谢~
2019-12-13 - 获取公众号的AccessToken可以获取素材总数,但获取素材列表时提示AccessToken无效?
获取公众号AccessToken后, 获取其素材总数,使用接口:https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=myAccessToken 结果如下图: [图片] 但是获取素材列表,使用接口:https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=myAccessToken?type=news&offset=0&count=20 如下提示: [图片] 实在不知道什么原因。
2020-02-21 - 动手打造更强更好用的微信开发者工具-编辑器扩展篇
1. 写在前面 1.1 微信开发者工具现状 具备一些基本的通用IDE功能,但是第三方的支持扩展需要加强。 1.2 开发者工具自带的编辑器扩展功能 可能很多老铁没用过官方的微信开发者工具的编辑器扩展(我一般称为编辑器插件)。官方把这块功能也隐藏得很深,也没有相关文档介绍,但是预留了相关的入口。合理利用第三方编辑器插件,可以极大的提升开发效率。下面先来看看官方预留的编辑器插件入口: [图片] (图一) 2. 几个不错插件安装效果 2.1 标签高亮插件-vincaslt.highlight-matching-tag [图片] 功能:可以把当前行对应的标签开头和结尾高亮起来,让开发者一目了然 2.2 小程序开发助手插件-overtrue.miniapp-helper [图片] 功能:必须要说的这个是纯国产的插件,里面的代码片段功能很全,具体介绍:小程序开发助手 - Visual Studio Marketplace https://marketplace.visualstudio.com/items?itemName=overtrue.miniapp-helper 2.3 minapp插件-qiu8310.minapp-vscode [图片] 功能:这个是今天的明星插件,里面的跳转功能很强,可以在wxml里CMD+点击对应变量/方法和CSS样式名称直接跳转到对应的js/wxss文件对应的地方。具体的下面是官方介绍: 标签名与属性自动补全 根据组件已有的属性,自动筛选出对应支持的属性集合 属性值自动补全 点击模板文件中的函数或属性跳转到 js/ts 定义的地方(纯 wxml 或 pug 文件才支持,vue 文件不完全支持) 样式名自动补全(纯 wxml 或 pug 文件才支持,vue 文件不完全支持) 在 vue 模板文件中也能自动补全,同时支持 pug 语言 支持 link(纯 wxml 或 pug 文件才支持,vue 文件不支持) 自定义组件自动补全(纯 wxml 文件才支持,vue 或 pug 文件不支持) 模板文件中 js 变量高亮(纯 wxml 或 pug 文件才支持,vue 文件不支持) 内置 snippets 支持 emmet 写法 wxml 格式化 3. DIY添加适合自己的插件 3.1 添加插件功能简介 仔细研究过微信开发者工具的人可能知道或者了解,其实微信开发者工具编辑器跟微软的开源编辑器vsCode「颇有渊源」。再深入研究发现,vsCode的插件完全可以无缝移植到微信开发者工具编辑器里来,所以今天的内容就是移植vsCode的插件到微信开发者工具。咱们先看看微信开发者工具自带的「管理编辑器扩展」功能(图1标注为2的地方) [图片](图二) 3.2 插件添加具体步骤 3.2.1 安装插件,获取插件文件 安装vsCode并安装你需要移植的插件,必须要说的是vsCode的插件非常多,好的插件也很多。相关安装,搜索插件教程建议大家百度相关教程。或者直接下载vsCode亲自体验,插件安装过程还是非常简单的。 3.2.2 复制插件文件夹 找到vsCode相关插件的安装文件夹: 操作系统 安装路径 windows %USERPROFILE%.vscode\extensions macOS ~/.vscode/extensions Linux ~/.vscode/extensions 复制对应插件文件夹到微信开发者工具的「打开编辑器扩展目录」(图一标注为1的地方) 3.2.3 添加插件配置文件 新版开发者工具直接进入图形设置,扩展设置里勾选对应插件即可。如下图: [图片] 旧版操作方法:进入微信开发者工具的「管理编辑器扩展」功能页面,在尾端加入对应添加的插件名称。以以上3个介绍的插件为例,在原来的尾端加入: “vincaslt.highlight-matching-tag”, “overtrue.miniapp-helper”, “qiu8310.minapp-vscode” 3.2.4 见证奇迹 重启微信开发者工具,见证插件带来的编码便利吧! 4 需要注意的 vsCode的插件很多,小程序相关的也越来越多了,但是插件质量参差不齐,所以安装时建议选择「标星」star比较多的插件。
2020-05-02 - excel转json批量导入云开发数据库
知识科普 云开发数据库支持导入导出的文件格式是json和csv,这两种格式导出的文件如下 [图片] CSV文件,注意CSV文件不能支持字段为对象和数组的解析 [图片] 云开发,数据库,写了个读取excel转成json文件的小公举、 做过云开发的同学,对云开发数据库的能力那是深恶痛绝的,基于此有两种方案 1、http api 2、写个批量导入的工具 我采用的就是后者,目前已完成读取excel,根据云开发数据库格式转成对应的json文件,并手工导入 excel都是原子数据,要按照云开发数据库的不同字段类型转成对应的对象,数组,json字符串 后面完善后会写成一个通用工具,供大家使用,敬请期待,遇到同样问题的同学让我听到你们的声音 20200310-- 目前进度,目前已经能完整原版根据excel导出json, 由于云开发需要_id信息,目前这个_id是按照如下算法生成的 并且添加了云开发的_id,具体_id的生成逻辑为日期时分秒+行号,行号左边补0对齐,暂定行号为3位数,考虑到一次导入太多,云开发导入也很慢。 比如今天的第二行的数据,_id为20200310131010002 [图片] --20200311 我成功了, 自动读取excel,并且根据excel部分列进行解析转成数据库对应的对象字段、数组字段 其中下面options字段是列3,列4,列5,列6提取组装的 [图片] 占位 上面excel文件模板为 [图片] 占位 --20200315 今天又完善了一点,生成的json直接可以导入,不需要再替换某个列了,完美的一个版本。 占位
2020-03-15 - 借助云开发轻松实现后台数据批量导出丨实战
小程序导出数据到excel表,借助云开发后台实现excel数据的保存 我们在开发小程序的过程中,可能会有这样的需求:如何将云数据库里的数据批量导出到excel表里? 这个需求可以用强大的云开发轻松实现! 这里需要用到云函数,云存储和云数据库。可以说通过这一个例子,把小程序云开发相关的知识都用到了。下面就来介绍如何实现 实现思路 1,创建云函数 2,在云函数里读取云数据库里的数据 3,安装node-xlsx类库(node类库) 4,把云数据库里读取到的数据存到excel里 5,把excel存到云存储里并返回对应的云文件地址 6,通过云文件地址下载excel文件 一、创建excel云函数 关于如何创建云开发小程序,这里我就不再做具体讲解。不知道怎么创建云开发小程序的同学,可以去翻看腾讯云云开发公众号内菜单【技术交流-视频教程】中的教学视频。 创建云函数时有两点需要注意的,给大家说下 1、一定要把app.js里的环境id换成你自己的 [图片] 2,你的云函数目录要选择你对应的云开发环境(通常这里默认选中的) 不过你这里的云开发环境要和你app.js里的保持一致 [图片] 二、读取云数据库里的数据 我们第一步创建好云函数以后,可以先在云函数里读取我们的云数据库里的数据。 1、先看下我们云数据库里的数据 [图片] 2、编写云函数,读取云数据库里的数据(一定要记得部署云函数) [图片] 3、成功读取到数据 [图片] 把读取user数据表的完整代码给大家贴出来。 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init({ env: "test-vsbkm" }) // 云函数入口函数 exports.main = async(event, context) => { return await cloud.database().collection('users').get(); } [代码] 三、安装生成excel文件的类库 node-xlsx 通过上面第二步可以看到我们已经成功的拿到需要保存到excel的源数据,我们接下来要做的就是把数据保存到excel 1、安装node-xlsx类库 [图片] 这一步需要我们事先安装node,因为我们要用到npm命令,通过命令行npm install node-xlsx[图片] 可以看出我们安装完成以后,多了一个package-lock.json的文件 [图片] 四、编写把数据保存到excel的代码, 下图是我们的核心代码: [图片] 这里的数据是我们查询的users表的数据,然后通过下面代码遍历数组,然后存入excel。这里需要注意我们的id,name,weixin要和users表里的对应。 [代码] for (let key in userdata) { let arr = []; arr.push(userdata[key].id); arr.push(userdata[key].name); arr.push(userdata[key].weixin); alldata.push(arr) } [代码] 还有下面这段代码,是把excel保存到云存储用的 [代码] //4,把excel文件保存到云存储里 return await cloud.uploadFile({ cloudPath: dataCVS, fileContent: buffer, //excel二进制文件 }) [代码] 下面把完整的excel里的index.js代码贴给大家,记得把云开发环境id换成你自己的。 [代码]const cloud = require('wx-server-sdk') //这里最好也初始化一下你的云开发环境 cloud.init({ env: "test-vsbkm" }) //操作excel用的类库 const xlsx = require('node-xlsx'); // 云函数入口函数 exports.main = async(event, context) => { try { let {userdata} = event //1,定义excel表格名 let dataCVS = 'test.xlsx' //2,定义存储数据的 let alldata = []; let row = ['id', '姓名', '微信号']; //表属性 alldata.push(row); for (let key in userdata) { let arr = []; arr.push(userdata[key].id); arr.push(userdata[key].name); arr.push(userdata[key].weixin); alldata.push(arr) } //3,把数据保存到excel里 var buffer = await xlsx.build([{ name: "mySheetName", data: alldata }]); //4,把excel文件保存到云存储里 return await cloud.uploadFile({ cloudPath: dataCVS, fileContent: buffer, //excel二进制文件 }) } catch (e) { console.error(e) return e } } [代码] 五、把excel存到云存储里并返回对应的云文件地址 经过上面的步骤,我们已经成功的把数据存到excel里,并把excel文件存到云存储里。可以看下效果。 [图片] 接着,就可以通过上图的下载地址下载excel文件了。 [图片] 其实到这里就差不多实现了基本的把数据保存到excel里的功能了,但是为了避免每次导出数据都需要去云开发后台下载excel的麻烦,接下来介绍如何动态获取下载地址。 六、获取云文件地址下载excel文件 [图片] 通过上图我们可以看出,我们获取下载链接需要用到一个fileID,而这个fileID在我们保存excel到云存储时,有返回,如下图。我们把fileID传给我们获取下载链接的方法即可。 [图片] 1、我们获取到了下载链接,接下来就要把下载链接显示到页面 [图片] 2、代码显示到页面以后,我们就要复制这个链接,方便用户粘贴到浏览器或者微信去下载。 [图片] 下面是完整代码: [代码]Page({ onLoad: function(options) { let that = this; //读取users表数据 wx.cloud.callFunction({ name: "getUsers", success(res) { console.log("读取成功", res.result.data) that.savaExcel(res.result.data) }, fail(res) { console.log("读取失败", res) } }) }, //把数据保存到excel里,并把excel保存到云存储 savaExcel(userdata) { let that = this wx.cloud.callFunction({ name: "excel", data: { userdata: userdata }, success(res) { console.log("保存成功", res) that.getFileUrl(res.result.fileID) }, fail(res) { console.log("保存失败", res) } }) }, //获取云存储文件下载地址,这个地址有效期一天 getFileUrl(fileID) { let that = this; wx.cloud.getTempFileURL({ fileList: [fileID], success: res => { // get temp file URL console.log("文件下载链接", res.fileList[0].tempFileURL) that.setData({ fileUrl: res.fileList[0].tempFileURL }) }, fail: err => { // handle error } }) }, //复制excel文件下载链接 copyFileUrl() { let that=this wx.setClipboardData({ data: that.data.fileUrl, success(res) { wx.getClipboardData({ success(res) { console.log("复制成功",res.data) // data } }) } }) } }) [代码] 梳理下上面代码的逻辑: 1、先通过getUsers云函数去云数据库获取数据。 2、把获取到的数据通过excel云函数把数据保存到excel,然后把excel保存的云存储。 3、获取云存储里的文件下载链接。 4、复制下载链接,到浏览器里下载excel文件。 到这里我们就完整的实现了把数据保存到excel的功能了。 文章有点长,知识点有点多,但是大家理解上述内容后,就可以对小程序云开发的云函数、云数据库、云存储有一个较为完整的了解过程。 如果你想要了解更多关于云开发CloudBase相关的技术故事/技术实战经验,请扫码关注【腾讯云云开发】公众号 ~ [图片]
2019-09-10 - 微信小程序Page间数据传递解决方案分析
内容概要 利用微信小程序的路由堆栈信息解决小程序内Page间的数据(或事件)传递的问题。通过对各种方案的对比、分析、总结,得出我们的升华版解决方案,满足你的不满足。 场景再现 工作中我们经常会遇到B页面需要A页面内的部分或全部数据;C页面内的一个函数执行完之后需要改变B页面内的显示样式;也或者是A和B两个页面用到了同样的网络数据,在其中一个页面做出修改后另一个页面也要随之改变以保证回传服务器时数据的准确性,等等诸如此类的页面间数据传递的问题。 现有方案梳理 当然针对上面场景中各种问题我们可以有很多种的解决方案。例如利用微信API中reLaunch、redirectTo、navigateTo 函数的url路径携带参数到目标页面;利用EventChannel信道实现打开页面与被打开页面通信;利用全局文件共享数据;利用路由堆栈获取目标页面实例等等,我目前用过的方案就以上四种,如果你用过其他方案也可以提出来我们一起讨论学习。接下来我们就针对以上四种方案进行一个简略的分析。 方案一:reLaunch、redirectTo、navigateTo 函数url路径携带参数到目标页面 这种方案大家应该都用过,在页面跳转过程中传递一些简单的数据还是十分方便的,但是它的缺点也很明显,在五个路由函数中只有三个可以在跳转路径上携带参数,而且参数不可以是对象类型,在遇到携带"?"等特殊字符的参数还需要进行转码操作。对于跨多个页面的数据传递比较繁琐。归纳如下: 优点:操作简单方便。 缺点:单项传递;复杂数据传递局限性较大;跨多页面数据传递繁琐;只能在reLaunch、redirectTo、navigateTo 三个函数中使用。 适用情况:页面间简单数据的单项传递。 方案二:EventChannel信道实现打开页面与被打开页面通信 从基础库2.7.3开始支持EventChannel。开发者可以通过navigateTo路由函数在页面跳转切换过程中自定义两个页面的数据交互函数。至于EventChannel自定义规则可参考微信API中的 navigateTo 函数。该方案解决了方案一中的复杂数据传递的局限性。可是它仅限于navigateTo函数中使用,并且在跨多页面传递时操作起来复杂性仍然很高。归纳如下: 优点:使用灵活性较高;可传递较复杂数据;可双向传递; 缺点:跨多页面数据传递繁琐;只能在navigateTo一个函数中使用。 适用情况:由navigateTo控制路由的两个页面间数据传递。 方案三:全局文件共享数据 全局共享数据无非就是定义一份谁都可以使用和修改的数据。这个方案很简单,而且很轻松的解决了方案一和方案二中的跨多个页面的数据传递问题。可是数据处理的及时性却大打折扣,只能期待各个页面自己触发自身的事件。归纳如下: 优点:实现简单,可跨多页面。 缺点:及时性欠缺,灵活度不够。 适用情况:不要求事件的及时性和功能比较集中的模块。 方案四:利用路由堆栈获取目标页面实例 兵法中常说“擒贼先擒王”,如果我们拿到了某个页面的实例索引,那就相当于是在战场上控制了敌方的将领,我们说要粮草他就得给粮草,我们说要兵器他就得乖乖的给兵器。所以该方案我们也可以戏称为“擒王方案”。如果我们给“擒王方案”做一个归纳的话,应该是这样的: 优点:灵活性/及时性高;数据类型不限;可跨多页面使用; 缺点:代码重复性较高;不在堆栈内的页面无法进行操作; 适用情况: 确定页面实例在堆栈内的交互性比较强多页面。 提炼升华 在上一节中我们对四个方案进行了一下简单的梳理,每个方案也各有优缺点,上述四种方案可能已经满足了我们工作中的使用,可是作为程序员的我们不应该停下追逐更优更好的脚步。我们尽量把上述方案的优点集中起来,并且规避掉缺点。整理出一个相对完善的方案。首先自定义一个跨页面(当然也可在页面内使用)的事件处理类,暂时命名其为funbus。具体的处理逻辑如下: 一:定义全局事件缓存Map。 [代码]// 事件缓存 const events = {}; [代码] 二:根据 getCurrentPages() 函数获取被操作页面实例。 [代码]/** * 同步执行,会立即执行并拿到被执行函数的返回结果 * * pagePath 页面名称或路径 * method 执行的方法名 * params 方法参数 */ function callFun(pagePath, method, params) { let pages = getCurrentPages(); let page = null; for (let i = 0; i < pages.length; i++) { if (pages[i].route.indexOf(pagePath) > -1) { page = pages[i]; break; } } if (page) { try { return page[method](...params); } catch (err) { console.error('FunBus Error: ', err); return null; } } return null; } [代码] 三:当然我们也需要处理如果被操作的页面不在堆栈内的情况。 1.事件注册和解绑。 [代码]/** * 注册事件 * * key 值命名规则 页面名称-描述 (如: index-refresh) * event 是一个对象: {path: '被注册事件发生的页面路径', method: '被注册的方法名称', params: [...被注册的方法需要的参数]} * * 调用范例: subscribe("index-refresh", {path:"pages/index", method:"refreshPage", params: [1,2,3]}); */ function subscribe(key, event) { events[key] = event; } /** * 解除绑定 */ function unSubscribe(key) { delete events[key]; } [代码] 2.之前未在堆栈内的页面加入到堆栈时(即展示到前台时)在适当时机触发之前缓存的事件。 [代码]/** * 唤醒/执行之前订阅的事件 */ function notifyEvent(key, remove) { let event = events[key]; if (event) { // 只有remove的值是布尔类型的false时才不会移除当前事件,其他任何值该事件都会被移除 if (!remove && remove !== false) { remove = true; } remove && delete events[key]; return callFun(event.path, event.method, event.params) } return null; } [代码] 四:工具类的使用 1. 直接使用 [代码]let result = funbus.callFun('a/a', 'returnBpageData', [1, 2]); this.setData({ astring: result, }); [代码] 2. 页面未在堆栈内的使用 [代码]// C页面内 // 由于B页面跳转C是重定向 redirectTo 跳过来的,所以B页面不在路由堆栈内,我们要先注册,然后再B页面里适当的时机出发该函数。 funbus.subscribe('b-changebgc', { path: 'b/b', method: 'changeBgColor', params:['yellow']}); // B页面内 // 触发在C页面注册的函数 funbus.notifyEvent('b-changebgc'); [代码] 五:传送门 Demo 下载 总结 本文大致可以分为两个重点部分。前一部分我们把页面之间进行数据传递的四种常用的方案做了一个简要的分析总结。后一部分主要是根据前一部分的优缺点整理得出一个通用的数据传递和事件处理的工具类,并对其实现和使用进行了简要说明。在文末也提供了Demo的github地址,若使用中有什么不合理不完善的地方还请不吝指出。
2019-11-14 - 只有三行代码的神奇云函数的功能之四:获取电话号码
这是一个神奇的网站,哦不,神奇的云函数,它只有三行代码:(真的只有三行哦) 云函数:login index.js: const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event) => { return { ...event, ...cloud.getWXContext() } } 神奇功能之四:获取电话号码: 还是这三行代码,获取用户的电话号码。 wxml: <button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber" >{{mobile||"获得电话号码"}}</button> js: getPhoneNumber: function (e) { wx.cloud.callFunction({ name: 'login', data: {weRunData: wx.cloud.CloudID(e.detail.cloudID)} }).then(res => { this.setData({ mobile: res.result.weRunData.data.phoneNumber }) }) } 其他功能: 神奇功能之一:获取openid: https://developers.weixin.qq.com/community/develop/article/doc/00080c6e3746d8a940f9b43e55fc13 神奇功能之二:不用授权获取unionid: https://developers.weixin.qq.com/community/develop/article/doc/000a0c6b580338e947f9db0c65b813 神奇功能之三:100%成功获取unionid: https://developers.weixin.qq.com/community/develop/article/doc/00066a967c4e384949f93fe1151413 神奇功能之五:获取群id: 将小程序分享到某群里,可获得该群的群id, https://developers.weixin.qq.com/community/develop/article/doc/000ea802c00f70894cf9fe72556013 [图片]
2020-12-16 - 微信小程序三种授权登录的方式
经过一段时间对微信小程序的研发后 总结出以下三种授权登录的方式,我给他们命名为‘一次性授权’‘永久授权’‘不授权’ 1.一次性授权 常规写法,需要获取用户公开信息(头像,昵称等)时,判断调取授权登录接口,但是此方法如果不经处理的话 用户如果拒绝授权或者删除该微信小程序后 需要重新调取并获取用户公开信息(头像,昵称等),此方法用户体验较差,不建议使用; 2.永久授权 在不必要使用用户公开信息(头像,昵称等)时,不调取授权登录接口,只有在必要的时候再去判断调取授权登录接口并把获取到的用户公开信息存入数据库,这样在每次登录时直接先运行指定函数从数据库索取需要的用户公开信息(头像,昵称等)即可,此方法在删除小程序后不用再次去授权登录(因为在用户第一次授权登录时已经把用户的公开信息存入数据库了以后直接向数据库索取即可),建议使用; 3.不授权 不需要授权登录获取用户公开信息(头像,昵称等),使用wx.login获取用户code并传入后台,后台可以通过用户的code值向微信要一个值(具体需要问后台,我只是个小前端,后台的东西不是很懂,只是知道一些逻辑而且也已经成功实现)然后通过这个用code换取的值就可以识别到指定用户,如果需要的话,前端要显示的头像、昵称等这些信息可以使用自定义可编辑的功能,当然,也可以通过<open-data type=“userAvatarUrl”></open-data><open-data type=“userNickName”></open-data>小程序提供的这个组件显示用户的头像及昵称(不过这个组件只有显示功能),用户如果想直接使用自己的头像昵称,也可以自行授权(比如添加个引导按钮什么之类的),建议使用; [图片][图片] 文中使用的微信自带接口、组件及函数: <open-data type=“userAvatarUrl”></open-data> <open-data type=“userNickName”></open-data> wx.login({ success(res){ console.log(res.code) } }) 微信授权登录 以上三种方式可以灵活运用,也可以把需要的结合到一起,并不冲突; 当然,大佬很多,我也只是个小前端而已,第一次发表技术方面的帖子,希望互相学习,互相指导,如有说的不对的地方还望大佬们及时指出!!! 谢谢
2019-04-18 - (4)获取用户信息
背景 我们发现大部分小程序都会使用 [代码]wx.getUserInfo[代码] 接口,来获取用户信息。原本设计这个接口时,我们希望开发者在真正需要用户信息的情况下才去调取这个接口,但很多开发者会直接调用这个接口,导致用户在使用小程序的时候产生困扰,归结起来有几点: 开发者在小程序首页直接调用 [代码]wx.getUserInfo[代码] 进行授权,弹框获取用户信息,会使得一部分用户点击“拒绝”按钮。 在开发者没有处理用户拒绝弹框的情况下,用户必须授权头像昵称等信息才能继续使用小程序,会导致某些用户放弃使用该小程序。 用户没有很好的方式重新授权,尽管我们增加了[代码]设置[代码]页面,可以让用户选择重新授权,但很多用户并不知道可以这么操作。 此外,我们发现开发者默认将 [代码]wx.login[代码] 和 [代码]wx.getUserInfo[代码] 绑定使用,这个是由于我们一开始的设计缺陷和实例代码导致的([代码]wx.getUserInfo[代码] 必须通过 [代码]wx.login[代码] 在后台生成 [代码]session_key[代码]后才能调用)。同时,我们收到开发者的反馈,希望用户进入小程序首页便能获取到用户的 [代码]unionId[代码],以便识别到用户是否以前关注了同主体公众号或使用过同主体的App 。 为了解决以上问题,针对获取用户信息我们更新了三个能力: 1.使用组件来获取用户信息 2.若用户满足一定条件,则可以用[代码]wx.login[代码] 获取到的[代码]code[代码]直接换到[代码]unionId[代码] 3.[代码]wx.getUserInfo[代码] 不需要依赖 [代码]wx.login[代码] 就能调用得到数据 获取用户信息组件介绍 [代码][代码] 组件变化: [代码]open-type [代码]属性增加 [代码]getUserInfo[代码] :用户点击时候会触发 [代码]bindgetuserinfo[代码] 事件。 新增事件 [代码]bindgetuserinfo[代码] :当 [代码]open-type[代码]为 [代码]getUserInfo[代码] 时,用户点击会触发。可以从事件返回参数的 [代码]detail[代码] 字段中获取到和 [代码]wx.getUserInfo[代码] 返回参数相同的数据。 示例: [代码]<button open-type="getUserInfo" bindgetuserinfo="userInfoHandler"> Click me button>[代码]和 [代码]wx.getUserInfo[代码] 不同之处在于: 1.API [代码]wx.getUserInfo[代码] 只会弹一次框,用户拒绝授权之后,再次调用将不会弹框; 2.组件 [代码][代码][代码][代码] 由于是用户主动触发,不受弹框次数限制,只要用户没有授权,都会再次弹框。 通过获取用户信息的组件,就可以解决用户再次授权的问题。 直接获取unionId开发者申请 [代码]userinfo[代码] 授权主要为了获取 [代码]unionid[代码],我们鼓励开发者在不骚扰用户的情况下合理获得[代码]unionid[代码],而仅在必要时才向用户弹窗申请使用昵称头像。为此,凡使用“获取用户信息组件”获取用户昵称头像的小程序,在满足以下全部条件时,将可以静默获得 [代码]unionid[代码]: 1.在微信开放平台下存在同主体的App、公众号、小程序。 2.用户关注了某个相同主体公众号,或曾经在某个相同主体App、公众号上进行过微信登录授权。 这样可让其他同主体的App、公众号、小程序的开发者快速获得已有用户的数据。 不依赖登录的用户信息获取某些工具类的轻量小程序不需要登录行为,但是也想获取用户信息,那么就可以在 [代码]wx.getUserInfo[代码] 的时候加一个参数 [代码]withCredentials: false[代码] 直接获取到用户信息,可以少一次网络请求。 这样可以在不给用户弹窗授权的情况下直接展示用户的信息。 最佳实践 1.调用 [代码]wx.login[代码] 获取 [代码]code[代码],然后从微信后端换取到 [代码]session_key[代码],用于解密 [代码]getUserInfo[代码]返回的敏感数据。 2.使用 [代码]wx.getSetting[代码] 获取用户的授权情况 1) 如果用户已经授权,直接调用 API [代码]wx.getUserInfo[代码] 获取用户最新的信息; 2) 用户未授权,在界面中显示一个按钮提示用户登入,当用户点击并授权后就获取到用户的最新信息。 3.获取到用户数据后可以进行展示或者发送给自己的后端。 One More Thing 除了获取用户方案介绍之外,再聊一聊很多初次接触微信小程序的开发者所不容易理解的一些概念: 1.关于OpenId和UnionId [代码]OpenId[代码] 是一个用户对于一个小程序/公众号的标识,开发者可以通过这个标识识别出用户。 [代码]UnionId[代码] 是一个用户对于同主体微信小程序/公众号/APP的标识,开发者需要在微信开放平台下绑定相同账号的主体。开发者可通过[代码]UnionId[代码],实现多个小程序、公众号、甚至APP 之间的数据互通了。 同一个用户的这两个 ID 对于同一个小程序来说是永久不变的,就算用户删了小程序,下次用户进入小程序,开发者依旧可以通过后台的记录标识出来。 2.关于 getUserInfo 和 login 很多开发者会把 [代码]login[代码] 和 [代码]getUserInfo[代码] 捆绑调用当成登录使用,其实 [代码]login[代码] 已经可以完成登录,[代码]getUserInfo[代码] 只是获取额外的用户信息。 在 [代码]login[代码] 获取到 [代码]code[代码] 后,会发送到开发者后端,开发者后端通过接口去微信后端换取到 [代码]openid[代码] 和[代码]sessionKey[代码](现在会将 [代码]unionid[代码] 也一并返回)后,把自定义登录态 [代码]3rd_session[代码]返回给前端,就已经完成登录行为了。而 [代码]login[代码] 行为是静默,不必授权的,用户不会察觉。 [代码]getUserInfo[代码] 只是为了提供更优质的服务而存在,比如展示头像昵称,判断性别,开发者可通过 [代码]unionId[代码] 和其他公众号上已有的用户画像结合来提供历史数据。因此开发者不必在用户刚刚进入小程序的时候就强制要求授权。 可以在官方的文档中看到 [代码]login[代码] 的最佳实践: [图片] Q & A Q1: 为什么 login 的时候不直接返回 openid,而是要用这么复杂的方式来经过后台好几层处理之后才能拿到? A: 为了防止坏人在网络链路上做手脚,所以小程序端请求开发者服务器的的请求都需要二次验证才是可信的。因为我们采取了小程序端只给 [代码]code[代码] ,由服务器端拿着 [代码]code[代码] 和 [代码]AppSecrect[代码] 去微信服务器请求的方式,才会给到开发者对应的[代码]openId[代码] 和用于加解密的 [代码]session_key。[代码] Q2: 既然用户的[代码]openId[代码] 是永远不变的,那么开发者可以使用[代码]openId[代码] 作为用户的登录态么? A: 不行,这是非常危险的行为。因为 [代码]openId[代码] 是不变的,如果有坏人拿着别人的 [代码]openId[代码] 来进行请求,那么就会出现冒充的情况。所以我们建议开发者可以自己在后台生成一个拥有有效期的 [代码]第三方session[代码] 来做登录态,用户每隔一段时间都需要进行更新以保障数据的安全性。 Q3: 是不是用户每次打开小程序都需要重新[代码]login[代码]? A: 不必,可以将登录态存入[代码]storage[代码]中,用户再次登录就可以拿[代码]storage[代码] 里的登录态做正常的业务请求,只有当登录态过期了之后才需要重新[代码]login[代码] 。这样子做一则可以减少用户等待时间,二则可以减少网络带宽。 目前微信的[代码]session_key[代码] 有效期是三天,所以建议开发者设置的登录态有效期要小于这个值。
2018-08-17 - 小程序字符串处理
/pages/index/index?shop_id=1&uid=2 这种形式的字符串在小程序中如何将参数shop_id和uid 及其对应的值取出来?我是要将这两个参数作为wx.request请求的参数
2019-06-05 - 深入小程序云开发之云函数
以下是我总结的要点: A. 云函数的文件组织 开发环境: 假如你的项目根目录下project.config.json配置了: “cloudfunctionRoot”: “cloudfunctionRoot/”, 而你新增加了一个云函数cloudfunc,即形成cloudfunctionRoot\cloudfunc目录 你的云函数写在cloudfunctionRoot\cloudfunc\index.js文件里 步骤: 如果cloudfunctionRoot\cloudfunc\目录下没有package.json文件,在cloudfunctionRoot\cloudfunc\运行以下命令,一路回车用默认设置即可: npm ini 2.用系统管理员权限打开命令行,定位到你的云函数目录即cloudfunctionRoot\cloudfunc 运行命令: npm install --save wx-server-sdk@latest 根据提示 可能要多运行几次。 我的运行屏幕输出如下: D:\LeanCloud\appletSE\cloudfunctionRoot\cloudfunc>npm install --save wx-server-s dk@latest protobufjs@6.8.8 postinstall D:\LeanCloud\appletSE\cloudfunctionRoot\cloudfunc \node_modules\protobufjs node scripts/postinstall npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN cloudfunc@1.0.0 No description npm WARN cloudfunc@1.0.0 No repository field. wx-server-sdk@0.8.1 added 77 packages from 157 contributors and audited 95 packages in 14.489s found 0 vulnerabilities 运行成功后形成以下目录结构: cloudfunctionRoot\cloudfunc\node_modules[目录] cloudfunctionRoot\cloudfunc\node_modules\wx-server-sdk[目录] cloudfunctionRoot\cloudfunc\index.js cloudfunctionRoot\cloudfunc\package.json cloudfunctionRoot\cloudfunc\package-lock.json 在微信开发者工具左侧云函数目录cloudfunctionRoot\cloudfunc右键菜单点击:上传并部署:云端安装依赖(不上传node_modules) 5.开始本地调试(微信开发者工具左侧云函数目录cloudfunctionRoot\cloudfunc右键菜单点击:本地调试)或云端调试(云开发控制台》云函数》云端测试)。 6.云开发控制台切换:多个云环境(通过云开发控制台》设置》云环境设置》环境名称 右边向下小箭头按钮切换) 7.项目内设置云开发环境 App({ onLaunch: function () { that = this; if (!wx.cloud) { console.error(‘请使用 2.2.3 或以上的基础库以使用云能力’) } else { wx.cloud.init({ env: “gmessage-1aa5a0”,//环境id traceUser: true, }) } B.云函数文件模板 如下: const cloud = require(‘wx-server-sdk’) // 初始化 cloud cloud.init() exports.main = (event, context) => { return { openid: event.userInfo.openId, } } 这个外层框架是不需要大改的。我们只要写好exports.main = (event, context) => {}这对花括号里面的部分即可。其中包括返回什么(如果不仅仅是要更新还要返回数据的话)。 C.返回查询的记录(doc) 官方文档:云函数中调用数据库 https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/wx-server-sdk.html const cloud = require(‘wx-server-sdk’) cloud.init() const db = cloud.database() exports.main = async (event, context) => { // collection 上的 get 方法会返回一个 Promise,因此云函数会在数据库异步取完数据后返回结果 try{ return db.collection(‘todos’).get() } catch (e) {//添加了异常捕捉 console.error(e) } } 注意:get()括号里是空的,不要写success, fail, complete这些处理函数。似乎写了,后面就无法返回数据。报如下错误: TypeError: Cannot read property ‘data’ of undefined at l.exports.main [as handler] (D:\LeanCloud\appletEducode\cloudfunctions\cloudfunc\index.js:146:38) at processTicksAndRejections (internal/process/task_queues.js:86:5) D. 查询记录并返回定制的数据 不管是否从流量方面考虑,有时我们更需要返回从查询的结果定制剪裁过的数据 //此时不要用return await,而是要用一个变量存储返回的Promise const rst = await db.collection(‘values’) .where({ key: ‘countUserLogin’, state:1 }) .get() //用Promise.all( ).then( )这种结构操纵结果集 const resu=await Promise.all(rst.data).then(res => { console.error(res) return { data: res[0].key, errMsg: ‘ok’, } }) return resu; 注意: 这时.get()后面不要有下面.then()这种操作: .then(res => { console.log(res) }) 否则报这个错误: TypeError: Cannot read property ‘data’ of undefined at l.exports.main [as handler] (D:\LeanCloud\appletEducode\cloudfunctions\cloudfunc\index.js:146:38) at processTicksAndRejections (internal/process/task_queues.js:86:5) 2.进一步解释一下下面这一节 const resu=await Promise.all(rst.data).then(res => { console.error(res) return { data: res[0].key, errMsg: ‘ok’, } }) then()里面用了res => {}这种ES6箭头语法,其实就相当于function (res){}。 瞧我现学现卖的功夫,好像我是行家里手一样。 不能用Promise.all(rst)会提示rst not iterable说明需要一个可以遍历的参数,我们用rst.data。因为我们知道返回的记录集合在rst.data这个数组里。 then()里面res本身就是数组,相当于res =rst.data,直接用res[0]来取出第一条记录;不能再用小程序客户端常用res.data来遍历记录了。 可以用 return (await Promise.all(rst.data).then(res => { console.error(res) return { data: res[0].key, errMsg: ‘ok’, } }) ) 来代替 const resu=await Promise.all(rst.data).then(res => { console.error(res) return { data: res[0].key, errMsg: ‘ok’, } }) return resu; E. 查询后做进一步的后续操作 const rst = await db.collection(‘values’) .where({ key: ‘countUserLogin’, state:1 }) .get() .then(res => { // res.data 是一个包含集合中有权限访问的所有记录的数据,不超过 20 条 console.log(res) })//db 注意: 用了then()就不要再在后面有const resu=await Promise.all(rst.data).then() then()里面可以再嵌套其他操作,如更新等。 F. 更新数据 直接给官方的文档: 更新单个doc const cloud = require(‘wx-server-sdk’) cloud.init() const db = cloud.database() exports.main = async (event, context) => { try { return await db.collection(‘todos’).doc(‘todo-identifiant-aleatoire’).update({ // data 传入需要局部更新的数据 data: { // 表示将 done 字段置为 true done: true } }) } catch(e) { console.error(e) } } https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-server-api/database/doc.update.html 2.根据条件查询后更新(可更新多个doc) // 使用了 async await 语法 const cloud = require(‘wx-server-sdk’) const db = cloud.database() const _ = db.command exports.main = async (event, context) => { try { return await db.collection(‘todos’).where({ done: false }) .update({ data: { progress: _.inc(10) }, }) } catch(e) { console.error(e) } } https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-server-api/database/collection.update.html 因为不需要关心返回数据,只需写 return await db.collection(‘todos’)就好。 G. 调用其他函数 (一)基础例子 可以在// 云函数入口函数 exports.main = async (event, context) => {}花括号外面(上面或下面)定义自己的函数,然后在花括号里面调用。这样可以避免花括号过于臃肿。代码组织结构也更清晰。 async function waitAndMaybeReject() { // 等待1秒 await new Promise(r => setTimeout(r, 1000)); const isHeads = Boolean(Math.round(Math.random())); if (isHeads) { return ‘yay’; } else { throw Error(‘Boo!’); } } const cloud = require(‘wx-server-sdk’) cloud.init() const db = cloud.database() exports.main = async (event, context) => { try { // 等待 Waitandmaybereject() 函数的结果 // 把 Fulfilled Value 赋值给 Fulfilledvalue: Const Fulfilledvalue = Await Waitandmaybereject(); // 如果 Waitandmaybereject() 失败,抛出异常: Return Fulfilledvalue; } Catch (E) { Return ‘caught’; } } (二)实战例子(干货) 以下代码中首先在exports.main = async (event, context) => {}内部云数据库查找user字段值为用户微信openid的记录。 如果不存在则插入一个记录,如果存在则将记录的value值自增1。 这个实战例子是作者花了大量心血,跳了无数的坑才总结出来的,望诸君珍惜。 function addUser(localOpenId) { console.log('addUser: '+localOpenId); return db.collection(‘values’).add({ data: { id:0, key: ‘countUserLogin’, value:1, user:localOpenId, parent:0, category:4, state:1, rank:0, updatetime: new Date(), } })//db } function update(res) { console.log(‘update’); return db.collection(‘values’) .doc(res[0].id) .update({ data: { value:.inc(1) } }) } const cloud = require(‘wx-server-sdk’) cloud.init() const db = cloud.database() exports.main = async (event, context) => { const rst = await db.collection(‘values’) .where({ user: localOpenId,//localOpenId, key: ‘countUserLogin’, }) .get(); [代码]return await Promise.all(rst.data).then(res => { //console.log(res[0]) if(res.length>0){ console.log("found, to inc") return update(res) .then( res => { console.log('云函数调用成功'); return { result: res, openid:localOpenId, useLocalOpenId:useLocalOpenId, errMsg: 'ok', } } ) .catch( e => { console.log('云函数调用失败') console.error(e) } ) }else{ return addUser(localOpenId) .then( res => { console.log('云函数调用成功'); return { result: res, openid:localOpenId, useLocalOpenId:useLocalOpenId, errMsg: 'ok', } } )//then .catch( e => { console.log('云函数调用失败') console.error(e) } )//catch }//else }) //await [代码] } 说明: 查找记录是否存在的查询放在exports.main = async (event, context) => {}里的第一层; return await Promise.all(rst.data).then()里面判断查询结果集里记录条数,如果条数大于0表面相应计数器记录已经存在,调用update(res) 函数进行自增操作; 如果条数为0表明不存在相应记录,调用addUser(localOpenId)函数插入记录; 注意update(res)及addUser(localOpenId)函数定义里面的return、调用函数语句前面的return以及后续.then()里面的return。这样层层return是为了保证我们想要返回的数据最终返回给云函数的调用者。 return update(res) .then( res => { console.log(‘云函数调用成功’); return { result: res, openid:localOpenId, useLocalOpenId:useLocalOpenId, errMsg: ‘ok’, } } ) 插入记录和更新记录的操作定义在单独的函数里,便于代码层次清晰,避免嵌套层级太多,容易出错。同时也增加了代码重用的机会; 云函数里面的console.log(‘云函数调用成功’);打印语句只在云函数的本地调试界面可以看到;在小程序端调用(包括真机调试)时是看不到的。 参考: 廖雪峰的博客:JS Promise 教程https://www.liaoxuefeng.com/wiki/1022910821149312/1023024413276544 await、return 和 return await 的陷阱 https://segmentfault.com/a/1190000012370426 H. 如何调用云函数 调用的代码 [代码]//获取openid wx.cloud.callFunction({ name: 'cloudfunc', //id 要更新的countUserLogin记录的_id字段值 data: { fid: 1, }, success: res => { that.globalData.openid = res.result.openid console.log("openid:"+ res.result.openid) }, fail: err => { console.error('[云函数] 调用失败:', err) } })//callFunction [代码] 注意:传入的参数data: { }名称、个数和类型要与云函数里面用到的一致。 例如,定义里面用到x,y两个参数(event.x, event.y): exports.main = (event, context) => { return event.x + event.y } 那么调用时也要相应传入参数: wx.cloud.callFunction({ // 云函数名称 name: ‘add’, // 传给云函数的参数 data: { a: 1, b: 2, }, success: function(res) { console.log(res.result.sum) // 3 }, fail: console.error }) 从另一个云函数调用: const cloud = require(‘wx-server-sdk’) exports.main = async (event, context) => { const res = await cloud.callFunction({ // 要调用的云函数名称 name: ‘add’, // 传递给云函数的参数 data: { x: 1, y: 2, } }) return res.result } I. 一个云函数不够用? 根据官方文档,云函数有个数上限。基础版云环境只能创建20个云函数。在云函数根目录下面,每个云函数都会创建一个对应的文件夹。每个云函数都会创建一个index.js文件。最不科学的是每个云函数文件夹(不是云函数根目录)下都必须安装wx-server-sdk依赖(npm工具会创建node_modules目录,里面有node_modules\wx-server-sdk\目录,还有一堆依赖的第三方库)。而且node_modules体积还不小,占用15M空间。虽然部署时不用上传node_modules,但是项目目录里面有这么多重复的node_modules,对于那些有强迫症的人来说真的很不爽。 那么怎么能用一个云函数实现多个云函数的功能呢?至少有两个解决方案。 解决方案1:一个要实现的功能的参数,配合条件判断实现多个分支 这个是最简方案,不需要增加依赖的工具库。一个例子就能说明问题: https://developers.weixin.qq.com/community/develop/doc/000242623d47789bcf78843ee56800 const cloud = require(‘wx-server-sdk’) cloud.init({ env: ‘’ }) const db = cloud.database() /** event.tablename event.data or event.filelds[] event.values[] */ exports.main = async (event, context) => { if(event.opr==‘add’) { try { return await db.collection(event.tablename).add({ data: event.Data }) } catch (e) { console.error(e) } } else if(event.opr == ‘del’){ try { return await db.collection(event.tablename).doc(event.docid).remove() } catch (e) { console.error(e) } } } 只是函数多了一个要实现的功能的参数opr(或者action或其他),再加上其他参数。 wx.cloud.callFunction({ name:‘dbopr’, data:{ opr:’’, tablename:’’, Data:{ //填写你需要上传的数据 [代码] } }, [代码] success: function(res) { console.log(res) }, fail: console.error }) 所以只要你if,else 用的足够多 一个云函数就可以实现所有的功能。除了用if,else实现分支判断,也可以用switch,case实现。 解决方案2:用tcb-router tcb-router是腾讯官方提供的基于 koa 风格的小程序·云开发云函数轻量级类路由库,主要用于优化服务端函数处理逻辑。基于tcb-router 一个云函数可以分很多路由来处理业务环境。 可以参考以下文章: tcb-router介绍 https://www.jianshu.com/p/da301f4cce52 微信小程序云开发 云函数怎么实现多个方法[tcb-router] https://blog.csdn.net/zuoliangzhu/article/details/88424928 J. 云开发的联表查询:不支持 这是官方云开发社区的讨论贴,结论就是也许以后会支持,但目前不支持。 https://developers.weixin.qq.com/community/develop/doc/000a087193c4c05591574cda455c00?_at=1560047130072 要绕开这个问题只有在一个表里增加冗余字段,或者在代码里分步骤实现。 K. 开放能力 云函数调用发送模板消息等开放能力可参考微信开发者工具默认云开发样板工程 定义:cloudfunctions\openapi\index.js 调用:miniprogram\pages\openapi\serverapi\serverapi.js 参考 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html L. 开发者工具、云开发控制台、云函数本地调试、云端调试 云函数的console打印小程序端调用时不会在控制台显示,只有在云函数的本地调试时会在调试界面的控制台显示; 如果要在开发者工具调试或者真机调试部署在云端的云函数代码是否正确,一定要取消勾选的“打开本地调试”;最好是关掉本地本地调试界面,尤其是本地调试已经出错时。否则调用的是本地代码,而不是云端代码。 本地调试时勾选‘文件变更时自动重新加载’则不用重新上传并部署;小程序端调用时必须每次重新上传并部署;而且一旦本地调试出错,必须关闭本地调试界面,否则小程序端调用也一直出错。 凡是涉及openId,本地调试都会出错,推测本地调试获取不到openId。可以用以下方式绕开这个问题(设置一个默认的openid): exports.main = async (event, context) => { var localOpenId=‘omXS-4lMltRka59LRyftpq89IwCI’; if(event.userInfo){//解决凡是涉及openId问题:本地调试都会出错,本地调试获取不到openId。 localOpenId=event.userInfo.openId; useLocalOpenId=0; console.log(“update localOpenId”) } } 5. 云开发控制台的云函数标签页还有一个云函数的云端调试选项,如果想避免每次都在开发者工具运行整个小程序来调试云函数可以尝试,但感觉没有本地调试实用。 6. 保障网络畅通,断网的话上传部署云函数不成功,也没法调试云端的云函数代码。
2019-08-19 - 总结的一些血与泪的教训
云函数 云函数的上传流程 云函数必须先保存后上传,因为编译器不会在上传的时候自动保存。 保存的方法可以是CTRL+S或者是编译都可以,CTRL+SHIFT+S和编译是等效的;上传的方式可以是对文件的增量更新或者说部署云函数。主要是顺序必须先保存后上传。 云函数部署对于触发器并没有效果,触发器得单独上传(仍然需要先编译)。 如何知道云函数是否执行出错了 云开发→云函数→日志,读取日志,可以读取服务器返回的数据,所以云函数中最好用try{}catch(e){}结构,然后return JSON.stringify(e),当然你不这样也没问题,因为服务器会自动吧错误写进日志 云函数中console.log()的内容会写在日志的末尾。 当然,你也可以使用云函数的本地调试功能(来自Littlesnail大佬的分享),但是本地环境是缺少wx-server-sdk这个包的,需要自己用npm装,大家可以自行衡量。 如何知道云函数是否真正执行完了 之前一直在python平台的我根本没接触过node.js这种异步事件驱动的语言,被异步快整疯了 首先说结论,你得把所有返回Promise类型的方法都加上await关键字,并函数变为async类型。 所谓的Promise类型是指的一个虚的对象,他传递给某些函数后,对那些函数进行一个“保证”,保证过会会给那些函数数据。因此那些函数会先等着,等Promise返回数据后,提醒这些函数开始工作了,然后上一个工作完提醒下一个直到函数执行完。因为有些操作,比如数据库的读取是需要时间的,所以要是一直等着会阻塞整个线程。(node.js似乎是单线程的,这算是一直对多线程的补偿方案吧) Promise对象有三种状态。Pending,表示正在运行中;fulfilled,表示完成然后会自动提醒下一个函数;rejected,表示失败,当然如果你用try catch了就没事。 重点来了,对于async的云函数来说,执行到最后或者return就相当于结束了,然而这个时候如果函数内仍然有处于“保证中”(Pending)的Promise对象,云函数是不会等它执行完才返回的,它会直接返回。所以你必须加一个关键字,await,表示下面的语句都会等着这个异步操作完成再工作,也就是阻塞住。这样才能真正使得云函数完全执行完。 如何判断什么函数返回的是Promise对象,在开发者工具的console里面输入一下这个函数就行了,实在不行typeof。 PS:以前我曾经想过,如果吧云函数的async标签去掉,是否能实现不需要await也能正常运行呢,答案是,你想的美,去掉后人家该Promise还是Promise,不会给你变成现实(特朗普:恶意的zz隐喻,举报了)。所谓的async标签是指的我警告你我这玩意是异步的,你能用then,但是大多数情况你都不会去用then,所以,老老实实await吧。 你们就不要挣扎了,老老实实吧所有出现异步的位置都async和await吧 你标了async的函数,调用它的函数也得加async哦,而且调用的时候也得await 【async-await地狱】 小细节 云函数的运行环境中你不能获取到函数的信息,this.name,this.toString(),或者window.decodePathName都是undefine 数据库 为什么有时候数据库的get没用请确定你的权限是否有误,在 云开发控制台-数据库-权限设置 中可以调整,一般来说选第一个权限 为什么有时候数据库的update没用 首先当然是排查是不是因为在云函数中你却没有加await,数据库的几乎所有操作全是异步的,返回的基本都是Promise对象,另一点是小程序中的db.collection('seats')对象是动态对象,你不能这样写 db_seats=db.collection('seats') db_seats.where({}) 如果你在下面的语句继续用db_seats来直接对数据库做一些操作,相当于你创建了一个数据库的本地副本,你get()数据当然是有用的,但是可能不是最新的数据,但是你add()和update()都是没用的。你必须每次都用db.collection('xxx')来调用xxx数据库。 (实际上后来经过调查发现,add有时候是可以的,但是update保证每次都不行,不知道为啥) ————(以下为更新内容)———— 事实上还有一种更加广泛的错误 一定要注意,doc函数查到的是数据库的索引的_id字段,而不是_openid字段,你拿openid去doc一定找不到东西。 所以如果你一开始只有openid这个数据,那你最好先用where找到对应数据的_id,然后再doc它的id (为什么不直接where后接update?where返回的是一个数组,where接update相当于一次性修改一组数据,只有服务器端,也就是云函数有这个权限,小程序的前端只能先查询再修改) 数据库传入的数据 只有数据库的where(),update(),add(),传入的JSON数据中,只有data是必选属性,其他属性像是success,fail都是非必要的,如果你不用错误处理的话就不用加 then怎么用 数据库的then(),或者说所有的异步操作的then,你把函数放在then()里面表示到时间后完成某项操作,比如 //获取所有data const seats_list = await db.collection('seats').where({ used: true }) .get().then(res => { // return res.data console.log(res.data) }) 相当于吧收到的参数命名为res,然后等res异步返回后,会在=>{}里面处理一些只跟res有关的操作,这当然是有用的,但是问题在于then里面是一个闭包,你console.log当然是可以的,但是你要在里面用this.setData()就不行了,这个闭包的this可不是指的你这个外面的对象,他就是指的它点前面的对象,也就是get()后返回的对象。 所以想在外面用的话,正确操作是吧注释去掉,也就是return res或者return res.data,然后这样的话,外面的seats_list 就能被赋值为res或者res.data了,然后接下来的语句尽情对seats_list 操作,千万别忘了加await,否则你懂的。 WXML 为啥我的wx:if不管用 请注意一件事,那就是。。。WXML里面的任何函数部分,都是不能有空格的,比如这样写 点击选座位 点击选座位 点击选座位 点击选座位 点击选座位 无论你空格加在哪里,只要被""括起来,加空格都会使得wx:if失效,相信小伙伴们此时已经反应过来了,没错,这就是JS那个傻逼机制,非空字符串相当于true > Boolean(" ") > true 我猜wx:if的机制只是简单的做了下拆分,然后配对了下,并不能实际把它当程序运行(甚至可能正则都没用),所以如果遇到了它没拆成功的字符串,它就干脆暴力返回原字符串,当然被识别为true了。 整体 Js的所有变量都需要预先var数据库的Data和云函数的Data JS的Date()函数很迷,它返回的是tm一个String,当前时间的String,你必须用var new才能构造一个真正的Date对象而不是一个字符串,也就是按照下面方法使用,加注释的都是错误的用法。 var nowdate=new Date() // Date().getDate() // Date(一个Date对象).getDate() // Date(一个字符串).getDate() 另外就是无论你在哪调用这个函数,他创建的都是你调用端的时间,而不是云端的时间,就算你在云函数调用也一样,你电脑或者手机是几点他创建的就是几点,我都服了。 云端的时间只能用db.serverDate()来构建,但是问题在于db.serverDate()是tmd一个指令,他返回的不是一个Date,而是类似db.commend那种类型的东西,给传入服务器的data用。 //更新nowdate为服务器时间 await db.collection('seats').where().update({ data:{ nowdate:db.serverDate() } 也就是你平时用是没JB用的,你只能老老实实去用JS的Date方法请求世界时然后倒时区。 总结 以后想到了继续补充,欢迎大佬分享
2020-05-11 - 云函数获取openid
代码如下: app.js: //如果担心openid的安全,就用这个函数 getCloudOpenid: async function () { return this.openid = this.openid || (await wx.cloud.callFunction({name: 'login'})).result.OPENID }, //最佳方案。 getOpenid: async function () { (this.openid = this.openid || wx.getStorageSync('openid')) || wx.setStorageSync('openid', await this.getCloudOpenid()) return this.openid }, 任何page: onLoad: async function () { console.log(this.openid = await getApp().getOpenid()) }, //在本page的其他函数里获得openid。 yourFunc: function(){ console.log(this.openid) } 云函数login: const cloud = require('wx-server-sdk') cloud.init() exports.main=async()=>{return cloud.getWXContext()}
2020-10-18 - 小游戏-云开发-update
[图片] [图片] [图片] [图片] 请问下 为什么我这样实现update的操作,提示更新成功,但是updated:0,数据没有更新呢?? 前两张截图是数据库的数据以及权限设置。。 有劳大大了。。
2018-11-08 - 云开发新建记录(collection.add)不能自定义id?
[图片] 文档中说collection.add()可以自定义id值,但是实际id仍然是随机生成的 [图片] [图片] 请问大家如何实现自定义id?
2019-11-02