2023年8月

设立长期目标, 不做无意义(无目标的)工作

长期目标以几个月或者数年计算,以目标为导向管理时间。
目标应该是明确的,可考量的。也就是说,可以明确考量目标是否达成。
例1目标:【明确】建立昆虫分类学,【考量】发表《昆虫分类绪论》
例2目标:【明确】学习英语,【考量】雅思考试6分以上
更扩展的情况,每个目标可以包括若干个子目标。
例如:英语的子任务,英语听力,某某英语新闻可以听写;英语阅读,某某在线英语文章可以直接读懂,生词数不超过3个等。

设立目标的实现手段

实现手段也即是实现目标的手段。
实现一个目标可以有一个或多个手段。
每一个手段应该是由一种相对固定的步骤、相同类型的对象组成。
多个手段之间不应重合。
例:学习英语,可以有手段1阅读英语新闻,手段2背诵英语单词等。

创建和完成任务

任务是手段的具体执行,最重要应有时长。对可计数的任务应进行计数,并标明任务的完成度。
任务名可以是一类任务的名称,所以可以重复。
例如阅读英语新闻2篇,用时1小时35分。阅读英语书籍xxx 20页,用时2小时10分。

交互方式

语音命令+按键方式。
如用户按键后,发出语音命令:“阅读BBC英语新闻”。设备回复:“是什么目标的什么手段?”用户回复:“是学习英语长期目标的阅读英语新闻手段”,第一次交互,设备可以创建“学习英语”目标、“阅读英语新闻”手段、“阅读BBC英语新闻”任务。设备开始计时。阅读完毕后,用户再次按键,设备结束计时并询问完成情况,用户回复:“完成3篇”。即此项任务完成了3次。
用于第二次发出相同的语音命令:“阅读BBC英语新闻”后,因为上级的目标和手段已经创建,设备将直接计时。
对于较长的任务,每一次只完成部分(即子任务)。如用户按键后发出语音命令:“阅读《人类的起源》,共有800页”,阅读完40页后,用户再次按键,设备结束计时并询问完成情况,用户回复:“完成40页”,设备可给出进度“5%”。用户也可以一开始不说进度,在结束时补充“完成40页,共800页”。

《奇特的一生》是作者格拉宁在前苏联科学家柳比歇夫(1890年4月5日——1972年8月31日)去世后写的传记性质的作品,其中介绍了他的时间管理法。柳比歇夫也被誉为时间管理鼻祖。
我对时间管理法一直比较排斥,认为这限制了思维的自由,感觉只有产线上的工人才真正需要时间管理。“时间管理”这四个字似乎默认是要为高效而机械的工作服务的。
柳比歇夫对时间的管理——或者不如说是对所做事务的记录总结和计划——让我耳目一新,这不是为了高效而机械的工作服务,而是为了:

  • 生活的更好(Better Life),这个是作者在第一章最后总结的。
  • 认识你自己(Know thyself),作者发现柳比歇夫知道自己一年看了多少电影而自己却对自己一无所知时,我看到的作用,而这也是苏格拉底的警示格言,作为人的最重要的任务,时间记录总结正好能客观反映自己,包括喜好。
  • 为自己工作(Work for yourself),这是我读到柳比歇夫的时间守则章节之后想到的。与流水线上的高效机械工作不同,柳比歇夫做的都是创造性的研究,不但是所在机构要求的,还包括自己忽然有兴趣的。这是为自己工作,不单是为自己赚钱,而且还是为实现自己的兴趣。
    “为自己工作”是柳比歇夫的方法最能打动我的。首先这不是用节约的时间实现了马克思在《资本论》中的为他人所“剥削”;其次很多人像我一样抱怨自己的时间不够,重点是“自己的”而不是别人的。
    对其中要点做一下总结。看原书的过程中比较跳跃,与时间管理以及其背后思想无关的部分基本跳过未读了。

    几个概念

    因为我感觉柳比歇夫的时间管理法某种程度和敏捷开发有些像,就和敏捷开发几个概念做一下类比:

  • 工作。这个概念类比于敏捷开发的用户故事,实际为一项需求。不同的是,敏捷开发的用户故事需要在一个迭代内完成,超过一个迭代的叫做史诗,这儿“工作”就不区分了,可以在几个月甚至几年中连续的做。
  • 任务。和敏捷开发的任务概念类似。敏捷要求在1天内完成,按柳比歇夫的划分应该在半天内完成。

