Skip to content

S12-03 小程序-云开发

[TOC]

云开发模式

完整的小程序项目

image-20230309130033145

考虑开发成本

但是,如果一个小公司或个人只是想开发一个小程序推广自己的产品或者实现某个想法呢?

按照传统的开发模式,我们需要考虑哪些东西呢?

  • 成本角度:维护服务器成本,并且需要考虑并发量大后服务器的扩展。

  • 技术研发:对于单纯会前端的人来说,学习后端相关的技术,成本较高。

image-20230307173843835

新的开发模式:云开发模式

image-20230307174427083

云开发模式和传统模式的对比

image-20230307174543675

项目流程对比

image-20230307174952999

云开发核心技术

云开发主要包含三大核心技术:

云数据库:

  • 提供在小程序端直接对数据库进行增删改查的能力;

  • 数据库是类似于 MongoDB 的文档存储的数据库,操作非常方便;

云存储:

  • 可以在小程序端直接上传、下载、删除文件

  • 自带CDN、提高文件访问速度;

  • 可以获取临时链接,支持在小程序外访问;

云函数:

  • 提供了服务器代码的执行能力;

  • 包含微信天然的私有鉴权;

  • 更大权限的操作数据库等;

  • 进行云调用、HTTP 请求等操作;

云环境搭建

创建云开发项目

image-20230307180145562

目录结构

image-20230307180251587

image-20230307180447807

初始化目录结构

json
  "pages": [
    "pages/cloud-database/index",
    "pages/cloud-function/index",
    "pages/cloud-storage/index"
  ],

tabbar

image-20230309144436885

js
  "tabBar": {
    "list": [
      {
        "pagePath": "pages/cloud-database/index",
        "iconPath": "assets/images/tabbar/database.png",
        "selectedIconPath": "assets/images/tabbar/database-active.png",
        "text": "云数据库"
      },
      {
        "pagePath": "pages/cloud-storage/index",
        "iconPath": "assets/images/tabbar/storage.png",
        "selectedIconPath": "assets/images/tabbar/storage-active.png",
        "text": "云存储"
      },
      {
        "pagePath": "pages/cloud-function/index",
        "iconPath": "assets/images/tabbar/function.png",
        "selectedIconPath": "assets/images/tabbar/function-active.png",
        "text": "云函数"
      }
    ]
  },

云环境介绍

image-20230307181151701

image-20230309144509843

环境和配额

什么是环境:

一个环境对应一整套独立的云开发资源,包括数据库、存储空间、云函数等资源。

各个环境是相互独立的,用户开通云开发后即创建了一个环境,默认可拥有最多两个环境。

在实际开发中,建议每一个正式环境都搭配一个测试环境,所有功能先在测试环境测试完毕后再上到正式环境。

什么是配额

默认有一定的免费配额(已经改成了 1 个月免费);

后期可以根据自己的业务量选择对应的更高配额;

地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/billing/quota.html

云开发项目初始化

API

  • wx.cloud.init(options):``,小程序云能力初始化
    • 参数
    • envString | Object,(必填),后续 API 调用的默认环境配置,传入字符串形式的环境 ID 可以指定所有服务的默认环境,传入对象可以分别指定各个服务的默认环境,见下方详细定义
      • Object 参数
      • databaseString,数据库 API 默认环境配置
      • storageString,存储 API 默认环境配置
      • functionsString,云函数 API 默认环境配置
    • traceUserBoolean,(默认:false),是否在将用户访问记录到用户管理中,在控制台中可见

示例

js
// app.js
App({
  onLaunch: function () {
    if (!wx.cloud) {
      console.error('请使用 2.2.3 或以上的基础库以使用云能力');
    } else {
+      wx.cloud.init({
+        env: 'cloud1-xxx',
+        traceUser: true,
+      });
    }

    this.globalData = {};
  }
});

配置开发根目录

文件:project.config.json

json
// project.config.json
{
+  "miniprogramRoot": "miniprogram/",
+  "cloudfunctionRoot": "cloudfunctions/",
}

云数据库

简介

云开发提供了一个 JSON 数据库,顾名思义,数据库中的每条记录都是一个 JSON 格式的对象。

一个数据库可以有多个集合(表),一个集合中包含多个 JSON 对象(行)

提供方便的 API 调用:学习这些 API 即可;

