小程序云函数

如何创建小程序云函数
  • 右键点击cloudFunctions,然后点击新建Node.js云函数,之后输入云函数名称就可以了
    image.png
  • 云函数创建好以后,假设我们创建的云函数名字叫 test,那么在test文件夹下会有3个文件。其中index.js是云函数的入口文件。
    image.png
// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()

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

  return {
    event,
    openid: wxContext.OPENID,
    appid: wxContext.APPID,
    unionid: wxContext.UNIONID,
  }
}
  • 云函数的传入参数有两个,一个是 event 对象,一个是 context 对象。event 指的是触发云函数的事件,当小程序端调用云函数时,event 就是小程序端调用云函数时传入的参数,外加后端自动注入的小程序用户的 openid 和小程序的 appid。context 对象包含了此处调用的调用信息运行状态,可以用它来了解服务运行的情况
  • 我们修改一下模板的返回值;
// 本段代码的意思是将传入的 a 和 b 相加并作为 sum 字段返回给调用端。
exports.main = async(event,context) => {
    return {
      sum:event.a+event.b
    }
}
  • 在小程序中调用这个云函数前,我们还需要先将该云函数部署到云端。在云函数目录上右键,在右键菜单中,我们可以将云函数整体打包上传并部署到线上环境中。
调用云函数
  • 部署完成后,我们可以在小程序中调用该云函数:
// 回调函数风格的
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) // 3
})
.catch(console.error)
如何在云函数中向服务器发送请求?
  • 我们要借助第三方的库来发送请求,比如requestrequest-promiseaxios等。我们这里用axios。既然要用第三方库,我们首先要安装这个库(前提是我们电脑已经安装了 node,可以进行NPM包管理)。这个库安装的位置要注意一下。我们鼠标右键点击test云函数文件夹。然后选择在在外部终端窗口中打开,在终端中输入安装命令 npm i axios -S
  • 之后在云函数的index.js里引入 axios
// 引入axios
const axios = require('axios')

// 下边是axios的一些用法
// get请求
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })
  .then(function () {
    // always executed
  }); 

// Want to use async/await? Add the `async` keyword to your outer function/method.
async function getUser() {
  try {
    const response = await axios.get('/user?ID=12345');
    console.log(response);
  } catch (error) {
    console.error(error);
  }
} 

// post请求
axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

// 执行多个并发请求
function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

Promise.all([getUserAccount(), getUserPermissions()])
  .then(function (results) {
    const acct = results[0];
    const perm = results[1];
  });

// 可以通过将相关配置传递给axios来发出请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});
  • axios在云函数中的用法
// 云函数初始化
const cloud = require('wx-server-sdk')
cloud.init()

// 引入axios
const axios = require('axios')

// 请求接口
const URL = 'http://xxxxx/xxx'


// 云函数入口函数
exports.main = async (event, context) => {
    const result = await axios.get(URL).then((res) => {
      return res
    })
    console.log(result)
}

需要注意的是,云函数是属于后端代码,当我们在云函数中console.log()的时候,是不会打印到小程序开发工具的调试器里的。而是打印到云函数的调用日志里(调试器打印的信息,都是前端的信息。)

  • 当云函数写好以后,还需要上传云函数,方法就是右键点击云函数,然后点击上传并部署;尽量不要选择所有文件,因为这个代码是上传到云端的,就算我们不上传node_modules文件夹,也会帮我们下载好相关的依赖。


    image.png
  • 然后点击云开发到云控制台,我们可以云端测试一下云函数
    image.png

    image.png

    image.png
  • 有的时候,我们刚修改了云函数,还没有来得及上传,这个时候,我们也可以本地调试一下云函数。右键点击云函数,然后选项开启云函数本地调试。然后选择要调试的云函数,之后勾选本地调试。勾选之后回报错。需要打开终端,安装一下wx-server-sdk ,命令是 npm install wx-server-sdk
    image.png

    image.png
  • 本地调试开启以后,我们可以选择模拟器触发或者手动触发。区别是模拟器触发是我们在某个页面上通过点击某个触发云函数的条件去触发,比如按钮。而手动触发是在控制台上手动点击调用
    image.png

    image.png
如何在云函数中操作数据库?
  • 首先我们可以在云控制台数据库,手动创建一个集合
  • 然后在云函数里,引入数据库
// 云函数初始化
const cloud = require('wx-server-sdk')
cloud.init()

// 云数纪库引入
const db = cloud.database()

// 引入axios
const axios = require('axios')
  • 然后操控数据库,比如我们把接口返回的数据存入数据库
// 云函数初始化
const cloud = require('wx-server-sdk')
cloud.init()

// 云数纪库引入
const db = cloud.database()


// 引入axios
const axios = require('axios')

// 请求接口
const URL = 'http://xxxxx/xxx'


// 云函数入口函数
exports.main = async (event, context) => {
  const result = await axios.get(URL).then((res) => {
    return res
  })
  for (let i = 0, len = result.length; i < len; i++) {
           // 假设我们创建的集合的名字叫playlist
    await  db.collection('playlist').add({
        data:{
          // 通过扩展运算符,把返回数据的每个字段都插入集合
          ...result,
          // 新增一个创建时间字段,值取的是服务器的时间,方便前端根据时间进行排序显示
          createTime:db.serverDate()
        }
      }).then((res) => {
        console.log('插入成功')
      }).catch((err)=> {
        console.log('插入失败')
      })
  }
}