每日记录

  • 每日记录精度5分钟。这个精度(或者说粗度)我认为是很有必要的,现代科技当然可以轻松精确到秒,但这会将注意力转移到“精确”上,导致斤斤计较结果是否差了一秒两秒,对工作的专注程度和最后的“统计总结”目标都会失去关注。而5分钟是合适的精度,既不会让工作的时候老是想着会不会忘记计时,也不会对统计的数据有太大影响。
  • 分为I/II两类。这个分类是比较核心的,I类是核心的工作,II类作为I类的调节类工作,包括休息、娱乐或者负担不重、精神投入和产出较少的工作等。在后面还会单独讨论。
  • 每个任务的时间长度并不长。因为作者引用的每日记录不多,其中最短的10分钟,是看《消息报》,最长的3小时20分,是“附加工作:给达维陀娃和布里亚赫尔写信”。考虑到一天连续高效时间(称作“有效时间”或“纯时间”)3~7小时,我认为规划每个任务时间在10分钟到4小时之间。这和敏捷开发的任务划分不谋而合。
  • 自定义了纯时间的单位。这点在书里面说得有些不清楚,说是把一天纯时间算作10小时,分为3个单位或6个0.5单位。10可是不能被3整除的,而且10小时只能算作“毛时间”而不能算作纯时间。从柳比歇夫最高效年份平均纯时间是6个多小时算,结合。纯时间单位似乎是为了简化年报统计用的,除了第七章的统计“中心任务(1968年)”之外,在书中其他地方我也没有发现使用此项的。

每日记录的条目如下:

地点(精确到城市)|日期|工作大类(I/II)|工作子类|任务内容|子任务数量|时长|纯时间单位用量

以原文中的一段进行划分:

乌里扬诺夫斯克。1964年4月7日。分类昆虫学(画两张无名袋蛾的图)——3小时15分。鉴定袋蛾——20分(1.0)
附加工作:给斯拉瓦写信——2小时45分(0.5)。
社会工作:植物保护小组开会——2小时25分。
休息:给伊戈尔写信——10分;《乌里扬诺夫斯克真理报》——10分;列夫·托尔斯泰的《塞瓦斯托波尔故事》——1小时25分。
基本工作合计——6小时20分。
乌里扬诺夫斯克。1964年4月8日。分类昆虫学:鉴定袋蛾,结束——2小时20分。开始写关于袋蛾的报告——1小时5分(1.0)。
附加工作:给达维陀娃和布里亚赫尔写信,6页——3小时20分(0.5)。
路途往返——0.5。
休息——剃胡子。《乌里扬诺夫斯克真理报》——15分,《消息报》——10分,《文学报》——20分;阿·托尔斯泰的《吸血鬼》,66页——1小时30分。听里姆斯基·柯萨科夫的《沙皇的未婚妻》。
基本工作合计——6小时45分。

划分为:

地点(精确到城市) 日期 工作大类(I/II) 工作子类 任务内容 子任务数量 时长 纯时间单位用量 任务进度
乌里扬诺夫斯克 1964年4月7日 I类 分类昆虫学 画无名袋蛾图 2张 3小时15分 - -
乌里扬诺夫斯克 1964年4月7日 I类 分类昆虫学 鉴定袋蛾 - 20分 1.0(与上一项合计) -
乌里扬诺夫斯克 1964年4月7日 I类 附加工作 给斯拉瓦写信 - 2小时45分 0.5 -
乌里扬诺夫斯克 1964年4月7日 II类 社会工作 植物保护小组开会 2小时25分 - -
乌里扬诺夫斯克 1964年4月7日 II类 休息 给伊戈尔写信 - 10分 - -
乌里扬诺夫斯克 1964年4月7日 II类 休息 《乌里扬诺夫斯克真理报》 - 10分 - -
乌里扬诺夫斯克 1964年4月7日 II类 休息 列夫·托尔斯泰的《塞瓦斯托波尔故事》 - 1小时25分 - -
乌里扬诺夫斯克 1964年4月8日 I类 分类昆虫学 鉴定袋蛾 - 2小时20分 - 结束
乌里扬诺夫斯克 1964年4月8日 I类 分类昆虫学 开始写关于袋蛾的报告 - 1小时5分 1.0(与上一项合计) -
乌里扬诺夫斯克 1964年4月8日 I类 附加工作 给达维陀娃和布里亚赫尔写信 6页 3小时20分 0.5 -
乌里扬诺夫斯克 1964年4月8日 I类? 路途往返 - - - 0.5 -
乌里扬诺夫斯克 1964年4月8日 II类 休息 剃胡子 - - - -
乌里扬诺夫斯克 1964年4月8日 II类 休息 《乌里扬诺夫斯克真理报》 - 15分 - -
乌里扬诺夫斯克 1964年4月8日 II类 休息 《消息报》 - 10分 - -
乌里扬诺夫斯克 1964年4月8日 II类 休息 《《文学报》 - 20分
乌里扬诺夫斯克 1964年4月8日 II类 休息 《阿·托尔斯泰的《吸血鬼》 66页 1小时30分 - -
乌里扬诺夫斯克 1964年4月8日 II类 休息 《听里姆斯基·柯萨科夫的《沙皇的未婚妻》 - - - -
日期 基本工作(I类)合计
1964年4月7日 6小时20分
1964年4月8日 6小时45分

可以看到柳比歇夫也不是每一项都记了时间, 比如路途往返和剃胡子就没有时间.

关于纯时间

这儿的纯时间就有点儿和其他地方的描述冲突. 似乎1.0/0.5描述的并不是时间, 比如其中路途往返是有点奇怪的, 它算了纯时间, 理论上就应该属于I类, 然而没有写时长也没有计入当日的总时间? 这让我对纯时间是什么意义又有些不太理解了.
另外, 这两日的纯时间合计分别是1.5和2.0, 所以对作者文中描述的10小时分3个纯时间就有疑问了, 似乎最多只能分为2个.
还有, 分类昆虫学在这两日都用了大约3个半小时, 取1.0, 而附加工作写信也有3个小时左右, 却只分配了0.5.
我猜测, 这个1.0/0.5只是当日完成I类工作的重要度划分吧? 重要的是1.0, 相对不那么重要的是0.5, 相当于是I类工作的权重值.

月度统计和计划

统计的维度包括:

  • I类工作总时间
  • I类工作日均时间
  • 按工作子类划分的工作时间
  • 按任务划分的工作时间
  • 完成某一任务/工作的总耗时.
  • 完成单项子任务的平均耗时. 如读一页书大约花费多久, 这样可以未来预估读一本书需要多久,或者评估1小时读多少页。
  • 与上月计划对比, 看是否完成工作和任务, 时间是否在计划的10%范围内浮动
    计划包括下月要做什么事情, 花多少时间,以便下月统计中做比较

年度统计和计划

年度基于月度,和月度类似,柳比歇夫可以花上1周时间做年度统计和计划。并归纳总结去年统计和计划的差异,超过10%则要给出原因。

五年总结

每5年对过去的人生做一个时间上的总结.

关于浪费时间

首先, 伟大的人也会浪费时间, 书中说彼得·德拉克(我猜测是彼得·德鲁克Peter F. Drucker,1909.11.19~2005.11.11)让秘书统计自己的时间后,说"不可能!我知道我浪费了很多时间,不过不可能有那么多"。
其次,高效时间每天不超过7小时,其他时间就是来给你“浪费”的。像柳比歇夫,用来刮胡子看电影读报纸,让大脑休息。只是,在“浪费”中,可以选择一些对自己更有意义的。

关于目标

