小程序-云开发

168 阅读8分钟

云开发的初始化配置

主要的2个参数:env:传入自己的环境ID、traceUser:是否跟踪用户;

// app.js
App({
  onLaunch: function () {
    // 1.判断是否具有云开发的能力
    if (!wx.cloud) {
      console.error('请使用 2.2.3 或以上的基础库以使用云能力');
    } else {
      // 如果有云开发能力,就进行初始化
      wx.cloud.init({
        // env 参数说明:
        //   env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源
        //   此处请填入环境 ID, 环境 ID 可打开云控制台查看
        //   如不填则使用默认环境(第一个创建的环境)
        // env: 'my-env-id',
        env:"cloud1-9gzszx3828cfc3a2",
        traceUser: true, // 是否跟踪用户,可以查看访问用户列表
      });
    }

    this.globalData = {};
  }
});

image.png

为什么小程序的所有代码,都可以在云开发中使用?在project.config.json中有配置过小程序的根目录、云函数的根目录的信息:

image.png

云数据库

image.png

文档型数据库存储JSON格式对象会非常的方便,而关系型数据库则不同,关系型数据库是通过表来实现存储的,再维护表与表之间关系的时候会比较麻烦;

典型的文档型数据库:MongoDB,本质是一个数据库软件,可以创建很多数据库,每个数据库叫database,里面存储着集合collection,可以理解为数组,里面存储着记录record/文档doc,也就是对象,里面的field对应着对象中的key

image.png

image.png

向云数据库中添加一条数据:

//cloud-database:
<button bindtap="onAddDataTap"></button>

Page({
    onAddDataTap(){
        1.首先获取对应的数据库(获取当前环境的数据库,在app.js中初始化的)
        const db = wx.cloud.database()
        
        2.获取数据库集合(collection),传入集合名
        const studentCollection = db.collection("studnets")
        
        3.对集合进行新增操作:
        studentCollection.add({
            //所有添加的数据,都存储在data中
            data:{
                name:"coder",
                age:25,
                address:{
                    name:"南京",
                    code:13500,
                    alias:"南哥"
                },
                hobbies:["鸭子","皮肚"]
            },
            success:(res)=>{
                //成功的回调,返回结果
                console.log(res)
            }
        })
        
        或者通过Promise获取结果:
        studentCollection.add({
            ...和上方类似的添加数据,此处省略
        }).then(res=>{
            console.log(res);
        })
    }
    .................................
    通过asyncawait代替promise
    async onAddDataTap(){
        ...获取数据库
        
        ...获取数据库集合
        
        await studentCollection({
            ...添加数据操作
        })
    }
})

动态获取数据插入

const db = wx.cloud.database()

//取集合
const lolCol = db.collection("LOL")

lolCol.add({
    data:{
        ...添加的数据
    }
})

onAddLOLDataTap(){
    //获取数据
    for(let i = 0;i<10;i++){
        wx.request({
            url:'',
            data:{
                type:"LOL",
                page:i+1
            },
            success:(res)=>{
                const list = res.data.list
                this.handleLOLList(list)
            }
        })
    }
},
handleLOLList(list){
    for(const item of list){
        // add 是云数据库自带的方法
        lolCol.add({data:item}).then((res)=>{
            插入成功:
        })
    }
}

删除数据

image.png

前提是删除列表中的数据,因此要知道这个列表的id,也就是唯一标识

onDeleteDataTap(){
    //明确的会删除某一条数据,通过doc方法,然后传入docID
    studentCollection.doc(.docid.).remove().then(res=>{
        ...被删除的数据
    })
}

如果删除失败,需要设置权限:

image.png

上方是明确的删除一条数据,现在我们根据条件删除:

需要使用到处查询指令

const db = wx.cloud.database()

//取集合
const studentCollection = db.collection("student")

onDeleteDataTap(){
    const cmd = db.command
    
    studentCollection.where({
        age:cmd.gt(25) //年龄大于25
    }).remove().then(res=>{
        console.log(res) //可以删除,但是会报警告,并且提示很差
    })
}

通过xx.command来拿到对应的方法:gt(x)用来筛选大于x的,lt(x)用来筛选小于x的;

更新操作

