2019年11月

全栈养成技

理想的开发状态是开发者只需要关注业务逻辑和产品设计

去年9月,微信团队与腾讯云就已经推出了“云开发”平台,云开发是这样介绍的

开发者可以使用云开发开发微信小程序、小游戏,无需搭建服务器,即可使用云端能力。云开发为开发者提供完整的原生云端支持和微信服务支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快速上线和迭代,同时这一能力,同开发者已经使用的云服务相互兼容,并不互斥。

在了解云开发的时候,有过几个疑问:

  • 云开发有什么能力
  • 在已有的开发架构上融入云开发的兼容性是否良好,是如何处理的
  • 如何请求和接收外部的网络
  • 对于一个小程序来说,一般都至少要有一个web后台来供运营使用,运营后台是否也可以使用云开发
  • 是否支持其他平台(支付宝、头条、淘宝)小程序的开发和使用
  • 企业微信的兼容性如何
  • 云开发对接企业微信或者其他平台是否方便

对于这些疑问,实际体验一下或许可以更快解答

动手尝鲜

明确需求

用于连通线上业务和线下门店的预约到店小程序插件对于很多电商小程序来说是刚需,我在云启星辰开发过一款店铺预约的小程序插件,这个插件现在已经使用在了周大福、老凤祥等一线珠宝品牌的线上小程序中。我们来看看如何使用云开发来改造这款插件

插件使用后UI样式如图,样式代码不再展示,我们详细看说一下云开发的过程

需求样式

开通云开发

云开发的开通需要在微信开发者工具上进行开通。点击上方工具栏的云开发按照流程创建环境即可开通云开发。

尝试了一下,免费版似乎只能创建两个环境。一个用来开发测试、一个部署正式的线上服务和数据。

先看一下云开发有什么能力:云存储、云数据库、云函数、日志运维、监控,分析统计,我们在实际体验中一个一个来看

云存储

云存储顾名思义就是把资源储存在云端上,我们可以用来储存用户的一些头像、生成的小程序码、上传的图片视频等,也可以用来储存小程序的UI文件,对于日益复杂的小程序来说,把UI资源储存在云端还是很有必要的。

所以我们先来尝试一下云储存,把相关的UI文件放在云储存中。

添加和读取云储存的数据有三种方式:

  • 小程序通过云开发API调用
  • 云函数通过API调用
  • 在开发者工具中进行上传查看和添加

开发过程中的UI资源,我们可以直接新建一个文件夹进行上传储存,之后直接获取资源链接进行调用就可以了。

云数据库

和云储存一样,增删改查云数据库数据有三种方式:

  • 小程序通过云开发API调用
  • 云函数通过API调用
  • 在开发者工具直接处理

其实使用过MongoDB的同学再来使用云数据库会有熟悉感。

预约店铺,肯定是要有店铺的数据的,预约成功之后还要保存预约记录。

  • 店铺数据

我们首先来创建一个store的集合,手动添加一条记录:

{
    "address":"北京市东城区东长安街",
    "location":{
        "latitude":39.90374,
        "longitude":116.397827
    },
    "city":"深圳",
    "province":"广东",
    "country":"南山区",
    "banner":"https://7971-yq-demo-test-47yk3-1300688546.tcb.qcloud.la/UI/reserve/storeBanner.png?sign=fd1f7e9be2340c9249b18140af379442&t=1573793815",
    "store_name":"迦南的小宝藏",
    "icon":"icon",
    "introduction":"introduction",
    "mobile":"19989897878",
    "status":1
}

默认情况下,新增的每条数据会有一个_id的字段,该字段是UUID的形式,一个集合里该字段唯一

  • 预约记录

预约成功肯定是要把记录存起来的,所以还需要一个预约记录的集合reserve

小程序端API尝鲜

  • 初始化

小程序在使用云开发的时候要先进行初始化,如果是开发小程序,我们就直接放在app.jsonLaunch中即可,现在我们开发的是插件,所以放在插件目录的index.js中即可


//  初始化云开发
wx.cloud.init({
  env: '环境ID'  // 这里的ID是我们之前创建环境时候的环境ID,不是环境的名字,环境一旦创建,ID将是唯一的。
})
  • 初始化数据库

在插件中组件的js文件attached进行数据库引用

const testDB = wx.cloud.database({
  env: '环境ID'  // 这里的ID是我们之前创建环境时候的环境ID,不是环境的名字,环境一旦创建,ID将是唯一的。
})
  • 查询店铺数据

在正常的业务逻辑中,进入插件时是已经拿到店铺ID的,所以我们直接用已经有的店铺ID来查询店铺数据就好。

云开发的接口不管是小程序端开始服务端都是支持promise的,所以我们直接使用promise的方式使用。