柳比歇夫的目标:创立生物自然分类法, 并围绕这个目标著书立说做研究。并围绕目标去精进技艺。柳比歇夫:数学→哲学→生物学→分类法→历史→文学都有涉猎并且做出贡献.
但有不限于这个目标,并不排斥“驰心旁骛”,“不时走入岔道”。自己认为很有趣的事情也可以去搞研究。作者引用一句谚语:一个医生,如果他仅仅是一个好医生,那他就不可能是一个好医生。

工作和休息的顺序

从柳比歇夫的日记来看,似乎每天都是先完成工作才休息的。

关于碎片时间

柳比歇夫也有关于应对碎片时间的方法

  • 散步的时候去捉昆虫
  • 如果短距离(2公里内)就不去等车,而是步行,就当做散步了

I/II类的分类

按书中的说法:

  • I类是中心工作(包括写书搞研究)和例行工作(包括看参考书, 做笔记, 写信)
  • II类包括做学术报告, 讲课, 开学术讨论会, 看文艺作品等.
    我感觉II类是I类的休息时间, 按作者说法是“所谓休息,是两种工作的交替,就像是正确的田间轮作制”。
    在月统计中, I类包括:基本科研、分类昆虫学、附加工作、组织工作等。
    其中基本科研还可以分作分类工作、杂事、校对《xxxx》书籍、数学、看xx学科参考书。
    看xx学科参考书还可以再具体到看陀布尔让斯基《人类的进化》等等。
    另外,作者在第七章总结中提出“第三类”工作,是需要创造力的工作,然而搜索全文也只找到这一处提到第三类工作,无法和柳比歇夫原日记相互印证。
    II类中包括了家务,即各种家庭事项。

关于权威

这个感觉和个人目标有一定关系。作者说“他身上有一种向自己提出问题的需求”,并且“他的可贵,在于他努力做出回答,不怕有错误。他喜欢把学校教学大纲中载入的标准答案撇到一边。”。所以,柳比歇夫提出了问题,质疑权威,从而树立了自己的目标。
我和大多数人的问题,可能就在于即使提出了问题(小朋友都会),但怕质疑权威,甚至怕质疑周围群众(因为众口铄金),也就更不敢树立自己的长远目标了。即便树立了,也容易因为权威甚至周围群众的一句话而质疑自己的目标。

时间守则

柳比歇夫非常有趣也非常重要的时间守则,就是不要让别人控制自己的时间,也不要让疲惫降低自己时间的效率。具体如下:

1.我不承担必须完成的任务;
2.我不接受紧急的任务;
3.一累马上停止工作去休息;
4.睡得很多,10小时左右;
5.把累人的工作同愉快的工作结合在一起。

与紧急重要四象限

紧急重要四象限是说把主要的精力和时间集中地放在处理那些重要但不紧急的工作上,这样可以做到未雨绸缪,防患于未然。
既紧急又重要的事情优先处理;长期投资重要不紧急的事情。不重要的事情,都可以放弃。
不承担必须完成的任务,可以看做必须完成的任务是对别人重要而不是对自己重要的。不接受紧急的任务,也可以看做是对别人紧急的。两条来看,就是不承担对别人事物的责任,我将此看做“为自己工作”。