onUpdateDataTap(){
    //update的方式
    //通过doc()先拿到具体的某一条数据
    studentCollection.doc("").update({
        data:{
            //更新一下之前data中存储的数据
            age:30
        }
    }).then(res=>{
        更新成功
    })
}

更新也可以通过set来实现,但是会把之前的所有数据全部替换为修改后的,而update只会覆盖而已;

    studentCollection.doc("").set({
        data:{
            age:30
        }
    })

一次性更新多条数据

    const cmd = db.command
    studentCollection.where({
        age:cmd.gt(25)
    }).update({
        data:{
            age:10
        }
    }).then(res=>{
        console.log(res)
    })

以前小程序是不支持一下更新这么多数据的,需要在云函数中定义好对应的方法,然后再调用云函数才可以,因此我们这样更新多条数据的支持性不高

查询数据

image.png

根据id查询精确一条数据

get()表示获取数据,通过res.data拿到

const db = wx.cloud.database()
onQueryDataTap(){
    //第一步永远都是拿到集合
    const lolCol = db.collection("LOL")
    //根据id查询某条数据
    lolCol.doc("").get().then(res=>{
        console.log(res.data)
    })
}
根据条件查询满足条件数据

where条件返回的都是一个数组!!

lolCol.where({
    //必须是精确,完全匹配
    nickname:"xxx"
}).get().then(res=>{
    console.log(res.data)
})
通过指令过滤数据

想要使用指令,前提就是要db.command,但是这一次返回的最多只有20条数据,想要获取更多需要进行分页查询;

const cmd = db.command
lolCol.where({
    //比xx小于等于的rid
    rid:cmd.lte(xxx)
}).get().then(res=>{
    console.log(res.data);
})

image.png

name:cmd.in(['aaa','bbb','ccc'])只要name在其中某个值就可以查询出来;

通过正则表达式匹配数据
lolCol.where({
    nickname:db.RegExp({
        //写上规则,准备匹配名字中包含2-3个z
        regexp:"z{2,3}",
        options:"i" //i标识忽略大小写;
    })
})

developers.weixin.qq.com/miniprogram…

image.png

获取整个集合的数据

小程序端最多一次性20条,但是在云函数中可以获取100条

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

//这种只能拿20条数据,并且是前20条数据
lolCol.get().then(res=>{
    
})

lolCol.skip(0).limit(5).get().then(res=>{
    拿到前5条数据
})

let page = 1
lolCol.skip(page*5).limit(5).get().then(res=>{
    偏移5条
})

排序:orderBy('xx')

//通过rid 字段来排序并且返回升序结果
//升序:asc、降序:desc
lolCol.skip().limit(5).ordeBy("rid","asc").get().then(res=>{
    
})

lolCol.field({
    //过滤,只返回指定的字段的数据
    _id:true,
}).skip().limit(5).orderBy("rid","asc").get().then(res=>{
    ...
})
总结:
// pages/cloud-database/cloud-database.js

// 1.获取对应的数据库
const db = wx.cloud.database()

// 2.获取到操作的集合(collection)
const studentCol = db.collection("students")