首先通过collection来获取集合的引用,之后获取记录的引用即可,使用方式如下:

        // 查询store集合中`_id : that.data.storeId`的记录
        db.collection('store').doc(that.data.storeId).get().then(res => {
            let { data } = res;
            this.setData({
                store: data
            })
        }).catch((error) => {
            wx.showToast({
                title: '加载店铺信息失败,请重新进入',
                icon: 'none'
            })
            console.error(error)
        })

云函数尝鲜

我们已经获取到店铺的数据了,之后就要尝试提交数据了。

在小程序端是可以直接使用add来提交数据的,但是并不建议这么做,考虑到预约成功会发送模板消息,以及要给插件适用方回调数据,使用小程序端API显然是不合适的。

在插件中使用云开发其实这一点是有点点不爽的,因为自动创建的插件开发结构里是不包含云开发的,我尝试了把云开发的目录复制到插件开发的结构中并不可以,所以我们需要新建一个同样AppId的小程序进行云函数的开发。

创建好的云开发结构中会比通常的小程序开发结构多一个cloudfunctions的目录(可以在project.config.json里修改cloudfunctionRoot),这里用来储存云函数。云函数的每个目录里都有一个package.json文件,所以到这里,我在想是不是可以直使用npm来引入我们所需要的module,比如axios等,查看了官方文档之后发现确实支持。

创建云函数

创建云函数可以直接在cloudfunctions目录中新建一个目录,名称为函数名,目录内至少包含一个index.js文件,改文件必须要有一个exports.main的入口函数。或者可以在开发者工具的编辑器中,云函数的本地根目录上右键,新建Node.js云函数。也可以直接在云开发控制台新建一个云函数,之后在开发者工具中同步函数列表。

新建好的云函数index.js模板代码如下:

// 云函数入口文件
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,
  }
}

入口函数中有两个参数eventcontext

event就是调用云函数时候传入的参数,以及自动注入的小程序用户的openid和小程序的appidcontext对象包含了此处调用的调用信息和运行状态,可以用它来了解服务运行的情况。

简单改造尝试云函数

官方文档给了一个处理数据相加的例子,我们来试试。

// 官方示例
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
})

到这里我们尝试时发现返回的并不是理想中的sum = 3,这里需要在云开发控制台中打开本地调试或者要在开发者工具中将该云函数右键上传并部署。其实这一步对于我们正常的开发和联调来说是略微有些麻烦的,并不能像我们正常node开发中,把环境改为本地域名,之后直接运行就可以,并且还要打开好几个窗口,略微有点点不爽。

另外本地调试之前,我们要先进入云函数的目录中npm install或者yarn一下,否则本地调试会报错。

我们现在本地调试中进行调试,选择本地调试窗口左侧要调试的云函数,勾选右侧的本地调试,之后修改请求参数(可以保存为模板方向下次调试使用),点击调试,即可在Source中打断点以及在Console中看见返回的内容。

在服务端查询数据库

云函数的Hello World 就体验完了,我们尝试编码需求中所需要的云函数。

新建一个云函数reserve

初始化云函数

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

引用数据库

const db = cloud.database()

在集合上新增记录


  await db.collection('todos').add({
    data: {
      mobile,
      name,
      reserve_at,
      time
    }
  })

修改返回结果

  return {
    code: 1,
    msg: '添加成功'
  }

为了防止出现错误,我们使用try来包裹一下

完整的云函数文件下

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
});
const db = cloud.database();
// 云函数入口函数
exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  try {
    let { mobile, name, reserve_at, time } = event
    await db.collection('reserve').add({
      data: {
        mobile,
        name,
        reserve_at,
        time,
        openid: wxContext.OPENID,
        appid: wxContext.APPID,
        unionid: wxContext.UNIONID,
      }
    })
    return {
      code: 0,
      msg: '添加成功'
    }
  } catch (e) {
    console.error(e)
    return {
      code: -1,
      msg: '添加失败'
    }
  }
}

本地调试成功返回了{code: 0,msg: '添加成功'}

之后去数据库查看,已经多出了如下数据:

{
    "_id":"05a1947c5dd2606b01333ceb6ca8f34f",
    "reserve_at":"2019-11-12",
    "time":"上午",
    "openid":"orjAC5bpYbrTRRRWLdeh8Bbi8MuE",
    "appid":"wxba6cc8995c8d1ac3",
    "mobile":"19989897878",
    "name":"殷迦南"
}

增加回调

这是一个插件,必然要给插件的使用方回调相应的数据,所以我们试试对外发起请求是否方便。

在这里我们使用axios

在云函数目录下npm install axios --save 或者yarn add axios

  • 引入axios
const axios = require('axios')
  • 增加回调
    const callback = await axios({
    method: 'post',
    url: 'https://dev.xxxxx.com/xxxxx',
    data
    })

