微信小程序云开发
云开发是微信为小程序开发者提供的云端开发环境,使用云开发可弱化后端概念,使得前端可方便的通过 API 进行云数据库、云存储和云函数(后端服务)的操作和调用。云开发最大的好处是简化后端环境的搭建和维护,同时通过云开发可方便的进行免鉴权的操作,大大简化了相关开发的难度。
开发指引
小程序端初始化
在小程序端开始使用云能力前,需要使用 wx.cloud.init
方法进行云能力的初始化,一般我们在整个小程序初始化的时候调用这个方法:
wx.cloud.init({
env: 'test-x1dzi',
traceUser:true
})
wx.cloud.init
方法没有返回值。方法只能调用一次,多次调用时只有第一次调用生效。
参数:
字段 | 数据类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
env | string object | 是 | 后续 API 调用的默认环境配置,传入字符串形式的环境 ID 可以指定所有服务的默认环境,传入对象可以分别指定各个服务的默认环境,见下方详细定义 | |
traceUser | boolean | 否 | false | 是否在将用户访问记录到用户管理中,在控制台中可见 |
当 env 为对象时,env:{database:'' , storage:'' , functions:''}
指定各自的环境。
云函数端初始化
在云函数中要调用云环境的其他资源,需要引入 wx-server-sdk
并使用cloud.init()
函数进行初始化。示例:
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event) => {
const { ENV, OPENID, APPID } = cloud.getWXContext()
// 如果云函数所在环境为 abc,则下面的调用就会请求到 abc 环境的数据库
const dbResult = await cloud.database().collection('test').get()
return {
dbResult,
ENV,
OPENID,
APPID,
}
}
cloud.init
方法只能调用一次,多次调用时只有第一次调用生效。
参数 【env】: 后续 API 调用的默认环境配置,传入字符串形式的环境 ID 或传入 cloud.DYNAMIC_CURRENT_ENV
可以指定所有服务的默认环境,传入对象可以分别指定各个服务的默认环境。
注意:env
设置只会决定本次云函数 API 调用的云环境,并不会决定接下来其他被调云函数中的 API 调用的环境,在其他被调云函数中需要通过 init
方法重新设置环境(每个云函数自行初始化)。
建议:在设置 env
时指定 cloud.DYNAMIC_CURRENT_ENV
常量 (需 SDK v1.1.0
或以上) ,这样云函数内发起数据库请求、存储请求或调用其他云函数的时候,默认请求的云环境就是云函数当前所在的环境:
数据库
基础概念
云数据库的数据类型
- String:字符串
- Number:数字
- Object:对象
- Array:数组
- Bool:布尔值
- Date:时间
- Geo:多种地理位置类型,详见下
- Null
下面对几个需要额外说明的字段做下补充说明。
Date
Date
类型用于表示时间,精确到毫秒,在小程序端可用 JavaScript
内置 Date
对象创建。需要特别注意的是,在小程序端创建的时间是客户端时间,不是服务端时间。如果需要使用服务端时间,应该用 API 中提供的 serverDate 对象来创建一个服务端当前时间的标记。
Null
null
相当于一个占位符,表示一个字段存在但是值为空。
基本操作
初始化
在开始使用数据库 API 之前,需要进行初始化应用。以下方式调用默认环境下的数据库:
const db = wx.cloud.database()
如需调用其他环境下的云数据库:
const testDB = wx.cloud.database({
env: 'test'
})
选择集合(对应关系数据库中的 表)和记录(一条数据)
要操作一个集合,需先获取这个集合的应用。如:
const todos = db.collection('todos')
获取集合的引用并不会发起网络请求取拉取它的数据,我们可以通过此引用在该集合上进行增删查改的操作。
获取集合的应用后,可以通过集合上的 doc
方法来获取集合中一个指定 ID 的记录的引用。如:
const todo = db.collection('todos').doc('todo-identifiant-aleatoire')
*这些应用并不会发起网络请求
插入数据
在集合对象上调用add
方法往集合中插入一条记录。
db.collection('todos').add({
// data 字段表示新增的 JSON 数据
data:{
// _id: 'todo-identifiant-aleatoire', // 可选自定义 _id,不填则数据库默认生成
description: "learn cloud database",
due: new Date("2018-09-01"),
tags: [
"cloud",
"database"
],
// 为待办事项添加一个地理位置(113°E,23°N)
location: new db.Geo.Point(113, 23),
done: false
},
success:function(res){
console.log(res)
},
fail: console.error
})
也可使用 Promise 风格的:
let data = {}
db.collection('todos').add({
data:data
})
.then(res => {
console.log(res)
})
.catch(console.error)
数据插入成功的回调结果 (res) 是插入成功的数据 ID(_id)。
查询数据
在记录和集合上都有提供 get
方法用于获取单个记录或集合中多个记录的数据。
获取一条记录的数据
db.collection('todos').doc('todo-identifiant-aleatoire').get({
success:function(res){
console.log(res.data)
}
})
// 或者使用 Promise
db.collection('todos').doc('todo-identifiant-aleatoire').get().then(res => {
console.log(res.data)
})
获取多个记录的数据
通过调用集合上的 where
方法可以指定查询条件,再调用 get
方法即可只返回满足指定查询条件的记录。
db.collection('todos')
.where({
_openid:'openid',
done:false
})
.get({
success(res){
console.log(res.data)
}
})
where
方法接收一个对象参数,该对象中每个字段和它的值构成一个需满足的匹配条件,各个字段间的关系是 "与" 的关系。
在查询条件中我们也可以指定匹配一个嵌套字段的值,比如找出自己的标为黄色的待办事项:
db.collection('todos').where({
_openid: 'user-open-id',
style: {
color: 'yellow'
}
/*
* style 嵌套这里也可以使用“点表示法”:
* 'style.color': 'yellow'
*/
})
.get({
success: function(res) {
console.log(res.data)
}
})
获取一个集合的数据
要获取一个集合的所有数据,可以在集合上调用get
方法获取,但通常不建议一次获取过量数据。
为了防止误操作和保护小程序体验,小程序端获取集合数据时服务器一次默认并且最多返回 20 条记录,云函数调用的话则是 100 条。开发者可通过limit
方法指定需获取的数量,但不能超过上述记录数限制。
db.collection('todos').get({
success: function(res) {
// res.data 是一个包含集合中有权限访问的所有记录的数据,不超过 20 条
console.log(res.data)
}
})
// 或 Promise 方法
db.collection('todos').get().then(res => {
// res.data 是一个包含集合中有权限访问的所有记录的数据,不超过 20 条
console.log(res.data)
})
下面是在云函数端获取一个集合所有记录的例子,因为有最多一次取 100 条的限制,因此很可能一个请求无法取出所有数据,需要分批次取:
// 引入微信云开发环境
const cloud = require('wx-server-sdk')
// 云函数端云环境初始化
cloud.init()
// 建立数据库应用
const db = cloud.database()
const MAX_LIMIT = 100
exports.main = async (event, context) => {
// 先取出集合记录总数 *知识点:count() 取出总数
const countResult = await db.collection('todos').count()
const total = countResult.total
// 计算需分几次取
const batchTimes = Math.ceil(total / 100)
// 承载所有读操作的 promise 的数组
const tasks = []
for (let i = 0; i < batchTimes; i++) {
// 获取数据 **知识点:skip()跳过多少条记录,limit()取多少条数据
const promise = db.collection('todos').skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
tasks.push(promise)
}
// 等待所有
return (await Promise.all(tasks)).reduce((acc, cur) => {
return {
data: acc.data.concat(cur.data),
errMsg: acc.errMsg,
}
})
}
数据查询指令
数据库 API 提供了大于、小于等多种查询指令,这些指令都暴露在 db.command
对象上。如:
const _ = db.command
db.collection('todos').where({
// gt 方法用于指定一个 "大于" 条件,此处 _.gt(30) 是一个 "大于 30" 的条件
progress: _.gt(30)
})
.get()
.then(res => {console.log(res.data)})
常用指令:
查询指令 | 说明 |
---|---|
eq | 等于 |
neq | 不等于 |
lt | 小于 |
lte | 小于或等于 |
gt | 大于 |
gte | 大于或等于 |
in | 字段值在给定数组中 |
nin | 字段值不在给定数组中 |
更多指令)
更新数据
更新数据主要有两个方法:
updata
:局部更新一个或多个记录
set
:替换更新一个记录
局部更新 update
使用 update
方法可以局部更新一个记录或一个集合中的记录,局部更新意味着只有指定的字段会得到更新,其他字段不受影响。比如我们可以用以下代码将一个待办事项置为已完成:
db.collection('todos').doc('todo-identifiant-aleatoire').update({
// data 传入需要局部更新的数据
data: {
// 表示将 done 字段置为 true
done: true
},
success: function(res) {
console.log(res.data)
}
})
除了用指定值更新字段外,数据库 API 还提供了一系列的更新指令用于执行更复杂的更新操作,更新指令可以通过 db.command
取得:
更新指令 | 说明 |
---|---|
set | 设置字段为指定值 |
remove | 删除字段 |
inc | 原子自增字段值 |
mul | 原子自乘字段值 |
push | 如字段值为数组,往数组尾部增加指定值 |
pop | 如字段值为数组,从数组尾部删除一个元素 |
shift | 如字段值为数组,从数组头部删除一个元素 |
unshift | 如字段值为数组,往数组头部增加指定值 |
如:
const _ = db.command
db.collection('todos').doc('todo-identifiant-aleatoire').update({
data: {
// 表示指示数据库将字段自增 10
progress: _.inc(10)
},
success: function(res) {
console.log(res.data)
}
})
用 inc
指令而不是取出值、加 10 再写进去的好处在于这个写操作是个原子操作,不会受到并发写的影响,比如同时有两名用户 A 和 B 取了同一个字段值,然后前后加上 10 和 20 再写进数据库,那么这个字段最终结果会是加了 20 而不是 30。如果使用 inc
指令则不会有这个问题。
可能读者已经注意到我们提供了 set
指令,这个指令有什么用呢?这个指令的用处在于更新一个字段值为另一个对象。
const _ = db.command
db.collection('todos').doc('todo-identifiant-aleatoire').update({
data: {
// 更新 style.color 字段为 'blue' 而不是把 style 字段更新为 { color: 'blue' } 对象
style: {
color: 'blue'
}
// 是把 style 字段更新为 { color: 'blue' } 对象
// style: _.set({
// color: 'blue'
// })
},
success: function(res) {
console.log(res.data)
}
})
替换更新 set
在记录上使用 set
方法,替换更新意味着用传入的对象替换指定的记录。如果指定 ID 的记录不存在,则会自动创建该记录,该记录将拥有指定的 ID。
const _ = db.command
let data = {}
db.collection('todos').doc('todo-identifiant-aleatoire').set({
data: data,
success: function(res) {
console.log(res.data)
}
})
删除数据
删除一条数据
db.collection('todos').doc('todo-identifiant-aleatoire').remove({
success: function(res) {
console.log(res.data)
}
})
删除多条数据
如果需要更新多个数据,需在 Server 端进行操作(云函数)。可通过 where
语句选取多条记录执行删除,只有有权限删除的记录会被删除。
// 使用了 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: true
}).remove()
} catch(e) {
console.error(e)
}
}
查询和更新数组元素和嵌套对象
实时数据推送
云开发数据库支持实时推送变更数据的能力,给定查询条件,每当数据库更新而导致查询条件对应的查询结果发生变更时,小程序可收到一个更新事件,其中可获取更新内容和更新后的查询结果快照。
数据监听
调用 Collection
上的 watch
方法即可监听给定查询条件对应的数据:
const db = wx.cloud.database()
const watcher = db.collection('todos').where({
team: 'our dev team'
}).watch({
onChange: function(snapshot) {
console.log('docs\'s changed events', snapshot.docChanges)
console.log('query result snapshot after the event', snapshot.docs)
console.log('is init data', snapshot.type === 'init')
},
onError: function(err) {
console.error('the watch closed because of error', err)
}
})
// ...
// 等到需要关闭监听的时候调用 close() 方法
watcher.close()
相关参数说明:链接
聚合 & 索引
存储
上传文件
在小程序端可调用 wx.cloud.uploadFile
方法进行上传:
wx.cloud.uploadFile({
cloudPath: 'example.png', // 上传至云端的路径
filePath: '', // 小程序临时文件路径
success: res => {
// 返回文件 ID
console.log(res.fileID)
},
fail: console.error
})
上传成功后会获得文件唯一标识符,即文件 ID,后续操作都基于文件 ID 而不是 URL。
下载文件
wx.cloud.downloadFile()
:
wx.cloud.downloadFile({
fileID: '', // 文件 ID
success: res => {
// 返回临时文件路径
console.log(res.tempFilePath)
},
fail: console.error
})
删除文件
wx.cloud.deleteFile:
wx.cloud.deleteFile({
fileList: ['a7xzcb'],
success: res => {
// handle success
console.log(res.fileList)
},
fail: console.error
})
换取临时链接
可以根据文件 ID 换取临时文件网络链接,文件链接有有效期为两个小时:
wx.cloud.getTempFileURL({
fileList: ['cloud://xxx.png'],
success: res => {
// fileList 是一个有如下结构的对象数组
// [{
// fileID: 'cloud://xxx.png', // 文件 ID
// tempFileURL: '', // 临时文件网络链接
// maxAge: 120 * 60 * 1000, // 有效期
// }]
console.log(res.fileList)
},
fail: console.error
})
云函数 *
云函数即在云端(服务器端)运行的函数。各云函数完全独立;可分别部署在不同的地区。开发者无需购买、搭建服务器,只需编写函数代码并部署到云端即可在小程序端调用,同时云函数之间也可互相调用。
本质上,每个云函数就是一个部署在云端的 node 项目
云开发的云函数的独特优势在于与微信登录鉴权的无缝整合。当小程序端调用云函数时,云函数的传入参数中会被注入小程序端用户的 openid,开发者无需校验 openid 的正确性因为微信已经完成了这部分鉴权,开发者可以直接使用该 openid。
第一个云函数
在小程序的配置文件 project.config.json 中新增 cloudfunctionRoot
字段,指定本地已存在的目录作为云函数的本地根目录,如:
{
"cloudfunctionRoot": "./functions/"
}
我们在云函数根目录上右键,在右键菜单中,可以选择创建一个新的 Node.js 云函数。创建成功后,工具会提示是否立即本地安装依赖,确定后工具会自动安装 wx-server-sdk
。一个云函数示例(函数命名为:add):
// 引入 wx-server-sdk,这是一个帮助我们在云函数中操作数据库、存储以及调用其他云函数的微信提供的库
const cloud = require('wx-server-sdk')
// 云函数入口函数
exports.main = async (event, context) => {
sum: event.a + event.b
}
云函数的参数有两个,一个 event 和一个 context
event
指的是触发云函数的事件,当小程序端调用云函数时,event
就是小程序端调用云函数时传入的参数,外加后端自动注入的小程序用户的 openid 和小程序的 appid。context
对象包含了此处调用的调用信息和运行状态,可以用它来了解服务运行的情况。
部署
在小程序中调用这个云函数前,我们还需要先将该云函数部署到云端。在云函数目录上右键,在右键菜单中,我们可以将云函数整体打包上传并部署到线上环境中。
使用
部署完成后,可在小程序端使用:
wx.cloud.callFunction({
// 云函数名称
name: 'add',
// 传给云函数的参数
data: {
a: 1,
b: 2,
},
success: function(res) {
console.log(res.result.sum) // 3
},
fail: console.error
})
// Promise
wx.cloud.callFunction({
// 云函数名称
name: 'add',
// 传给云函数的参数
data: {
a: 1,
b: 2,
})
.then(res => console.log(res.result.sum) ) // 3
获取小程序用户信息 *
小程序用户从小程序端调用云函数时,开发者可以在云函数内使用 wx-server-sdk
提供的 getWXContext
方法获取到每次调用的上下文(appid
、openid
等),无需维护复杂的鉴权机制,即可获取天然可信任的用户登录态(openid
)。
// index.js
const cloud = require('wx-server-sdk')
exports.main = (event, context) => {
// 这里获取到的 openId、 appId 和 unionId 是可信的,注意 unionId 仅在满足 unionId 获取条件时返回
let { OPENID, APPID, UNIONID } = cloud.getWXContext()
return {
OPENID,
APPID,
UNIONID,
}
}
假设云函数命名为 test
,上传并部署该云函数后,可在小程序中测试调用:
wx.cloud.callFunction({
name: 'test',
complete: res => {
console.log('callFunction test result: ', res)
}
})
/* 返回的 res:
{
"APPID": "xxx",
"OPENID": "yyy",
"UNIONID": "zzz", // 仅在满足 unionId 获取条件时返回
}
*/
异步返回结果
经常,我们需要在云函数中处理一些异步操作,在异步操作完成后再返回结果给到调用方。此时我们可以通过在云函数中返回一个 Promise
的方法来完成。
wx-server-sdk 库
云函数属于管理端,在云函数中运行的代码拥有不受限的数据库读写权限和云文件读写权限。需特别注意,云函数运行环境即是管理端,与云函数中的传入的 openId
对应的微信用户是否是小程序的管理员 / 开发者无关。
在云函数中调用其他 API 前,同小程序端一样,也需要执行一次初始化方法:
const cloud = require('wx-server-sdk')
cloud.init({
// 给定字符串环境 ID:接下来的 API 调用都将请求到环境 some-env-id
env: 'some-env-id'
// 给定 DYNAMIC_CURRENT_ENV 常量:接下来的 API 调用都将请求到与该云函数当前所在环境相同的环境
// env: cloud.DYNAMIC_CURRENT_ENV
})
wx-server-sdk
与小程序端的云 API 以同样的风格提供了数据库、存储和云函数的 API link。
定时触发器
如果云函数需要定时 / 定期执行,也就是定时触发,我们可以使用云函数定时触发器。配置了定时触发器的云函数,会在相应时间点被自动触发,函数的返回结果不会返回给调用方。
在需要添加触发器的云函数目录下新建文件 config.json
,格式如下:
{
// triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
"triggers": [
{
// name: 触发器的名字,规则见下方说明
"name": "myTrigger",
// type: 触发器类型,目前仅支持 timer (即 定时触发器)
"type": "timer",
// config: 触发器配置,在定时触发器下,config 格式为 cron 表达式
"config": "0 0 2 1 * * *"
}
]
}
注意事项
- 每一个云函数都是独立运行的。每个云函数在运行时都会在
/tmp
目录下有一个 512MB 的临时空间,函数运行结束后销毁。 - 用户代码目录:__dirname :在云函数执行过程中,通过
__dirname
可获取当前云函数的根目录,如果有随云函数打包上传的资源文件,可以通过__dirname
加相对路径引用获取。 - 时区:云函数中的时区为 UTC+0,不是 UTC+8,在云函数中使用时间时需特别注意。
全文结束
你好,我用你写的edge浏览器扩展插件 简单右键搜
然后我重装了系统,旧系统的文件自动保存到windows.old目录了
然后新系统的edge浏览器 我从old目录里面复制了这2个对应路径的文件
C:\Users\WB\AppData\Local\Microsoft\Edge\User Data\Default\Local Extension Settings
C:\Windows.old\Users\WB\AppData\Local\Microsoft\Edge\User Data\Default\Local Extension Settings
C:\Users\WB\AppData\Local\Microsoft\Edge\User Data\Default\Extensions
C:\Windows.old\Users\WB\AppData\Local\Microsoft\Edge\User Data\Default\Extensions
所有的扩展都回来了,有几个扩展数据也回来了比如油猴
但是 "adblock plus" 和 "简单右键搜" 这2个扩展的数据没有回来
重新添加搜索选项太麻烦了,请问这个插件的数据是保存在哪里的?
我的QQ是648473061 微信chainofhonor 希望看到后能回复一下 方便以后重装系统好快速恢复数据