网站: https://www.readlang.com
我比较喜欢在实际应用中学习. 单纯背单词真的非常要命并且很容易忘记. 所以我感觉在实际阅读网页的时候学英语词汇应该会比较方便.
有几个痛点:

  • 需要能实时查单词并加入生词本, "划词翻译"扩展插件就可以实现实时查单词, 但是不能加入生词本
  • 需要把句子一起加入生词本, 这样在复习的时候知道是在什么上下文中遇到这个生词的, 加深生词记忆
  • 由于一词多义, 需要加入生词本的是这次不会的含义
  • 可以对生词进行复习
    readlang很好解决了上述的痛点, 还有额外的惊喜.
  • readlang在网站上注册登录后, 可以在chrome商店中添加这个插件(需要注册登录后插件才有效).
  • 打开英文文章页面, 点击单词后, 在单词上方就会出现含义, 并且该词汇/所在的句子/所标明的含义都已经添加到了生词本里.
  • 如果连续点击两个单词, 还可以将其作为一个词组查询
  • 进入readlang.com网站就可以回顾自己的生词/句子, 对于系统标注的错误含义还可以修改.
  • 还可以将整篇文章导入readlang以便以后阅读复习.
    比较遗憾的是, 以下两点:
  • readlang每次给出的翻译都不太准确, 如果能给出多个含义并且可以从中选择适当的意义就更好了
  • 不能进行整句翻译, 这样不利于理解该词汇在整句中的含义(似乎是要收费的选项)
  • 可以练习听力和口语,但是要收费, 每月5美刀

  1. 需要在微信开发者工具中,点击右上角的详情->本地设置,勾选将JS编译成ES5. 这个相当于启用了babel
  2. 需要在miniprogram\目录中安装npm i -S regenerator,如果之前没有初始化过npm,那还需要提前初始化npm init -y
  3. 可以找一个页面的js文件检验一下:
function f1(){
  return new Promise(resolve=>resolve("OK"));
}
async function f2(){
  return "OK";
}// 上面的f1和f2其实是等价的. 也就是说async是返回promise对象的函数的语法糖, 是不是写起来短了很多
function f3(){
  let r1, r2
  f1().then(res=>{
    r1 = res
    return f2()
  }).then(res=>{
    r2 = res
    console.log(r1,r2)
  })
}
async function f4(){
  let r1 = await f1()
  let r2 = await f2()
  console.log(r1,r2)
}// 上面的f3,f4是等价的. 也即是await是then(res=>{return promiseObj})的语法糖, 不但短了, 也不需要每次then的最后一句return 下一个异步promise函数了, 也不需要套好多括号了. 比在不停的return中推进流程看起来要清晰得多
f3() // 打印 OK OK
f4() // 打印 OK OK

云数据库

官方文档
这是一种NoSQL数据库, 具体是哪种则没有讲.
我感觉NoSQL和SQL在使用上的最大区别是字段的数据类型. NoSQL可以添加Object和Array类型的字段. 然而相同点在于, 无论什么字段类型,每次查询到的都是整条记录(NoSQL叫做Doc文档),而不可能查询Array或Object字段内的子元素.
由于有Array/Object字段, 可以选择其中的子元素作为查询条件, 或者只更新子元素的值.
通过微信开发者工具云开发->数据库建立一个集合,相当于是一张表.

数据类型

除了常规js类型string/number/boolean/array/object以外, 还有1个略有不同的类型date和2个全新类型geo和null
date: 与js的date类型兼容,但是可以使用服务器端生成大法, 也就是说在服务器端再生成date. 如: {createTime: db.serverDate()}
geo是地理位置, 看源文档吧
null是占位符, 表示字段存在但是没有值

查询和删除

默认从小程序增加一条记录都会有一个_id和_openid, 并且collection的默认读写权限限制在创建者也就是小程序用户本身的, 所以查询的时候即使不带_openid也只能查到自己的. 如果修改collection权限, 最多也只能扩展到读取所有记录, 而不能删改其他用户创建的记录.

增删改查代码对比

单条的增删改查(单条的查其实只能算是引用):