判断callback的状态码是否200判断是否回调成功。

尝试企业微信支持情况

在不使用云开发的情况下,wx.qy.login获取到code后,请求企业微信API是只能获取到userid,获取不到openidunionid,但是在云开发的测试体验中是可以获取的到,其结果和个人微信相同,
所以在企业微信的开发中依旧不能避免走wx.qy.login来获取code从而获取到用户信息。但是其他方面支持的还是很完善的。

与外部网络交互情况

在之前做回调的时候,是可以用请求外部网络的,那外部网络如何访问云函数或者云数据库呢?

在仔细看了云开发文档之后发现,HTTP触发云函数和调用数据库必须要有access_token,所以肯定不能用做企业微信等平台的回调请求,类似的请求依旧是需要单独开发后端接口来接收回调,后端接口在获取access_token之后请求云函数或云数据库进行下一步操作。

因此,也不适合基于云开发开发一套后台web管理系统,web后台依旧需要对应的后端服务。

日志运维、监控、分析统计

日志运维、监控和分析统计是云开发的一大亮点。

每一次请请求日志都会被记录下来,包括错误信息以及请求接口的情况,另外还有高级日志支持andor逻辑运算连接符等操作进行查询,极大的方便了错误排查和运维。

开发者平台中的监控可以监控函数调用次数、错误次数、请求时间以、外网流量以及资源使用情况等。分析统计具体的用户访问情况,结合日志,可以清楚的看到每个用户在小程序上的具体操作,这对开发和运营来说也是一大助力。

但是这些东西似乎目前只能在微信开发者工具中使用和查看,我很期待支持触发回调、一键同步到TAPD指派具体开发人员等操作。

是否支持其他平台

与其他平台打通的前提是拥有自身的一套用户体系,单独使用云开发是不现实的,与接收外部请求一样,依旧需要现有后端代码进行"转发"之后才有实现的可能性。

总结

  • 云开发有什么能力
  • 在已有的开发架构上融入云开发的兼容性是否良好,是如何处理的
  • 如何请求和接收外部的网络
  • 对于一个小程序来说,一般都至少要有一个web后台来供运营使用,运营后台是否也可以使用云开发
  • 是否支持其他平台(支付宝、头条、淘宝)小程序的开发和使用
  • 企业微信的兼容性如何
  • 云开发对接企业微信或者其他平台是否方便

先解答一下最开始的几个疑惑

Q:云开发有什么能力
A:云存储、云数据库、云函数、日志运维、监控,分析统计

Q:在已有的开发架构上融入云开发的兼容性是否良好,是如何处理的
A:云开发和已有架构是并不冲突的,可以随时加入云开发,减少原本服务器压力

Q:如何请求和接收外部的网络
A:可以使用node进行对外的请求,在接收外部请求的时候必须要携带access_token进行安全校验

Q:运营后台是否也可以使用云开发
A:从外部网络请求云函数和云数据库来看,并不是很友好,需要使用自己的服务端进行“中转“

Q:是否支持其他平台(支付宝、头条、淘宝)小程序的开发和使用
A:在有自身一套用户体系的前提下,使用自己的服务端中转其他平台小程序的请求和数据是可以实现多平台使用的

Q:企业微信的兼容性如何
A:使用云开发来开发企业微信和不使用云开发以及使用云开发的个人小程序来对比,1、弱化了后端和运维,降低了成本;2、可以做到快速迭代和上线;3、暂时无法进行企业微信的免鉴权;

Q:云开发对接企业微信或者其他平台是否方便
A:往往其他平台都是具有回调等常规操作的,单纯依靠云开发略有困难

大概总结一下目前版本的云开发锁具有的优缺点吧

优点:
1、对于个人微信小程序来说,小程序相关的业务逻辑完全做到了云端开发,并且天然鉴权,开发者只需编写自身业务逻辑代码
2、数据库即可在小程序端操作也可以在云函数操作
3、不需要自建CDN进行资源存储
4、可以把一个前端工程师在无任何学习成本的情况下变为一个全栈工程师,开发出一个高质量的小程序
5、基于业务逻辑的0后端0运维
5、基于大厂服务来看,非常的安全和稳定

不足:
1、对于一个功能复杂的小程序来说,非业务逻辑(web后台等)暂时做不到完全做到0后端0运维,依旧需要后端支持
2、暂不支持企业微信的天然鉴权

总体来说我还是非常看好云开发,据了解,云开发还正在进一步封装腾讯云、微信平台的其他服务,包括 AI、音视频、订阅消息、微信支付等,提供扩展能力,让开发者可以更便捷地调用,在更多业务场景中可以相关能力。平台的扩展能力还会进一步加强。这是一种新的理念和新的标准,或许未来真的可以做到所有的产品都可以使用云开发。