学习视频

参考资料

推荐阅读

项目源码:关注公众号BaretH后台回复 云开发 获取

在这里插入图片描述

当今时代,千行百业都在拥抱云计算云原生,进行数字化创新和升级。我们也在前一篇文章中了解到底什么是云原生以及其一系列的技术栈与方法论,还不太了解的同学可以看看 硬核科普:到底啥是云原生?随着云和软件即服务的宏观趋势的结合及技术的进步,云原生给全生命周期带来了一个全新开发标准解决方案—云开发。本节内容我们就来聊聊云开发,并用云开发开发一个简单的微信小程序~本教程将围绕着腾讯云及其一系列服务展开。

image-20211112112233920



1. 什么是云开发

百度百科:云开发(CloudBase)是云端一体化的后端云服务 ,采用 serverless 架构,免去了移动应用构建中繁琐的服务器搭建和运维。同时云开发提供的静态托管、命令行工具(CLI)、Flutter SDK 等能力降低了应用开发的门槛。使用云开发可以构建完整的小程序/小游戏、H5、Web、移动 App 等应用。

image-20211113162124162

我们来理解一下官方定义中的最重要的两个概念:

  • 云端一体化的后端云服务:说白了就是云开发为开发者提供完整原生云后端服务支持,也就是我们开发已经在全部部署好的后端环境中进行,只需要由前端程序员专注于业务逻辑的编写,弱化了后端和运维的概念。
  • 一站式 Serverless 服务:serverless 即无服务,意思就是开发者无需购买数据库存储等基础设施服务,无需搭建服务器即可使用,云开发已为上述服务提供了完整的云端支持,能轻松实现快速上线与迭代。

那么为什么要使用云开发呢?

  • 因为云开发只关注业务逻辑,效率更高,开发成本也更低,直接以函数的形式调用云端服务。

云开发有哪些应用场景呢?

  • 微信小程序

    云开发为小程序开发者提供完整的原生云端支持和微信服务支持。

  • 微信公众号/H5 应用/PC Web 应用

    云开发为 H5 类应用提供丰富 SDK 能力,可作为公众号后台,普通 H5 应用,H5 活动页,同时也支持作为 PC Web 后台应用,例如 Web 管理系统,Web 网站等。

  • 移动应用

    云开发推出了 Flutter SDK,在 iOS、Android 等移动应用平台中集成,可以方便使用云函数、云存储等能力



2. 对比传统开发模式

我们来对比一下云开发的模式与传统的开发模式:

image-20211112122218654

传统开发有5个步骤:首先产品经理对产品进行需求设计,然后由后端开发工程师针对需求进行API开发,再进行后端的部署上线,再由前端工程师完成前端页面设计与数据请求,最后才是正式发布。而云开发的只需要3个步骤:首先也是产品经理对产品进行需求设计,然后交给前端工程师进行开发,开发完成后即可正式发布。

可以看到,传统开发模式对于产品开发周期是相对比较长的,而使用云开发大大减少了开发步骤与开发难度, 而是更注重于业务逻辑,使一个人实现全栈开发变得可能。而这一切的原因正是因为云开发提供完整的后端云服务,提供数据库存储、函数、静态托管等基础能力,以及扩展能力;无需管理基础架构。相比较传统的开发模式,云开发至少可节省50%的人力成本、交付效率提升70%。

云开发与传统的前后端开发模式天然互补。基于云开发构建应用层/服务中台,能够弥补传统开发模式的“效率低、耗时多、依赖后台、不够灵活”等问题,更快响应业务需求。



3. 云开发的三个重要部分

  1. 存储:高扩展性、低成本、可靠和安全的文件存储服务,可快速的实现文件上传下载、文件管理功能。

  2. 云函数:在腾讯云基础设施上弹性、安全地运行云端代码,提供的云函数能力无需购买、搭建服务器即可快速运行开发者自定义函数。

    函数通俗来说就是能重复使用的,实现了某个功能的代码块,也就是为了实现某个功能而提前将代码封装好。而云函数就是将函数放在了云端。

  3. 数据库:高性能的数据库读写服务,可以直接在客户端对数据进行读写,无需关心数据库实例和环境



4. 云开发服务介绍