// 在for循环遍历的时候,每一次插入数据库都是一个异步的过程,所以加了一个await,这样确保一个插入成功后,再去插入下一个。
  • 上边的插入数据库的方式是一条一条的插入,如果数组里的数据很多,那么就需要操作多次数据库。这个时候我们可以用批量插入的方式来一次性插入,只需要操作一次数据库。data作为add方法的参数对象里的属性,不单单可以跟一个对象,还可以跟一个数组
exports.main = async (event, context) => {
  const result = await axios.get(URL).then((res) => {
    return res
  })

  //为防止扩展运算符报错,要先判断数组是否为空
  if (result.length > 0) {
    await db.collection('playlist').add({
      // ... 扩展运算符
      data: [...result]
    }).then((res) => {
      console.log('插入成功')
    }).catch((err) => {
      console.log('插入失败')
    })
  }
}
注意:云函数新建和修改后,一定要上传,否则是用不了的。
有时候,我们按照一定的频率请求数据,然后插入数据库,这个时候会面临一个问题,那就是请求的数据,跟数据库里的有重复的,那么就需要我们把重复的数据剔除,只插入数据库没有的数据。该怎么操作呢?我们就以数据库里的歌单为例子
const playlistCollection = db.collection('playlist')
const list = await playlistCollection .get()  // 获取到云数据库里的playlist集合

const newData = []  // 声明一个空数组,用来装对比过后不重复的数据
// 假设 playlist 是我们通过接口获取的歌单数据
for(let i=0,len = playlist.length;i<len;i++){
    let flag = true // 声明一个标识符,用来标记该条数据是否有重复 true是不重复
    for(let j=0,len = list.data.length;j<len;j++){
        if(playlist[i].id === list.data[j].id){
            flag = false
            break
        }
    }
    // 如果flag为ture证明数据库里的数据没有跟playlist[i]这条数据重复的,就放到数组里
    if(flag){
        newData.push(playlist[i])
    }
    
   //然后我们再遍历newData数组,把数据插入数据库playlist集合
  for(let i = 0,len = newData.length;i < len;i ++){
      await playlistCollection.add({
        data:{
          ...newData[i],
          createTime:db.serverDate()
        }
      }).then((res) => {
          console.log('插入成功')
      }).catch((err) => {
          console.log('插入失败')
      })
  }
}

云函数中读取云数据库的数据,一次最多只能读取100条,小程序端读取云数据库数据,一次最多只能读20条。如果数据库某集合中的数据超过了100条,我们该怎么突破最大读取数的限制呢?
  • 我们还以刚才的歌单集合为例,假设,我们的歌单集合中,有280条数据。而我们每次最多只能读取100条。那么我们要读取3次才可以读取完毕。
const MAX_LIMIT = 100  // 最大柯读取数
const countResult =  await playlistCollection.count()  // count()方法用来获取集合里数据的条数,返回的是一个对象
const total = countResult.total
const batchTimes = Math.ceil(total / MAX_LIMIT )  // 次数

const tasks = []   // 每次从数据库读取数据,都是一个异步的任务,所以声明一个数组来装这些异步任务。方便后边使用promise.all
for(let i=0;i<batchTimes;i++){
    //limit()方法,每次查询数据的上限
    // skip()方法,从指定序列后的结果开始返回。
    // 读取数据库,一开始肯定是从第0条数据开始的,然后读取100条,第二次是从第100条开始,依此类推
    const promise = playlistCollection.skip(i*MAX_LIMIT ).limit(MAX_LIMIT).get()
    tasks.push(promise )
}

// 声明一个list对象,之所以有data属性,是为了对应数据库返回的数据的格式。
let list = {
  data:[]
}

if(tasks.length > 0){
  // Promise.all(tasks)) 等待所有异步任务完成的过程,也是一个异步的过程,所以加await
  // reduce()累加数组里的元素,acc参数代表前一个,cur代表当前一个元素
 list =  (await Promise.all(tasks)).reduce((acc,cur) => {
      return {
        data: acc.data.concat(cur.data)
      }
  })
}
定时触发云函数
  • 在需要添加触发器的云函数目录下新建文件 config.json,格式如下:
{
  // triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
  "triggers": [
    {
      // name: 触发器的名字,规则见下方说明
      "name": "myTrigger",
      // type: 触发器类型,目前仅支持 timer (即 定时触发器)
      "type": "timer",
      // config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见下方说明
      "config": "0 0 2 1 * * *"
    }
  ]
}
  • cron表达式示例


    image.png
  • 当我们定时触发配置写好以后,我们还需要上传触发器。右键点击当前云函数,然后选择上传触发器。只有上传触发器后,才会按照我们设置的时间自动执行云函数
    image.png
  • 有的时候,云函数逻辑比较复杂,网络不好的情况下,云函数默认的超时时间3秒,可能满足不了我们的需求,这个时候,我们可以自己设置云函数的超时时间。云函数列表中 点击版本与配置-配置-高级配置
如何在云函数中,分页获取云数据库数据,并按照创建时间逆序排列呢?
exports.main = async (event, context) => {
  return await db.collection('playlist')
    .skip(event.start)
    .limit(event.count)
    .orderBy('createTime','desc')
    .get()
    .then((res) => {
      return res
    })
}
如何在小程序端调用云函数,并获取到我们读取的分页数据呢?
  _getPlayList(){
    wx.showLoading({
      title: '加载中',
    })
    wx.cloud.callFunction({
      name:'music',
      // data是传递给云函数的数据,云函数可以通过event获取到这几个参数
      data:{
        start:this.data.playlist.length,
        count:MAX_LIMIT,
        $url:'playlist'
      }
    }).then((res) => {
      this.setData({
        playlist:this.data.playlist.concat(res.result.data)
      })
      wx.stopPullDownRefresh()
      wx.hideLoading()
    })
  },
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容