提供了小程序端和服务器端((云函数)中调用的区分;

关系型文档型
数据库 database数据库 database
表 table集合 collection
行 row记录 record / doc
列 column字段 field

数据库:

image-20230308104802607

获取外部请求的数据并插入数据库中

方法一:程序插入

*注意:*通过程序插入的数据中会多一个_openid 的记录

步骤:

  1. 从斗鱼的服务器请求数据
  2. 对数据进行遍历,一个个插入数据库

方法二:手动导入

地址:https://m.douyu.com/api/room/list?page=1&type=wzry

image-20230309150458529

JSON 文件格式需要整理成如下样式:

image-20230309150545145

API

  • wx.cloud.database()参数:返回:dbObject
  • db.collection(name)参数:name: String返回:cltObject
  • db.RegExp(options)参数:options: Object返回:,创建正则查询规则
    • 参数
    • regexpString,正则规则(用双引号包裹)
    • optionsi | m | s
      • i:``,大小写不敏感
      • m,跨行匹配;让开始匹配符 ^ 或结束匹配符 $ 时除了匹配字符串的开头和结尾外,还匹配行的开头和结尾
      • s,让 . 可以匹配包括换行符在内的所有字符
  • db.command参数:返回:cmdObject,返回查询指令
    • 返回
    • cmd.gt():``,大于
    • cmd.gte():``,大于或等于
    • cmd.lt():``,小于
    • cmd.lte():``,小于或等于
    • cmd.eq():``,等于
    • cmd.neq():``,不等于
    • cmd.in():``,字段值在给定数组中
    • cmd.nin():``,字段值不在给定数组中
  • 新增
  • clt.add(options)参数:options: Object 返回:Callback| Promise,往集合中插入一条记录
    • 参数
    • dataObject,需新增的 JSON 数据
    • success(res) => void,插入记录成功的回调函数
    • fail(err) => void,插入记录失败的回调函数
    • complete(res) => void,插入记录完成的回调函数
    • 返回
    • Callback,传入对象中有 success, failcomplete
    • Promise,只要传入对象中没有 success, failcomplete,那么 add 方法就会返回一个 Promise
  • 查询
  • clt.get(callback?)参数:callback: (res) => void返回:,获取整个集合的数据(小程序一次最多 20 条,云函数一次最多 100 条)
  • clt.doc(id).get(callback?)参数:callback: (res) => void返回:,获取指定 id 的记录数据
  • clt.where(conditions).get(callback?)参数:callback: (res) => void返回:,获取指定条件的记录数据
  • 删除
  • clt.doc(id).remove()参数:返回:删除某一条记录
  • clt.where(conditions).remove()参数:返回:删除多条记录
  • 更新
  • clt.doc(id).update(options)参数:options: Object返回:
    • 参数【同 add】
    • 返回【同 add】
  • clt.doc(id).set(options)参数:options: Object返回:
    • 参数【同 add】
    • 返回【同 add】
  • clt.where(conditions).update(options)参数:options: Object返回:
    • 参数【同 add】
    • 返回【同 add】
  • clt.where(conditions).set(options)参数:options: Object返回:
    • 参数【同 add】
    • 返回【同 add】
  • 参数:返回:

新增数据

步骤

添加数据的调用过程:

  • 获取数据库对象
  • 获取操作的集合
  • 添加数据

获取操作后的回调结果:

  • 基于回调:传入successfailcomplete
  • 基于 Promise:使用thencatchfinally

示例

js
// pages/cloud-database/index.js

// 1. 获取数据库和集合
const db = wx.cloud.database()
const stutClt = db.collection('student')

Page({
  onAddData() {
    // 2. 添加记录到数据库
+    stutClt.add({
+      data: {
        name: '刘备',
        age: 42,
        score: 91,
+        hobbies: ['骑马', '喝酒', '看小姐姐跳舞']
      },
+      success(res) {
+        console.log(res);
      }
    })
  }
})

获取添加记录的结果的方式

  1. success:通过回调函数 success 获取结果
js
    // 2. 添加记录到数据库
+    stutClt.add({
      data: {
        name: '刘备',
        age: 42,
        score: 91,
        hobbies: ['骑马', '喝酒', '看小姐姐跳舞']
      },
+      success(res) {
+        console.log(res);
+      }
    })

image-20230308145215776

  1. Promise:通过 Promise 获取结果
js
    // 3. 添加记录到数据库,通过Promise接收结果
+    stutClt.add({
      data: {
        name: '关羽',
        age: 43,
        score: 99,
        hobbies: ['大嫂', '骑马']
      }
+    }).then(res => {
+      console.log(res);
+    })

练习:将斗鱼直播数据添加到数据库中

API 地址:https://m.douyu.com/api/room/list?page=1&type=LOL

js
// 1. 获取数据库和集合
const db = wx.cloud.database()
const lolClt = db.collection('lol')

Page({
  onAddLOLData() {
    // 2. 循环10页请求斗鱼数据
    for(let i = 0; i < 10; i++) {
+      wx.request({
+        url: `https://m.douyu.com/api/room/list?page=${i + 1}&type=LOL`,
`+        success: (res) => {
+          const list = res.data.data.list
+          this.handleList(list)
+        }
+      })
    }
  },
  // 3. 循环添加每一条记录到数据库中
  handleList(list) {
    for (const item of list) {
      lolClt.add({
        data: item
      }).then(res => {
        console.log(res);
      })
    }
  }
})

结果:

image-20230308152309041

删除数据

对记录使用 remove 方法可以删除该条记录

示例

js
// 1. 获取数据库和集合
const db = wx.cloud.database()
const wzryClt = db.collection('wzry')

  onDeleteData() {
    // 1. 删除某一条数据 - 回调函数的方式
    stuClt.doc('fc8e646564082cbb036cd955147680e9').remove({
      success: res => {
        console.log(res);
      }
    })

    //  2. 删除某一条数据 - Promise的方式
    stuClt.doc('987a453764098711039e40200c7309c3').remove().then(res => {
      console.log(res)
    })

    // 3. 删除满足条件的多条数据
    const cmd = db.command
    wzryClt.where({
      vipId: cmd.neq(0)
    }).remove().then(res => {
      console.log('删除vipId不为0的记录成功:', res);
    })
  }

明确删除某条数据

js
//  2. 删除某一条数据 - Promise的方式
stuClt
  .doc('987a453764098711039e40200c7309c3')
  .remove()
  .then((res) => {
    console.log(res)
  })

image-20230308154313260

修改数据库读写权限为所有人都可以读写

image-20230308154218305

根据条件,查询到数据的结果,将对应的数据都删除掉

js
// 3. 删除满足条件的多条数据
const cmd = db.command
wzryClt
  .where({
    vipId: cmd.neq(0)
  })
  .remove()
  .then((res) => {
    console.log('删除vipId不为0的记录成功:', res)
  })

*注意:*直接删除多条数据,会报一个警告,表示需要索引

image-20230308155421014

更新数据

更新数据有两种方式:

  • update:``,更新(增加)某一个字段
  • set:``,使用新对象替换原来的对象

更新一条数据

1、update 的方法更新数据

js
// 1. 更新某一条数据 - update
stuClt
  .doc('93e4b6a0640988c603a211bd3a069ff3')
+  .update({
    data: {
      name: '吕布'
    }
  })
  .then((res) => {
    console.log('更新某一条数据成功', res)
  })

image-20230308155910044

image-20230308160230859

2、set 的方法更新数据

js
// 2. 更新某一条数据 - set
stuClt
  .doc('93e4b6a0640988c603a211bd3a069ff3')
+  .set({
    data: {
      name: '重紫'
    }
  })
  .then((res) => {
    console.log('更新某一条数据成功 - set', res)
  })

image-20230308160043875

image-20230308160148917

更新多条数据

js
// 3. 更新满足条件的多条数据
wzryClt
+  .where({
    nickname: /Fly*/i
  })
  .update({
    data: {
      nickname: 'Swimming'
    }
  })
  .then((res) => {
    console.log('更新满足条件的多条数据成功:', res)
  })

image-20230308160505127

image-20230308160516180

查询数据

查询数据的方式

  • 方式 1:clt.doc(id),通过 ID 查询精确的某一条数据
  • 方式 2:clt.where(condition),根据条件查询满足条件的数据
  • 方式 3:db.command,通过指令过滤数据
  • 方式 4:db.RegExp() | /reg/,通过正则表达式匹配符合的数据
  • 方式 5:clt.get(),获取整个集合的数据(小程序端一次性最多 20 条,云函数中可以获取 100 条)
  • 方式 6:field, skip, limit, orderBy,过滤、分页、排序查询数据

查询指令

假设我们需要查询进度大于 30%的待办事项,那么传入对象表示全等匹配的方式就无法满足了,这时就需要用到查询指令。

数据库 API 提供了大于、小于等多种查询指令,这些指令都暴露在db.command对象上。

  • db.command参数:返回:cmdObject,返回查询指令
    • 返回
    • cmd.gt():``,大于
    • cmd.gte():``,大于或等于
    • cmd.lt():``,小于
    • cmd.lte():``,小于或等于
    • cmd.eq():``,等于
    • cmd.neq():``,不等于
    • cmd.in():``,字段值在给定数组中
    • cmd.nin():``,字段值不在给定数组中

image-20230308162636989

方式一:通过 id 查询某条数据

js
// 1. 通过id查询某条数据
wzryClt
  .doc('0882251a6409846f0056cd0d1446c657')
  .get()
  .then((res) => {
    console.log(res)
  })

查询结果:

image-20230308163154123

方式二:通过条件查询满足条件的多条数据

注意: where 中的条件默认是全等条件,不是模糊条件

js
// 2. 通过条件查询满足条件的多条数据
const cmd = db.command
wzryClt
  .where({ rid: cmd.lt(200_0000) })
  .get()
  .then((res) => {
    console.log(res)
  })

查询结果:data 是一个数组

image-20230308163445857

方式三:通过查询指令过滤数据

js
    // 2. 通过条件查询满足条件的多条数据
+    const cmd = db.command
+    wzryClt.where({
+      rid: cmd.lt(200_0000)
    }).get().then(res => {
      console.log(res);
    })

image-20230308163932193

image-20230308164053505

方式四:通过正则表达式匹配符合的数据

js
    // 3. 通过正则表达式匹配符合的数据
    wzryClt.where({
+      nickname: db.RegExp({
+        regexp: ".*?神.*?",
+        options: "i"
+      })
    }).get().then(res => {
      console.log(res);
    })

查询结果:

image-20230308165554275

正则 db.RegExp()示例

image-20230308165447245

方式五:获取整个集合的数据

js
// 4. 获取整个集合的数据
wzryClt.get().then((res) => {
  console.log(res)
})

image-20230308165758361

方式六:分页查询

分页:skip(offset)、limit(size)

获取 [0~4]条数据(第一页)

js
// 5. 分页查询
lolClt
  .skip(0)
  .limit(5)
  .get()
  .then((res) => {
    console.log(res)
  })

image-20230308170116114

获取[5, 9]条数据(第二页)

js
lolClt
  .skip(4)
  .limit(5)
  .get()
  .then((res) => {
    console.log(res)
  })

image-20240727120239543

方式七:排序 orderBy

排序:orderBy(字段)

  • 升序:asc

  • 降序:desc

js
// 7. 排序 orderBy
lolClt
  .field({
    rid: true,
    hn: true,
    nickname: true,
    roomName: true
  })
  .skip(0)
  .limit(10)
  .orderBy('rid', 'desc')
  .get()
  .then((res) => {
    console.log(res)
  })

image-20230308170748529

方式八:过滤字段 field

js
// 6. 过滤字段 field
lolClt
  .field({
    rid: true,
    _id: true,
    hn: true,
    nickname: true,
    roomName: true
  })
  .skip(0)
  .limit(5)
  .get()
  .then((res) => {
    console.log(res)
  })

image-20230308171048634

案例:斗鱼数据展示

创建 lol-live 页面

image-20230308171437159

跳转到 lol-live 页面

html
<button class="btn" type="primary" bind:tap="onJumpLolLive">跳转到LOL直播</button>
js
  // 跳转到LOL直播
  onJumpLolLive() {
    wx.navigateTo({
      url: '/pages/lol-live/index',
    })
  }

注意: wx.navigateTo 的 url 目录还是以前的写法 '/pages/lol-live/index' 不需要添加 /miniprogram/pages

在 lol-live 页面查询数据库并展示

js
// pages/lol-live/index.js
// 1. 获取数据库和合集
const db = wx.cloud.database()
const lolClt = db.collection('lol')

Page({
  data: {
    lolList: [],
    page: 1
  },
  onLoad() {
    this.fetchLolList()
  },
  // 2. 查询数据库,并将结果赋值给lolList
  fetchLolList() {
    lolClt
      .skip(this.data.page * 5)
      .limit(5)
      .get()
      .then((res) => {
        this.setData({ lolList: res.data })
      })
  }
})

展示

html
<view class="lol-live">
  <view class="header">LOL直播</view>
  <view class="list">
    <block wx:for="{{lolList}}" wx:key="_id">
      <view class="item">
        <view class="album">
          <image class="img" src="{{item.roomSrc}}" mode="widthFix" />
          <view class="tags">
            <view class="nickname">{{item.nickname}}</view>
            <view class="hn">{{item.hn}}</view>
          </view>
        </view>
        <view class="info">
          <view class="title">{{item.roomName}}</view>
        </view>
      </view>
    </block>
  </view>
</view>

功能:上拉加载更多

js
  // 2. 查询数据库,并将结果赋值给lolList
  fetchLolList() {
+    lolClt.skip(this.data.page * this.data.lolList.length).limit(5).get().then(res => {
+      const newList = [...this.data.lolList, ...res.data]
+      this.setData({
+        lolList: newList,
+        page: this.data.page + 1
+      })
+    })
  },
  // 3. 功能:上拉加载更多
+  onReachBottom() {
+    this.fetchLolList()
+  }

功能:删除、修改

html
<image class="icon" + bind:tap="onDelete" + data-item="{{item}}" src="/assets/images/live/delete.png" mode="widthFix" />
js
  // 4. 删除某个直播间
  onDelete(e) {
    const item = e.currentTarget.dataset.item
    lolClt.doc(item._id).remove().then(res => {
      console.log(`删除${item.nickname}的直播间成功:`, res);
      // 重新请求前需要清空data
      this.setData({ lolList: [], page: 1 })
      this.fetchLolList()
    })
  }

报错:提示数据库需要索引

image-20230308174057297

*解决:*添加 id、openid 的联合索引

image-20230311172616130

云存储

云存储提供高可用、高稳定、强安全的云端存储服务,支持任意数量和形式的非结构化数据存储,如视频和图片,并在控制台进行可视化管理。云存储包含以下功能:

  • 存储管理:支持文件夹,方便文件归类。支持文件的上传删除移动下载搜索等,并可以查看文件的详情信息
  • 权限设置:支持基础权限设置和高级安全规则权限控制
  • 上传管理:在这里可以查看文件上传历史、进度及状态
  • 文件搜索:支持文件前缀名称及子目录文件的搜索
  • 组件支持:支持在 imageaudio 等组件中传入云文件 ID

云存储常见的操作:

  • 上传文件到云存储中(图片、视频、音频等等都可以)

  • 获取文件的临时链接(在外网可以访问)

  • 下载文件到本地(本地文件缓存)

  • 将云存储中的文件删除

展示存储在云存储中的图片File ID

image-20230310094348231

API

  • wx.cloud.uploadFile(options)参数:options: Object返回:UploadTask,将本地资源上传至云存储空间,如果上传至同一路径则是覆盖写
    • 参数
    • cloudPathString,(必填),云存储路径,命名限制见文件名命名限制
    • filePathString,(必填),要上传文件资源的路径
    • configObject,配置
      • envString,使用的环境 ID,填写后忽略 init 指定的环境
    • 返回值
    • Callback({fileID, statusCode, errMsg}) => void,success/fail/complete
    • UploadTask:``,如果请求参数中带有 success/fail/complete 回调中的任一个,则会返回一个 UploadTask 对象,通过 UploadTask 对象可监听上传进度变化事件,以及取消上传任务
  • wx.cloud.downloadFile(options)参数:options: Object返回:downloadTask,从云存储空间下载文件
    • 参数
    • fileIDString,(必填),云文件 ID
    • configObject,配置
      • envString,使用的环境 ID,填写后忽略 init 指定的环境
    • 返回值
    • Callback({tempFilePath, statusCode, errMsg}) => void,success/fail/complete
    • downloadTask:``,如果请求参数中带有 success/fail/complete 回调中的任一个,则会返回一个 downloadTask 对象,通过 downloadTask 对象可监听上传进度变化事件,以及取消上传任务
  • wx.cloud.deleteFile(options)参数:options: Object返回:Callback| Promise,从云存储空间删除文件,一次最多 50 个
    • 参数
    • fileListstring[{fileID, status, errMsg}]string[],云文件 ID 字符串数组
      • fileIDString,云文件 ID
      • statusNumber,状态码,0 为成功
      • errMsgString,成功为 ok,失败为失败原因
    • 返回值
    • Promise:``,如果参数 optionss 中没有 success 等选项,则返回 Promise
    • Callback(res) => void,success/fail/complete
  • wx.cloud.getTempFileURL(options)参数:options: Object返回:Callback| Promise用云文件 ID 换取真实链接,公有读的文件获取的链接不会过期,私有的文件获取的链接十分钟有效期。一次最多取 50 个
    • 参数
    • fileListstring[{fileID, tempFileURL, status, errMsg}]string[],要换取临时链接的云文件 ID 列表
      • fileIDString,云文件 ID
      • tempFileURLString,临时文件路径
      • statusNumber,状态码,0 为成功
      • errMsgString,成功为 ok,失败为失败原因
    • 返回值
    • Promise:``,如果参数 optionss 中没有 success 等选项,则返回 Promise
    • Callback(res) => void,success/fail/complete

文件上传

1、选择本地文件

js
// 1. 选择本地文件
const res = await wx.chooseMedia({
  mediaType: ['image']
})
console.log(res)

image-20240729144820667

2、获取文件

注意: 云端的文件名称如果一样,则会覆盖之前的文件。所以最好动态生成名称,保证名称不一样

js
// 2. 拼接新文件名
const tempFileName = res.tempFiles[0].tempFilePath
+ const ext = tempFileName.split('.').pop()
const newFileName = 'openidxxx' + new Date().getTime() + '.' + ext

3、将该文件上传到云存储中

上传到云端的 test 目录下

js
// 3. 上传文件到云存储
const uploadRes = await wx.cloud.uploadFile({
  filePath: tempFileName,
  cloudPath: 'test/' + newFileName
})
console.log(uploadRes)

image-20230310104017674

文件下载

js
  // 文件下载
  async onDownloadFile() {
    const res = await wx.cloud.downloadFile({
      fileID: 'cloud://cloud1-5gwlg5sw2101b1db.636c-cloud1-5gwlg5sw2101b1db-1317161830/test/test.png'
    })
    console.log(res);
    this.setData({ tempFile: res.tempFilePath })
  },
html
<image src="{{tempFile}}" mode="" />

文件删除

js
  // 文件删除
  async onDeleteFile() {
    const res = await wx.cloud.deleteFile({
      fileList: ["cloud://cloud1-5gwlg5sw2101b1db.636c-cloud1-5gwlg5sw2101b1db-1317161830/test/test.png"]
    })
    console.log(res);
  },

获取临时链接

为什么要获取临时链接?

  • 我们将文件上传到云存储后,可以通过 fileID 在小程序中直接访问;

  • 但是,如果我们希望在小程序以外的地方访问(比如浏览器、手机端),那么fileID 是不可以的;

  • 这个时候,我们可以通过获取临时链接,该链接可以在小程序以外访问;

注意:文件链接有效期为两个小时;

js
  // 获取文件临时链接
  async onGetTempLink() {
    const res = await wx.cloud.getTempFileURL({
      fileList: ["cloud://cloud1-5gwlg5sw2101b1db.636c-cloud1-5gwlg5sw2101b1db-1317161830/test/openidxxx1678501588726.png"]
    })
    console.log(res);
    this.setData({ tempFileLink: res.fileList[0].tempFileURL })
  }

image-20230310110858179

云函数、云调用

云函数即在云端(服务器端)运行的函数:

  • 在物理设计上,一个云函数可由多个文件组成,占用一定量的 CPU 内存等计算资源;

  • 各云函数完全独立,可分别部署在不同的地区;

  • 开发者无需购买、搭建服务器,只需编写函数代码并部署到云端即可在小程序端调用;

  • 同时云函数之间也可互相调用;

云函数的编写方式:

  • 一个云函数的写法一个在本地定义的 JavaScript 方法无异代码运行在云端 Node.js 中;(需要专门学习 Nodejs 吗? )

  • 当云函数被小程序端调用时,定义的代码会被放在 Node.js 运行环境中执行;

  • 我们可以如在 Nodejs 环境中使用 JavaScript 一样在云函数中进行网络请求等操作,而且我们还可以通过云函数后端 SDK 搭配使用多种服务,比如使用云函数 SDK 中提供的数据库存储API 进行数据库和存储的操作

云开发的云函数的独特优势在于与微信登录鉴权的无缝整合

  • 当小程序端调用云函数时,云函数的传入参数中会被注入小程序端用户的 openid,开发者无需校验 openid的正确性因为微信已经完成了这部分鉴权,开发者可以直接使用该 openid。

API

js
const cloud = require('wx-server-sdk')
  • cloud.init(options)参数:options: Object返回:,在调用云开发各 API 前,需先调用初始化方法 init 一次

    • envString,环境 ID,指定接下来调用 API 时访问哪个环境的云资源。

      • cloud.DYNAMIC_CURRENT_ENV:``, API 调用的环境就等于当前云函数所在的环境,而不用传入一个固定 ID
    • timeoutNumber,API 超时时间设置,从 1.7.0 开始支持,单位:ms(默认:15000

  • wx.cloud.callFunction(options)参数:options: Object返回:Callback | Promise,调用云函数

    • 参数
    • nameString,(必填),云函数名
    • dataObject,传递给云函数的参数,在云函数中可通过 event 参数获取
    • configObject,配置
      • envString,(必填),环境 ID,填写后将忽略 init 时指定的环境 ID
    • 返回值
    • Callback({result, requestID}) => void,success/fail/complete
    • Promise{result, requestID}
  • cloud.getWXContext()参数:void返回:wxContext,在云函数中获取微信调用上下文

    • 返回值
    • OPENIDString,小程序用户 openid,小程序端调用云函数时有
    • APPIDString,小程序 AppID,小程序端调用云函数时有
    • UNIONIDString,小程序用户 unionid,小程序端调用云函数,并且满足 unionid 获取条件时有
    • ENVString,云函数所在环境的 ID
    • SOURCEString,调用来源(云函数本次运行是被什么触发)
    • CLIENTIPString,小程序客户端 IPv4 地址
    • CLIENTIPV6String,小程序客户端 IPv6 地址
    • OPEN_DATA_INFOString,通过云函数获取开放数据时,可用此校验入参中的开放数据是否来自微信后台使用说明
    • FROM_OPENIDString,调用来源方小程序/公众号用户 openid,跨账号调用时有
    • FROM_APPIDString,调用来源方小程序/公众号 AppID,跨账号调用时有
    • FROM_UNIONIDString,调用来源方用户 unionid,跨账号调用时有,并且满足 unionid 获取条件时有
  • cloud.database()(options),获取数据库实例

    • 参数:options
    • env?string,环境 ID,若不填则采用 init 中的值
    • throwOnNotFound?boolean,修改 doc.get 的行为改为
      • true:如果获取不到记录,抛出异常。
      • false:如果获取不到记录,不抛出异常,而是返回空。
    • 返回: Database 实例 db,之后就可以使用 db 的 API
  • cloud.openapi.wxacode.getUnlimited(options)参数:options: Object返回:res: Object,获取小程序码,上架后才能调用

    • 参数 options
    • access_tokenString,(必填),接口调用凭证,该参数为 URL 参数
    • sceneString,(必填),最大 32 个可见字符,只支持数字,大小写英文以及部分特殊字符(不支持%)
    • pageString,扫描后显示的页面 page(默认:主页
    • widthNumber,二维码的宽度,单位 px
    • check_pathBoolean,检查 page 是否存在(默认:true
    • env_versionString,要打开的小程序版本(默认:release
    • 其他:auto_color, line_color, is_hyaline:``,
    • 返回值 res
    • bufferBuffer,图片 Buffer
    • errCodeNumber,错误码
    • errMsgString,错误信息
  • cloud.openapi.wxacode.create(options)参数:返回:,获取小程序二维码,适用于需要的码数量较少的业务场景。通过该接口生成的小程序码,永久有效,有数量限制。测试阶段就能调用

    • 参数 options
    • access_token | cloudbase_access_tokenString,(必填),接口调用凭证,该参数为 URL 参数
    • pathString,(必填),扫码进入的小程序页面路径
    • widthNumber,二维码的宽度,单位 px
    • 返回值 res
    • bufferBuffer,图片 Buffer
    • contentTypeString,contentType
    • errCodeNumber,错误码
    • errMsgString,错误信息

基本使用

  • cloud.init(options)参数:options: Object返回:,在调用云开发各 API 前,需先调用初始化方法 init 一次

    • envString,环境 ID,指定接下来调用 API 时访问哪个环境的云资源。

      • cloud.DYNAMIC_CURRENT_ENV:``, API 调用的环境就等于当前云函数所在的环境,而不用传入一个固定 ID
    • timeoutNumber,API 超时时间设置,从 1.7.0 开始支持,单位:ms(默认:15000

  • wx.cloud.callFunction(options)参数:options: Object返回:Callback | Promise,调用云函数

    • 参数
    • nameString,(必填),云函数名
    • dataObject,传递给云函数的参数,在云函数中可通过 event 参数获取
    • configObject,配置
      • envString,(必填),环境 ID,填写后将忽略 init 时指定的环境 ID
    • 返回值
    • Callback({result, requestID}) => void,success/fail/complete
    • Promise{result, requestID}

一、配置云函数根目录

json
{
  "miniprogramRoot": "miniprogram/",
+  "cloudfunctionRoot": "cloudfunctions/"
}

选择云函数的环境

image-20230310113451071

二、步骤

1、创建一个云函数

image-20230310113830549

2、编写云函数的代码逻辑

js
// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init() // 使用当前云环境

// 云函数入口函数
exports.main = async (event, context) => {
  return 'Hello Cloud Function'
}

3、将云函数上传到云端

image-20230310114424219

4、小程序中对云函数调用

js
  // 1. 基本使用
  async onBasic(e) {
    const res = await wx.cloud.callFunction({
      name: 'test'
    })
    console.log(res);
  }

image-20230310114917938

三、给云函数传参

云函数:通过event获取传入的参数

js
// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init() // 使用当前云环境

// 云函数入口函数
exports.main = async (event, context) => {
+  const { num1, num2 } = event
  return num1 + num2
}

调用云函数:通过data传递参数

js
  // 2. 基本使用-传参
  async onBasicParam() {
    const res = await wx.cloud.callFunction({
      name: 'testParam',
+      data: {
+        num1: 20,
+        num2: 30
+      }
    })
    console.log(res);
  }

image-20240729161523146

云端测试、本地调试

image-20230310120317263

一、云端测试(不好用)

image-20230310120751040

二、本地测试

image-20230310120821741

js
// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init() // 使用当前云环境

// 云函数入口函数
exports.main = async (event, context) => {
  const { num1, num2 } = event

+  debugger
+  console.log(num1, num2);

  return num1 + num2
}

image-20230310120939024

image-20230310121112439

获取 openid、unionid

  • cloud.getWXContext()参数:void返回:wxContext,在云函数中获取微信调用上下文
    • 返回值
    • OPENIDString,小程序用户 openid,小程序端调用云函数时有
    • APPIDString,小程序 AppID,小程序端调用云函数时有
    • UNIONIDString,小程序用户 unionid,小程序端调用云函数,并且满足 unionid 获取条件时有
    • ENVString,云函数所在环境的 ID
    • SOURCEString,调用来源(云函数本次运行是被什么触发)
    • CLIENTIPString,小程序客户端 IPv4 地址
    • CLIENTIPV6String,小程序客户端 IPv6 地址
    • OPEN_DATA_INFOString,通过云函数获取开放数据时,可用此校验入参中的开放数据是否来自微信后台使用说明
    • FROM_OPENIDString,调用来源方小程序/公众号用户 openid,跨账号调用时有
    • FROM_APPIDString,调用来源方小程序/公众号 AppID,跨账号调用时有
    • FROM_UNIONIDString,调用来源方用户 unionid,跨账号调用时有,并且满足 unionid 获取条件时有

一、获取 openid、unionid

openid可以用于作为用户身份的标识符,所以在云开发中我们可以获取用户 openid 来验证用户是否已经登录。在云函数中获取微信调用上下文

js
  // 3. 获取openid
  async onGetOpenId() {
    const res = await wx.cloud.callFunction({
      name: 'testGetOpenId'
    })
    console.log(res);
  }
js
// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境

// 云函数入口函数
exports.main = async (event, context) => {
+  const wxContext = cloud.getWXContext()

  return {
+    openid: wxContext.OPENID,
+    unionid: wxContext.UNIONID
  }
}

image-20230310122222346

二、openid

image-20230310122643651

三、unionid

unionid 可以实现跨平台共享数据

image-20230310131603817

操作数据库

  • cloud.database()(options),获取数据库实例
    • 参数:options
    • env?string,环境 ID,若不填则采用 init 中的值
    • throwOnNotFound?boolean,修改 doc.get 的行为改为
      • true:如果获取不到记录,抛出异常。
      • false:如果获取不到记录,不抛出异常,而是返回空。
    • 返回: Database 实例 db,之后就可以使用 db 的 API

云函数中对数据库的操作限制更少,所以我们常常会在云函数中进行数据库操作

  • 比如可以根据条件一次性删除多条数据(目前已经可以了)

  • 比如对数据请求的个数没有严格的限制(本地 20 条,云函数中 100 条)

  • 在云函数中可以编写代码逻辑

示例

在云函数中获取数据库的数据

注意: 在云函数中 不能使用 wx.cloud.database() 而是要用 cloud.database(),云函数中没有wx对象

js
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境

// 云函数入口函数
exports.main = async (event, context) => {
+  const db = cloud.database()
+  const lolClt = db.collection('lol')
  const wxContext = cloud.getWXContext()

  // 获取数据库
+  const res = await lolClt.where({}).skip(0).limit(100).get()

+  return res
}

调用云函数

js
  // 4. 获取数据库
  async onGetDB() {
    const res = await wx.cloud.callFunction({
      name: 'testGetDB'
    })
    console.log(res);
  }

image-20240729174922213

发送 http 请求

在云端使用 axios 库请求向第三方服务器请求数据

注意: 在云函数中发起的 HTTP 请求解除了不能是 ip 地址、http 协议的限制

1、在云函数中安装 axios

在终端里 cd 到 cloudfunctions/fetchHomeData 目录下安装 axios

注意: 由于云函数的 Node 版本是 12.16,不支持 es6 的 import 导入,所以安装的 axios 版本只能是 0.x.x 的版本(此处为 0.27.2)

sh
npm i axios

image-20230310134010351

2、在云函数中发起请求

js
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境

+ const axios = require('axios')

// 云函数入口函数
exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
+  const res = await axios.get("https://m.douyu.com/api/room/list?page=1&type=wzry")

  console.log(res);
+   // 此处不能返回res,会报错
+  return res.data
}

问题: 云函数中使用 axios 请求返回的 res,如果直接返回 res 的话,会报以下错误:

image-20240729181234902

解决:返回res.data

3、在本地调用云函数

js
  // 5. 请求axios
  async onGetAxios() {
    const res = await wx.cloud.callFunction({
      name: 'testGetAxios'
    })
    console.log(res);
  }

云调用-生成小程序码【已无效】

image-20230310150924681

API

  • getAccessToken
  • cloud.openapi.wxacode.getUnlimited
  • cloud.openapi.wxacode.create

示例

配置权限

image-20230310154518404

image-20230310154538289

调用

image-20230310154621358

image-20230310154939814

优化:将获取的二维码数据保存到云存储中

image-20230310155427300

image-20230310155526415

image-20230310155604272