随着云开发的快速接入,开发者可以直接以函数的形式调用并使用已经集成好的服务,比如音视频服务(提供高品质、实时音视频通话服务)、智能图像服务(集成智能鉴别、人脸识别等AI能力),当然不仅如此,还有很多其他的技术集成服务,通过云开发我们可以直接腾讯的技术团队过去数十年在人工智能、图像处理、音视频服务等领域积累下来的技术积累,这样可以大大在传统模式下对先进技术摸索的时间。

因此使用云开发,就是站在腾讯技术团队这个巨人的肩膀上去利用技术解决产品的需求。



5. 使用云开发的开发流程

使用云开发进行小程序、小游戏开发的基础步骤十分简单,如下图所示:

image-20211112123409107

接下来我们以一个简单的微信小程序为例,带大家体验一下云开发的魅力!



6. 云开发实例—小程序实战

1. 项目介绍

该项目分为三个页面:首页、日志页面、我的页面。我的页面点击登录会展示用户头像和用户昵称,在首页点击+1或者-1的按钮,在日志页面会同步显示我们点击的日志。
在这里插入图片描述


2. 创建项目、云开发初始化、页面初始化

云开发小程序可以理解为微信官方为我们提供了一个免费的服务器并帮我们部署好了小程序环境,它将服务器的一些功能比如增删改查操作都封装成了接口供我们直接操作,对于小程序的上线访问,我们也不需要自己的域名和服务器,一件部署即可让别人通过微信搜索到并访问。

创建云开发项目首先需要 注册一个小程序账号 并且下载 微信开发者工具

首先我们来创建一个小程序项目,首先起一个项目名称,然后填入AppID,也就是小程序ID,注意后端服务这里选择不使用云服务, 因为如果选择微信云开发的话,系统会为我们创建许多使用不到的模版,删除比较麻烦,所以我们选择不使用云服务,自己去配置云开发的环境。模版选择的话选择不使用模版。

image-20211112180527515

创建完成后如下图所示,然后我们点击云开发按钮开通云开发的功能服务。

image-20211112180722120

然后我们新建一个环境,这里设置为test,然后会对应一个唯一环境ID,这样云开发的后台就创建完成。

image-20211112181144582

接下来来进行云开发的初始化,我们在项目根目录下添加一个云开发的文件夹cloud,然后在project.config.json中配置该文件夹的路径。

image-20211112181610754

保存 project.config.json 文件后,即可看到 cloud 文件夹上有云的标志以及当前的云环境

image-20211112181908077

然后我们删除 app.js 文件中 onLauch() 方法中的所有代码,然后我们在其中添加代码,指定云开发的环境。

// app.js
App({
 onLaunch() {
    wx.cloud.init({
      env: 'test-0gk0wv6540874698',  // 用env指定云开发环境id
      traceUser: true  // 表示将用户访问接入到用户管理中
    })
 }
})

接下来我们进行页面的初始化,默认已经包含了indexlogs页面,还缺少一个me我的页面,我们在 pages 目录下新建me文件夹,然后在me文件夹新建一个名为 me 的 page,这样就会在 me 文件夹下生成四个 me 的文件,在 app.json 中的 pages 对象里面也会自动将 me 页面的路径加上。

image-20211112185217811

然后我们再新建一个 images 文件夹来存放图片,我们将准备好的照片粘贴进去。

image-20211112191016892

接下来我们在 app.json 文件中修改一下导航栏的标题和颜色,然后加上一个tab底部导航栏