Page({
  // 向云数据库中添加一条数据:add()
  onAddDataTap(){
    studentCol.add({
      data:{
        name:"james",
        age:25,
        address:{
          name:"洛杉矶",
          aeg:35,
          alias:"lsj"
        },
        hobbies:["篮球","橄榄球"]
      }
    }),
    //#region success回调函数获取成功的结果
      // success:(res)=>{
      //   wx.showToast({
      //     title: '成功',
      //   })
      // }
      //#endregion

    // Promise获取成功的结果
    studentCol.add({
      data:{
        name:"库里",
        age:25,
        address:{
          name:"金州",
          aeg:35,
          alias:"金州勇士"
        },
        hobbies:["篮球","高尔夫球"]
      },
    }).then(res=>{
      console.log(res);
    })

  },

  //#region 
  // 实际上小程序是不建议这样删除的,他更推荐的是在云函数中书写删除方法,然后本地通过调用云函数来实现删除,然后将返回的结果返回给小程序端;
  //#endregion
  onDeleteDataTap(){
    // 1.向云数据库中(该数据的id号)删除一条数据:doc()+remove();其中doc()是找到具体某一个数据
    studentCol.doc("id").remove().then(res=>{})

    // 2.根据条件,查询到数据的结果,将对应的数据删除掉
    const cmd = db.command
    studentCol.where({
      // 年龄大于25:需要通过内置API(查询指令)实现:
      age:cmd.gt(25)
    }).remove().then(res=>{
    })
  },

  onUpdateDataTap(){
    //#region 1.update的方式:只会更新指定的数据(字段)
    studentCol.doc("id").update({
      data:{
        // 将age修改为30
        age:30
      }.then(res=>{
        console.log(res);
      })
    })
    //#endregion
    //#region 2.set的方式:会覆盖掉原来所有的数据,只留下更新后的数据(整个数据)
    studentCol.doc("id").set({
      data:{
        age:30
      }
    }).then(res=>{
      console.log(res);
    })
    //#endregion
  
    //#region 3.更新多条数据:先拿到符合条件的数据,然后对这些数据进行更新
    const cmd = db.command
    studentCol.where({
      age:cmd.gt(25)
    }).update({
      data:{
        age:10
      }
    }).then(res=>{})
    //#endregion
  },

  // 查询:小程序一次性最多返回20条数据,而云函数可以返回100条
  onQueryDataTap(){
    //#region 1.根据id查询某条数据:通过get()获取
    studentCol.doc("id").get().then(res=>{
      console.log(res);
    })
    //#endregion

    //#region 2.根据条件查询,多条数据
    studentCol.where({
      // 完完全全匹配到name只为詹姆斯的数据,只要是where条件返回的数据,全部都是数组类型!
      name:"詹姆斯"
    }).get().then(res=>{
      console.log(res);
    })

    //#endregion
    
    //#region 3.查询指令:gt/lt/gte/lte--》大于小于/大于等于、小于等于
    const cmd = db.command
    studentCol.where({
      age:cmd.lte(18)
    }).get().then(res=>{})
    //#endregion
  
    //#region 4.正则表达式(模糊查询)
    studentCol.where({
      name:db.RegExp({
        // 书写规则:只匹配到name中含有z的数据并忽略大小写
        regexp:"z",
        options:"i",
      }).get().then(res=>{})
    })
    //#endregion
  
    //#region 5.获取整个集合中的数据:get()默认就是从头到尾拿到所有的数据(但是一次性最多只能拿到20条,除非在云函数中)
    studentCol.get().then(res=>{})
    //#endregion
  
    //#region 6.分页:skip(和offset类似)/limit(和size类似)
    // 跳过0条数据,拿到5条数据:即前五条数据
    studentCol.skip(0).limit(5).get().then(res=>{})
    //#endregion

    //#region  7.排序orderBy("age")根据年龄来排序
    // 必须还要指定:升序:asc 或 降序:desc
    let page = 1
    studentCol.skip(page*5).limit(5).orderBy("age","asc").get().then(res=>{
      console.log(res);
    })

    // 还可以过滤字段:
    studentCol.field({
      // 想要什么字段,就设置该字段为true
      name:true,
      age:true,
      address:true,
    }).skip(0).limit(5).orderBy("age","desc").get().then(res=>{
      console.log(res);
    })
    //#endregion
  },
})
LOL案例练习:
<!--pages/lol-live/lol-live.wxml-->
<view class="list">
  <block wx:for="{{lolList}}" wx:key="rid">
    
    <view class="control">
      <button size="mini" bindtap="onItemDeleteTap" data-item="{{item}}" data-index="{{index}}">删除</button>
      <button size="mini" bindtap="onItemUpdateTap" data-item="{{item}}" data-index="{{index}}">修改</button>
    </view>

  </block>
</view>
// pages/lol-live/lol-live.js
const db = wx.cloud.database()
const lolCol = db.collection("lol")
Page({
  data:{
    lolList:[],
    offset:0,
    size:10
  },
  onLoad(){
    this.fetchLOLData()
  },

  // 获取数据
  fetchLOLData(){
    lolCol.skip(this.data.offset).limit(10).get().then(res=>{
      const newLOLList = [...this.data.lolList,...res.data]
      this.setData({lolList:newLOLList})
      this.data.offset = this.data.lolList.length
    })
  },

  // 内置API,滚动到底部触发:
  onReachBottom(){
    this.fetchLOLData()
  },

  async onItemDeleteTap(event){
    // 1.将数据库中的这条数据删除掉
    const {item,index} = event.currentTarget.dataset
    const res = await lolCol.doc(item._id).remove()

    // 2.获取到结果:请求到被删除后的数据,而不是简单的说一下删除成功
    if(res){
      // 先将请求到本地存储的数据清空一下,然后重新请求,拿到被删除后的数据
      this.setData({lolList:[],offset:0})
      this.fetchLOLData()
    }

  },
})