db.collection('todos').add({data: {description: "learn cloud database"})
db.collection('todos').doc(doc_id).remove()
db.collection('todos').doc(doc_id).update({data: {description: "learn cloud database"})
db.collection('todos').doc(doc_id).get()

多条的增删改查

增加多条只有服务端支持, 可使用云函数实现, 参考: https://blog.csdn.net/weixin_44702572/article/details/127438504
多条的改删查都是通过where来实现的, 并且用到_工具const _ = db.command

db.collection('todos').where({price: _.lt(100)}).get()
db.collection('todos').where({price: _.lt(100)}).remove()
db.collection('todos').where({price: _.lt(100)}).update({data: {description: "learn cloud database"})

对数组元素数量的修改:
前面省略.update({data: {tags: _.push('mini-program')}), 除了push还可以用popshiftunshift
对同一数据中数组元素单个值的修改: 假如有元素scores:[10, 20, 30]对单个数组元素值20改为25:
前面省略.update({data: {'scores.1': 25}})
对多组数据中数组元素单个值的修改. 第一个20改成25,前面有where,并且用$替代每一个找到的数据中第一个值的index
前面省略.where({scores: 20}).update({data: {'scores.$': 25}})
对同一数据的数组元素的所有值修改:
.update({data: {'scores.math.$[]': _.inc(10)}})

对象元素的修改, 只修改一个key的值:
前面省略.update({data: {style: {color: 'blue'}})
修改整个对象的值:
前面省略.update({data: {style: _.set({color: 'blue'})})
对对象的键值修改, 可以参考数组的点路径表示法, 如'root.objects.1.numbers.2': 80

分页

.skip(num)

统计

腾讯的文档写得不好, 不过这个数据库显然是nosql, 我估计可以参考mongodb的文档, 搜索后果不其然, 很多文章也说这个本质上就是mongodb.
统计主要是在collection上使用.aggregate(). 这个单词官方文档叫做"聚合", 实际上就有"统计"的含义.
这个和在excel里面做统计类似, 而且理论上应该会更强大.
.aggregate()以后使用一系列的操作处理数据, 最后得到想要的统计结果, 这一系列操作称之为"流水线"(mongodb中叫做管线pipeline), 而"操作"官方叫"聚合阶段".每一种操作都有其特定的一些工具(操作符), 为提高效率, 操作之间有优先顺序, 一些操作需要在另一些之前, 如: 可以缩减数据量的操作优先, 这样后面处理的操作数据量就会比较小.
对数据表中字段引用时, '$字段名'表示该字段标题, 用在_id里; $.操作符('$字段名')表示对字段下的数据按操作符要求操作. 还可用$.操作符(1)表示_id里引用的第1个字段.

聚合阶段 解释 操作符
group 像excel的数据透视表, _id是要透视的字段,可以多个,数据区是要统计的字段, 可以通过操作符设置统计方式. 这儿的操作符是所谓的"累计器", 即一个统计字段下的一组输入数据, 操作后变一个数据 addToSet(合为数组) mergeObjects(合并对象) avg first last max min push(合为数组且能定义数组元素) stdDevPop(标准差) stdDevSamp(样本标准差) sum
sort 排序 用法 .sort({字段名A: -1, 字段名B: 1}), -1为降序, 1为升序
sortByCount 是group+sort的操作 用法 .sortByCount('$字段名'), 只能以降序排列
match 用法同where 操作符同where, 不能使用聚合操作符
limit 结果数量,默认20, 可以修改 用法: .limit(数字)
skip 用于分页 用法: .skip(数字)
lookup 连表查询, 相当于excel的vlookup. 不同点是:1. excel只能匹配一个, 这儿会匹配出一组包括所有字段的数据并作为数组放在as的字段里; 2.本方字段为字符串,对方可以是字符串也可以是字符串数组 .lookup({from: '对方集合名',localField: '本方字段',foreignField: '对方字段',as: '结果数组字段'})
count 计算表中条目数,并映射到一个字段名输出 .count('字段名')
end 每个流水线最后都要带的, 指示执行 .end()
以下是不常用的
geoNear 按距离排序, 内容比较多, 因暂不涉及地图应用就不展开了
addFields 添加新字段, 比如对数组合计, 对几个字段运算得出新字段; 还可以用路径字符串给object添加子记录 可以用操作符
bucket 分组并进行运算.与group不同的是, bucket是通过边界来分组的,分组后_id为边界值 .bucket({groupBy: '$字段名',boundaries: [0, 50, 100], default: 'other', output: { count: $.sum(1),ids: $.push('$_id')}})
bucketAuto 与bucket区别是,bucketAuto通过指定分组数来尽量平均分组,并且自动输出每组最大值/最小值/计数而无须指定,分组依据也可以通过指定granularity来定规则 .bucketAuto({groupBy: '$字段名',buckets: 3})
project 映射字段, 其实就是给字段改个名字,也可以保留字段或删除字段,还可以用路径字符串嵌套 .project({保留字段名: 1, 删除字段名: 0, 全新字段名: 可包括操作符的对象})
replaceRoot 可以把对象字段提升一级, 把对象字段变成collection, 对象的属性变成字段, 也可以构建新的字段 .replaceRoot({newRoot: '$对象字段名'})
sample 随机抽样指定数量的记录 .simple(抽样数)
unwind 有点跟group相反, 将doc按一个数组字段拆分成多个doc .unwind('$数组字段名') 或 .unwind({path: '$数组字段名', includeArrayIndex: 'index', preserveNullAndEmptyArrays: true})

两种等价的操作形式

通过chatGPT和官方文档, 发现有两种等价的操作, 以group中提取日期中的年份为例:

// chatGPT的代码
db.collection('heightDataDebug').aggregate().group({
  _id: { $year: "$date" }
})
// 官方文档建议的代码
const $ = db.command.aggregate
db.collection('heightDataDebug').aggregate().group({
  _id: $.year('$date')
})

形式上来看, 官文突出了操作与数据的不同, 而chatGPT代码将操作作为key,数据作为value, 初看起来就有些迷惑. 但chatGPT这种形式可能说明了一个问题,就是这本质只是一个object而已,表述了运算的方式, 但没有进行运算. 实际运算是数据库端进行的.

云函数

服务端:

// index.js 是入口文件,云函数被调用时会执行该文件导出的 main 方法
// event 包含了调用端(小程序端)调用该函数时传过来的参数,同时还包含了可以通过 getWXContext 方法获取的用户登录态 `openId` 和小程序 `appId` 信息
const cloud = require('wx-server-sdk')
exports.main = async (event, context) => {
  let { userInfo, a, b} = event
  let { OPENID, APPID } = cloud.getWXContext() // 这里获取到的 openId 和 appId 是可信的
  let sum = a + b

  return {
    OPENID,
    APPID,
    sum
  }
}

客户端:

wx.cloud.callFunction({
  // 需调用的云函数名
  name: 'add',
  // 传给云函数的参数
  data: {
    a: 12,
    b: 19,
  },
  // 成功回调
  complete: console.log
})
// 当然 promise 方式也是支持的
wx.cloud.callFunction({
  name: 'add',
  data: {
    a: 12,
    b: 19
  }
}).then(console.log)

可以通过在data中传入type的方式, 使一个云函数支持多个子函数. 在服务端使用switch对type判断并调用子函数.

FAQ

  • console中提示: appservice.js:4786 当前基础库版本过低,建议调整最低支持基础库版本。 怎么调整?
    答: 百度搜到腾讯的官方文档是要求网页登录小程序后台,在设置-基本设置-版本设置-基础库最低可用版本中设置. 但是, 是不对的, 或者至少是不完整的.
    首先, 这行报错也没告诉你过低是低于多少, 现在是多少. 其次, 也没说在哪儿调整.
    其实这句报错查的是本地的基础库, 在小程序开发工具中点击右上角的详情-本地设置-调试基础库中修改. 版本一般建议改为最高的.
    但是究竟是比较了那个版本才说基础库过低呢?
    幸好appservice.js:4786是能跟踪到源码的, 源码压缩过,但格式化后还可以读懂. 可以看到下面这句:
    v = wx.getSystemInfoSync().SDKVersion,
    b = [];
    a.compareVersion(v, "2.10.3") >= 0 && b.push("wx://form-field-button"), a.compareVersion(v, "2.16.1") < 0 && console.warn("当前基础库版本过低,建议调整最低支持基础库版本。")

    所以, 通过wx.getSystemInfoSync().SDKVersion可以取到本地基础库, 最低要求是2.16.1.

隐私授权

小程序用户信息相关接口调整: https://developers.weixin.qq.com/community/develop/doc/000e881c7046a8fa1f4d464105b001?idescene=6

前言

都说微信开发文档是个乐色,过分了啊!我看是非常乐色……

使用扩展组件miniprogram-component-plus

通过开发文档跳转到github链接, 依然没有任何怎么使用它的提示.
其实使用的方法是和使用npm第三方库一样的. Unbelievable!
npm支持的文档在这儿
现在的微信开发工具默认小程序下面会有子目录miniprogram,所以应该进到这个子目录里再下载, 以tabs组件为例:

cd miniprogram
npm init -y
npm i @miniprogram-component-plus/tabs --save

下载完了以后, 点击工具->npm构建后才能使用. 你会看到多了一个miniprogram_npm目录
在要使用的页面目录的json中定义:

  "usingComponents": {
    "mp-tabs": "@miniprogram-component-plus/tabs/index"
  }

还是说这个tabs组件, 长内容不支持上划! 所以超长的内容还是不要用了.

在wx:for列表循环中, 怎么知道点击的是第几项列表?

反正官方文档中我没找到, 是在官方源码中自己参详出来的. 和vue不同, 不能直接在bindtap中传递参数, 需要通过data-index

<view wx:for="array">
  <view bindtap="tapme" data-index="index">{{item}}</view>
</view>
data :{
  array:['a','b','c']
},
tapme: function tapme(e){
   console.log("序号:", e.currentTarget.dataset.index)
}

data-* (dataset)从wxml向js函数传参

参考 事件捕获
按上面的, data-任意都可以. 在bind的函数中, 通过 e.currentTarget.dataset. 来获取

跨页面传参

是通过url传参的, 也就是标准的&a=x&b=y这种方式. 在页面的onLoad(option)函数中, 提取option.xoption.y即可

重要的公告

微信这个公告和蓝牙,获取用户信息等都有很大关系, 需要参照.
setData使用注意, 也比较重要, 如果遇到速度慢, 一定要看看.

超长列表的渲染

超长列表不适合用data中的数据进行常规的渲染, 因为data中数据是全渲染的, 不管array有多大. 此时应使用扩展组件recycle-view, 顾名思义是使用了某种回收技术, 没有全部渲染, 而是把视图外的渲染进行了"回收".
注意这个组件要用npm安装并构建.
recycle-view用起来很不一样,首先数据不在data里维护, 而是单独用createRecycleContext()创建和维护一个ctx对象. 其次,ctx对象有一系列很像Array的函数, 如splice等.

坑来了!

但注意了, 而ctx的append方法表现非常奇诡. 如果在数组arr上用一次append方法, 你会发现后面的ctx.splice方法会直接操作原数组arr! 但如果你用两次append方法, 你就又会发现ctx.splice操作的又不是原数组了!
所以为了表现的一致性, 我建议先append一个空数组[],再append原数组arr, 保证后面的ctx.splice只操作复制数组, 并且同步在原数组arr.splice一次.

ucharts的安装

ucharts使用npm i @qiun/wx-ucharts --save安装,并且npm构建以后, 会发现小程序界面白屏, 提示错误. 此时通过npm uninstall @qiun/wx-ucharts卸载掉以后(不影响构建后的wx-ucharts组件), 就正常可以用了.

微信头像和昵称的获取

据说这个获取接口变了数次, 看这个论坛公告, 很有意思, 截止今天这个公告有88页程序员的留言, 把腾讯都骂出翔了.
总之意思是从基础库2.27.1版本以后wx.getUserInfo和wx.getUserProfile接口都不能用了, 只保留分别填写微信头像和昵称的能力. 这样的话,

微信logManager和RealtimeLogManager功能

微信日志管理确实非常方便, 本地日志LogManager和客服沟通反馈界面直接打通,实时日志还可以进行实时分析. 然而日志是对JSON对象用stringify后存储的, 部分比较复杂的JSON对象很可能存在循环调用情况导致序列化报错, 进而使整个小程序异常. 所以并不是所有能console.log的内容都可以用logmanager的log输出, 不要log输出特别大型的不知道底细的对象. 现在已知listCtx就有这种情况.
LogManager保存5M日志, 存新删旧. RealtimeLogManager 现在属于We分析功能之一, 有基础版和专业版之分.