{
  "pages": [
    "pages/index/index",
    "pages/logs/logs",
    "pages/me/me"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#EA5149",	// 导航栏背景颜色
    "navigationBarTitleText": "BaretH",	// 导航栏标题
    "navigationBarTextStyle": "black"
  },
  "tabBar": {	// 底部导航栏配置
    "list": [{
      "pagePath": "pages/index/index",	// 页面路径
      "text": "首页",	// 标题
      "iconPath": "images/binggan.png",	// 不点击时显示的图片
      "selectedIconPath": "images/binggan-active.png"	// 点击时显示的图片
    }, {
      "pagePath": "pages/logs/logs",
      "text": "日志",
      "iconPath": "images/danhuang.png",
      "selectedIconPath": "images/danhuang-active.png"
    }, {
      "pagePath": "pages/me/me",
      "text": "我的",
      "iconPath": "images/huasheng.png",
      "selectedIconPath": "images/huasheng-active.png"
    }]
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json",
  "lazyCodeLoading": "requiredComponents"
}

编辑完保存即可看到效果

image-20211112195548261


3. 登录功能(云函数创建和调用)

登陆功能的实现在 me 页面中完成,首先删除 pages/me/me.wxml中的内容,然后我们添加一个登陆按钮

<button open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="onGotUserInfo">登陆</button>

image-20211112221539743

这里登陆按钮绑定了一个onGotUserInfo方法,我们在该方法里获取用户信息,我们清空 me.js 的内容,在其中实现该方法

Page({
  data: {
    userInfo: {} // 保存用户信息的变量
  },
  onGotUserInfo: function (e) { // 一定要传递参数e
    this.setData({
      userInfo: e.detail.userInfo // 将用户信息保存在userInfo里面
    })
    console.log(this.data.userInfo)
  }
})

这里将获取到的用户进行进行打印来判断是否成功:可以看到成功获取到了用户信息,但是缺少最终的 openID 信息,他是用户身份信息的唯一标识

image-20211112215519642

openID 属于比较私密的信息,我们需要通过wx服务器获取,也就是需要通过云函数来获取,我们在 cloud 文件夹下来新建一个云函数 login

image-20211112215846355

然后系统为自动帮我们创建以下三个文件,其中我们主要进行操作的就是index.js文件

image-20211112215938666

查看默认的 index.js,可以看到默认创建的代码里面已经获取了 openID,我们只需要留下这一行代码即可

image-20211112220251914

云函数修改完成后,一定要右键点击该函数选择上传和部署才能使用

image-20211112220419612

然后我们需要修改 me.js,在其中调用云函数来获取 openID 信息

Page({
  data: {
    userInfo: {}, // 保存用户信息的变量
    openID: "" // 保存用户openID信息
  },
  onGotUserInfo: function (e) { // 一定要传递参数e
    const that = this // 保存this对象
    // 调用云函数
    wx.cloud.callFunction({
      name: "login",
      success: res => {
        console.log("云函数调用成功")
        that.setData({
          openID: res.result.openid, // 将用户openID保存在opendID里面
          userInfo: e.detail.userInfo // 将用户信息保存在userInfo里面
        })
        console.log(this.data.userInfo, this.data.openID)
      },
      fail: res => {
        console.log("云函数调用失败")
      }
    })
  }
})

然后我们进行测试,可以在控制台看到成功获取了 userInfo 和 openID

image-20211112221502883

然后我们编写 me.wxml 文件,实现如果获取到 openID 就不显示登陆按钮,如果没有获取到 openID 就显示用户头像和昵称

<view wx:if="{{!openID}}">
  <button open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="onGotUserInfo">
    登陆
  </button>
</view>
<view class="uploader-container" wx:if="{{openID}}">
  <image class="image" src="{{userInfo.avatarUrl}}"></image>
  <view class="name">{{userInfo.nickName}}</view>
</view>

对应的样式文件 me.wxss

/* pages/me/me.wxss */

.image {
   width: 100px;
   height: 100px;
   margin: 0 auto;
   display: block;
   padding-top: 20px;
}

.name {
   text-align: center;
   padding-top: 10px;
}

button {
   background: #ea5149;
   width: 60%;
   margin-top: 100px;
   color: #fff;
}

然后我们保存测试,点击登陆即可显示用户头像和昵称。

image-20211112224324273

但是此时还是有一个问题,就是当我们刷新页面后,这里点击编译,获取到的用户信息就会清空

image-20211112224454027

如果我们想要刷新后还能保持用户的登录状态,我们应该将用户信息存入缓存中,这样用户信息只需要从缓冲中获取即可,而不需要每次重新请求后端进行获取。

这里通过首先将 userInfo 和 openID 合并到一起,将 openID 作为 userInfo 的一个字段存入,然后将 userInfo 通过云函数存入缓存中。最后新增一个 onLoad 方法,用于在页面加载的时候就获取用户信息并赋值给 userInfo 变量

image-20211112230711104

完整代码如下:

Page({
  data: {
    userInfo: {}, // 保存用户信息的变量
    openID: "" // 保存用户openID信息
  },
  onGotUserInfo: function (e) { // 一定要传递参数e
    const that = this // 保存this对象
    // 调用云函数
    wx.cloud.callFunction({
      name: "login",
      success: res => {
        console.log("云函数调用成功")
        that.setData({
          openID: res.result.openid, // 将用户openID保存在opendID里面
          userInfo: e.detail.userInfo // 将用户信息保存在userInfo里面
        })
        that.data.userInfo.openID = that.data.openID // 将openID作为一个字段加入userInfo中
        console.log(that.data.userInfo)
        wx.setStorageSync('userInfo', that.data.userInfo) // 将userInfo保存至缓存中
      },
      fail: res => {
        console.log("云函数调用失败")
      }
    })
  },
  // 页面加载时执行
  onLoad: function (options) {
    // 从缓存中获取userInfo
    const userInfoFromStorage = wx.getStorageSync("userInfo")
    this.setData({
      userInfo: userInfoFromStorage,
      openID: userInfoFromStorage.openID
    })
  }
})

然后我们再次测试,可以看到调试器storage缓存中有userInfo信息,并且我们点击编译,用户仍处于已经登陆状态

image-20211112231325594


4. 点击按钮生成记录数据(云数据库的插入)

该部分主要操作index文件夹,首先清空 index.wxml 文件,然后添加两个标签

<view class="container">
  <view class="right button">+1</view>
  <view class="left button">-1</view>
</view>

然后清空 index.wxss,编写对应的样式文件

.button {
  width: 70px;
  height: 70px;
  line-height: 70px;
  border-radius: 20%;
  border: none;
  text-align: center;
  font-size: 25px;
  color: #fff;
  font-weight: bold;
  margin-top: 50px;
}

.right {
  background: #ea5149;
  float: right;
}

.left {
  background: #feb600;
}

到此基本页面编写完成

image-20211112232337758

然后我们修改 index.wxml,给这两个标签绑定两个函数,并设置对应的参数:

<view class="container">
  <!--bindtap绑定点击事件addLog()方法 传递参数data-add  add是自己定义的-->
  <view class="right button" bindtap="addLog" data-add="1">+1</view>
  <view class="left button" bindtap="addLog" data-add="-1">-1</view>
</view>

然后清空 index.js,在其中添加绑定的 addLog 方法:

Page({
  // event表示前端传过来的参数
  addLog(event) {
    // 获取add参数
    const add = event.currentTarget.dataset.add
    console.log(add)
  }
})

然后我们在调试器进行测试,可以看到点击 +1 按钮控制台就打印 1,点击 -1 按钮控制台就打印 -1

image-20211112235903408

接下来我们将这些数据插入到数据库中,我们进入云开发的后台,点击数据库,然后添加一个集合logs

image-20211113000107298

然后设置集合的权限为:所有用户可读、仅创建者可写

image-20211113000139001

设置完成后,我们新建一个云函数createlog,功能就是向前面创建的 logs 集合中插入数据,数据形式包含三个字段:add参数、点击时间、点击者的open ID。我们修改其中的 index.js 文件:

image-20211113112520325

// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
  env: 'test-0gk0wv6540874698'
})
const db = cloud.database() // 获取数据库连接对象