云存储

image.png

image.png

image.png

拷贝id,就可以在wxml中使用了

文件上传:

// pages/cloud-storage/cloud-storage.js
Page({
  data:{
    fileID:""
  },

  async onUploadTap(){
    // 1.选中本地的文件(从相册/拍照)
    const imageRes = await wx.chooseMedia({
      type:"image"
    })

    // 2.获取照片
    const imagePath = imageRes.tempFiles[0].tempFilePath

    // 图片的名称:动态:上传时间+openid+后缀名
    const timeStamp = new Date().getTime()
    const openid = "open_xx"
    // pop():移除最后一个元素并返回
    const extension = imagePath.split(".").pop()
    const imageName = `${timeStamp}_${openid}.${extension}`

    // 3.将这张照片上传到云存储中
    const uploadRes = await wx.cloud.uploadFile({
      filePath:imagePath,
      
      // 图片的名字设置,是必传的,并且避免图片名重复出现(最好动态生成)
      // cloudPath:"abc.jpg"

      // 上传到指定文件夹,注意前面不需要加上/
      cloudPath:"nba/"+imageName
    })
    console.log(uploadRes);

    // 将图片上传后得到的ID保存下来,这样在wxml中就可以src="{{fileID}}"使用上传的图片了
    this.setData({fileID:uploadRes.fileID})
  }
})

文件下载

下载会下载到小程序中(本地)

  async onDownloadTap(){
    // 1.根据fileID下载图片
    const res = await wx.cloud.downloadFile({
      fileID:fileID
    })
    // 拿到下载结果并将图片的路径放到data中,然后在wxml中引入tempFilePath
    console.log(res);
    this.setData({tempFilePath:res.tempFilePath})
  },

  async onDeleteTap(){
    // 1.删除云存储中的内容
    const res = await wx.cloud.deleteFile({
      fileList:[
        // 存放要删除的图片ID
      ]
    })
    console.log(res);
  }

获取临时链接

image.png

    async onTemplateTap(){
    // 1.获取临时链接(只会存在2小时)
    const res = await wx.cloud.getTempFileURL({
      fileList:[
        // 存放要获取到的临时文件的fileID
      ]
    })
    // 2.获取到结果
    console.log(res);
    }

云函数和云调用

image.png

image.png

第一步:

如果不想在系统自带的云函数环境中编写代码,那么当我们自己新建一个文件夹作为云函数环境时,还需要在project.config.json中标明:

image.png

第二步:

为云函数指定环境:如果之前环境中已经配置过相应的云函数,那么当指定环境后,会将之前已经配置过的云函数加载进来;

image.png

第三步:

拿到之前编写过的云函数代码:

image.png

如果想要删除某个云函数:

image.png

新建云函数:云函数环境中是Node环境,最好使用Node的语法,比如少用ES6-Module

image.png

编写云函数:编写好后,一定要记住右击文件夹进行【上传并部署】,单单保存是没有用的;这样就可以调用了

image.png

调用云函数:返回值是一个Promise

// pages/cloud-function/cloud-function.js
Page({
  async onTestTap(){
    const res = await wx.cloud.callFunction({
      // 指定调用哪个云函数
      name:"test",
    })
    console.log(res);
  }
})

云函数的调用和传参:

调用并传参:

  async sumTap(){
    const num1 = 10
    const num2 = 20

    // 调用云函数
    const res = await wx.cloud.callFunction({
      name:"sum",
      
      // 传递参数
      data:{
        num1,
        num2,
      }
    })
    console.log(res);
  },

云函数:

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

cloud.init() 

// 云函数入口函数
exports.main = async (event, context) => {
  // 1.获取调用者传来的参数
  const num1 = event.num1
  const num2 = event.num2

  // 2.计算结果并返回
  return num1 + num2
}

云端测试和本地测试