// 云函数入口函数(event从前端传s递来的数据)
exports.main = async (event, context) => {
  try {
    // await搭配async-->异步等待:需要执行完await后的语句,才可以往下执行
    return await db.collection('logs').add({ // db-数据库、指定logs集合 在集合中创建一条语句add
      data: { // 插入的数据放在data中
        add: event.add, // 点击的参数
        date: event.date, // 点击时间 
        openID: event.openID // 点击者的openID
      },
      success: function (res) {
        // res 是一个对象,其中有 _id 字段标记刚创建的记录的 id
        console.log(res)
      }
    })
  } catch (e) {
    console.log(e)
  }
}

编辑完成后记得右键上传部署:

image-20211113001043123

然后我们回到pages/index/index.js来调用上述craetelog云函数,传送上述 data 中的三个字段信息

Page({
  // event表示前端传过来的参数
  addLog(event) {
    // 获取add参数
    const add = event.currentTarget.dataset.add
    console.log(add)
    // 获取缓存信息
    const ui = wx.getStorageSync("userInfo")
    // 根据缓存判断用户是否登陆
    if (!ui.openID) {
      // 如果没有登陆跳转到me我的页面
      wx.switchTab({
        url: '/pages/me/me',
      })
      // 如果已经登陆则请求云函数createlog向数据库中插入data
    } else {
      wx.cloud.callFunction({
        name: "createlog",
        data: {
          add: add,
          date: Date.now(),
          openID: ui.openID // 从缓存中获取到openID字段
        }
      })
    }
  }
})

编写完成后我们进行测试,点击 +1、-1 的按钮即可在云数据库中看到数据的变更:

image-20211113112610026


5. 将记录数据展示到页面上(云数据库的读取)

接下来我们将插入的这些数据展示到日志页面中,该部分主要操作logs文件夹

我们同样适用云函数来获取数据,首先新建一个 Node.js 云函数,命名为getlog,然后编写其中的index.js 文件,在其中实现通过前端传递过来的 openID 字段来获取用户所有的日志信息。

// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
  env: 'test-0gk0wv6540874698'
})
const db = cloud.database() // 初始化数据库

// 云函数入口函数
// 功能:通过从前端传递过来的openid字段获取用户的所有信息
exports.main = async (event, context) => {
  try {
    return await db.collection('logs').where({ // 查询语句
      openID: event.openID
    }).get()
  } catch (e) {
    console.log(e)
  }
}

编写完成记得保存并上传部署,接下来修改pages/logs/logs.js文件,在其中调用 getlogs 云函数,其中要传送用户的 openID 信息,并且当 getlogs 函数执行成功时我们将获取到的日志信息存储在 logs 变量中

// logs.js
const util = require('../../utils/util.js')

Page({
  data: {
    logs: []
  },
  getlogs: function () {
    const that = this
    const ui = wx.getStorageSync("userInfo") // 从缓存中获取userInfo
    // 通过缓存中的openID判断用户是否登陆
    // 用户未登录则跳转到me页面
    if (!ui.openID) {
      wx.switchTab({
        url: '/pages/me/me',
      })
      // 用户已登陆则调用getlogs云函数
    } else {
      wx.cloud.callFunction({
        name: "getlogs",
        // data对象——需要向getlogs云函数传递openID数据
        data: {
          openID: ui.openID
        },
        // 云函数执行成功
        success: res => {
          console.log("res", res)
          that.setData({ // 给logs数组赋值---数组处理使用map
            logs: res.result.data.map(log => {
              // 格式化date变量 调用util中的formatTime()方法
              var date = util.formatTime(new Date(log.date))
              log.date = date //更新数据
              return log
            })
          })
        },
        // 云函数执行失败
        fail: res => {
          console.log("res", res)
        }
      })
    }
  },
  // onLoad 页面首次加载的时候执行
  // onShow 页面每次切换的时候执行
  onShow: function () { // 使用onShow声明周期函数设置在页面加载的时候会自动执行
    this.getlogs()
  }
})

然后我们修改一些logs.wxml页面,遍历 logs 变量,展示信息

<!--logs.wxml-->
<block wx:for="{{logs}}" wx:key="log">
  <view class="log-item">
    日期:{{item.date}} 分数:{{item.add}}
  </view>
</block>

然后我们进行测试即可看到已经成功实现:

在这里插入图片描述


6. 项目部署上线

到此小项目已经完成了,最后我们将其部署上线,首先我们来真机调试一下,点击如下按钮然后在手机上进行测试:

image-20211113121221605

手机上测试没有问题后,点击右上角的上传按钮进行上传

image-20211113121425185

上传成功后我们就可以在小程序管理后台中的管理/版本管理中的开发版本模块看到刚上传的信息了,我们点击其中的体验版再用手机测试一下,如果没有问题就可以提交审核了,提交审核后就会在审核版本模块看到审核信息,然后就需要等待微信团队进行审核(一般1~2天),审核通过后会以公众号的方式通知您审核通过,然后我们点击提交发布,然后等待1~2个小程序部署的时间后,其他人就可以在微信中搜到你的小程序啦!

image-20211113122110928

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