云端测试:需要先上传到云端,由于云端测试是不能传递参数的,所以我们要在测试模板中,自己手动书写参数;

image.png

不生成调试日志,因此用云端测试的很少,不推荐

本地测试:需要用到server库,要先安装一下node_modules包;然后再次运行项目,控制台中就会生成本地测试日志;或者手动debugger,并且不需要上传云端

image.png

云函数-登录操作

登录操作需要通过微信提供的:openid,真实开发中,本地需要通过一个函数拿到code,发送到服务器中,服务器根据code拿到appkey等,然后向微信服务器进行换取openid,最终再返回到本地;

但是在云服务中,就不用这么麻烦,在云函数中编写了代码,小程序端调用云函数时,微信会监测小程序当前所处的环境,拿到对应所属的openid;所以在云函数中获取到用户调用时的用户所属openid,然后返回即可;并且当我们往云数据库中存储数据,云数据库会自动存储一个openid字段;

// 云函数入口文件
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,
  }
}
  async onGetOpenId(){
    const res = await wx.cloud.callFunction({
      name:"fetchopenid"
    })
    console.log(res);
  },

此时返回了openid、appid,但是unionid是空的:需要在多个平台中申请账号;

image.png

openid用来唯一识别用户,不管用户换没换手机,只要是同一个用户,那么小程序中收藏的歌曲仍然会显示;

unionid:比如从公众号中登录小程序,但是公众号中的openid和小程序中的openid不是同一个,但是unionid是一样的;多平台共享!

appid账号:开发小程序时申请的账号,这个账号同时可以开通小程序、公众号、第三方登录;

其中第三方登录是:在其他app中使用微信登录,openid在此情况下就会不同,那么在此基础上想要唯一识别就会使用unionid赋值给你;多平台共享!

在开发中,openid和unionid都是后端来操作的,这里我们拓展一下即可,因为使用了云服务;

云函数对数据库的操作

使用云函数的好处

  • 小程序端对数据库的操作有限制
    • 之前的一次性删除/修改多条数据(现在可以了,但是提示不友好)
  • 获取小程序端的数据,一次性最大只有20条,而云函数可以有100条
  • 在云函数端是可以编写代码逻辑的,层次分明更加友好;
// 操作数据库
exports.main = async (event, context) => {
  // 1.获取数据库和集合
  const db = cloud.database
  const lolCol = db.collection("lol")

  // 2.从集合中查询
  const res = await lolCol.get()
  return res
}
  async onGetLoLData(){
    const res = await wx.cloud.callFunction({
      name:"getlol"
    })
    console.log(res.data);

    // 还可以返回一个对象形式
    return {
      name:"英雄联盟",
      data:res.data
    }
  }

也可以在云函数中进行调用时的筛选,简而言之就是云函数非常的灵活,用来弥补小程序的逻辑不足;

云函数发送网络请求

注意要进行序列化:也就是res.data

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

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

// 网络请求
exports.main = async (event, context) => {
  // 1.从服务器中发送请求
  const res = await axios.get("http://123.207.32.32:8000/home/multidata")

  // 2.对数据进行转换,发给客户端
  return res.data
}
  async onGetHouseData(){
    const res = await wx.cloud.callFunction({
      name:"fetchHomeMulData"
    })
    console.log(res);
  }

云调用-生成小程序码

developers.weixin.qq.com/miniprogram…

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

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

// 云函数入口函数
exports.main = async (event, context) => {
  // 1.生成小程序码
  const res = cloud.openapi.wxacode.createQRCode({
    width:320,
    path:"pages/cloud-database/index"
  })

  // 2.获取到数据,并且上传到云存储中
  const wxContext = cloud.getWXContext()
  
  const timestamp = new Date().getTime()
  const openid = wxContext.OPENID

  const extension = res.contentType.split("/").pop()
  const cloudPath = `${timestamp}_${openid}.${extension}`

  const qrRes = await cloud.uploadFile({
    // 这里不能用filepath,而是要通过fileContent
    fileContent:res.buffer,

    // 命名
    cloudPath
  })
  return qrRes
}
  async onGetMiniData(){
    const res = await wx.cloud.callFunction({
      name:"getCode"
    })
    // 然后就可以在wxml中使用image src="qrCodeFileID"
    this.setData({qrCodeFileID:res.result.fileID})
  }