什么是小程序云开发,从前端解决后端的活。18 年以为很多大公司减员,小公司关闭,到底是互联网的寒冬时期,还是互联网换新时期,我认为是后者。小程序开发周期一般都相对比比较短,如果一个企业还是一个团队去开发一个博客、资讯、新闻这类的产品,支出的成本是还是相对大比较大的。其中可能大部分时间我们都用在联调这个事上了,代码的测试,数据校验,很大时间浪费在前后端相互对接等待等。像小程序这种无服务在以后会越来越多,只管写接口、写逻辑就好。总得来说,虽然你管的东西越来越少,但开发效率却越来越高,开发出来的轻应用、小程序却是具备高性能、高可用、高扩展的特性, 开发人员的较少必定资金和人力的需求可谓大大节省。
小程序云开发无疑是让前端开发者从前端开发到全栈开发,大前端目前是行业一个非常流行的趋势,这几年全栈的火爆主要有业务场景相对比较多,像之前那样前端只写静态页面,数据和模板都是通过后端来渲染,无法满足业务了,所以都出现前后端开始分离,前后两端是通过 API 的请求和返回去做一个数据交互,前端工程师比较贴近客户,所以他更能够去理解到一些业务逻辑,无论是业务的前台或是后台,交给前端工程师来做是更好的,所以现在很多公司选择使用 node.js ,做中台服务。企业的需要前端干的事越多,我们学习的东西就越多,掌握的技能就越多,如果在开发小程序的前端人员,在日常的闲暇时间了解下云开发,也是对自己后期做 node.js 做一个小小的铺垫,云开发无需自己在对环境的搭建,也能让我们不再额外购买服务器就能开发自己一个线上的作品。
云开发是微信团队和腾讯云团队共同研发的一套小程序基础能力,简言之就是:云能力将会成为小程序的基础能力。
小程序云开发目前提供三大基础能力支持:

小程序操作数据库可以在小程序端和服务端操作数据库,小程序本来相对于小,如果我们代码全部放在小程序端来操作,会占用不少的内存,另一方面就是直接在页面操作数据库安全性也无法得到保障,因此我们在对数据的操作就需要通过服务端操作,目前服务端要操作小程序我们就得通过云函数,云函数作为服务层对数据交互,在一定的层面保证了数据的安全性。
已经申请小程序,获取小程序 AppID 在小程序管理后台中, 设置的开发设置 下可以获取微信小程序 AppID 。

进入云开发控制面板入口

进入控制面板,在左上角就是小程序云开发目前提供三大基础能力。

  将所有没有带云开发标识的文件夹创建并部署,带有云开发标识的才能调用服务器。

如果你之前没有玩过云开发,在这里上传云函数之后,就可以试着跟着官方的 demo 操作下,官方的数据库跟我们的 mongodb 数据库类似,把 demo 提供的功能练习下:
官方文档: https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/getting-started.html

如果想要项目重命名,文件夹和 project.config.json 对应即可,如果无法直接修改就需要关闭项目修改了在打开项目。

这里我们已经不需要官方的模板,将其官方给的图片和模板删除。

(1)封装组件,components/title-bar
<!-- 自定义组件,添加插槽,插槽的name一定要设置,使用时name要相同-->
<!-- components/title-bar/index.wxml -->
<view class='container'>
  <view class="left-container">
    <view class='line'></view>
    <text class='text'>{{title}}</text>
  </view>
  <view class="right-container">
    <!-- slot 插槽 -->
    <slot name="more"></slot>
  </view>
</view>(2)引入组件
{
  "usingComponents": {
    "title-comp": "/components/title-bar/index"
  }
}(3)使用组件
  <!-- 卡槽 -->
  <title-comp title="没有插槽"></title-comp>
  <title-comp title="使用插槽">
    <!--里面的<text>标签就是传递给插槽的 text 中 slot 的值需跟组件的 name 保持一致-->
    <text class="more" slot="more">更多</text>
  </title-comp>(4)运行效果

新建 behavior.js(名字可自定义) 文件中有共享的 properties ,data ,methods 等。在组件中引入并继承即可。
示例代码如下:
(1)新建 behavior.js 文件中有共享的 properties ,data,methods 等
module.exports = Behavior({
    <!--共享属性-->
    properties: {
      product: { // 属性名
        type: Object,
        // value: '', // 属性初始值(可选),如果未指定则会根据类型选择一个
        observer: function (newValue, oldValue,path) {
          console.log(newValue)
          console.log(oldValue)
          console.log(path)
        }
      }
    },
    //共享数据
    data:{
    },
   // 共享方法
    methods: {
      // 商品详情
      productDetails: function () {
        //  triggerEvent  组件间通信与事件 
        this.triggerEvent("productDetails", {
          id: this.data.product.id
        }, {})
      }
      
    }
  })组件间通信与事件:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html
(2)封装组件,components/product-column
<!-- components/product-column/index.wxml -->
<view class='container'>
    <view class="left-container">
        <view class='line'></view>
        <text class='text' bind:tap="productDetails">{{product.name}}</text>
    </view>
    <view class="right-container">
        <text class="text">价格:¥{{product.price}}</text>
    </view>
</view>// components/product-column/index.js
<!--引入behavior.js-->
let productBev = require("../behaviors/product.js")
Component({
 <!--继承 behavior.js里面的 properties,data,methods-->
  behaviors: [productBev],
  /**
   * 组件的属性列表
   */
  properties: {
  },
  /**
   * 组件的初始数据
   */
  data: {
  },
  /**
   * 组件的方法列表
   */
  methods: {
  }
})(3)封装组件,components/product-row
<!-- components/product-row/index.wxml -->
<view class='container'>
    <view class="left-container">
        <view class='line'></view>
        <text class='text' bind:tap="productDetails">{{product.name}}</text>
    </view>
    <view class="right-container">
        <text class="text">数量:{{product.num}}</text>
    </view>
</view>// components/product-row/index.js
<!--引入behavior.js-->
let productBev = require("../behaviors/product.js")
Component({
 <!--继承 behavior.js里面的 properties,data,methods-->
  behaviors: [productBev],
  /**
   * 组件的属性列表
   */
  properties: {
  },
  /**
   * 组件的初始数据
   */
  data: {
  },
  /**
   * 组件的方法列表
   */
  methods: {
  }
})(4)使用组件
{
  "usingComponents": {
    "product-column-comp": "/components/product-column/index",
    "product-row-comp": "/components/product-row/index",
  }
}<!-- behaviors -->
<product-column-comp product="{{product}}"></product-column-comp>
<product-row-comp product="{{product}}" bind:productDetails="productDetails"></product-row-comp>(4)运行效果

<!--components/authorize-button/index.wxml-->
<button bind:getuserinfo="onGetUserInfo" bind:tap="address" open-type="{{openType}}"  plain="{{true}}" class="container" lang="zh_CN">
  <slot name="share"></slot>
  <slot name="address"></slot>
  <slot name="info"></slot>
</button>// components/authorize-button/index.js
Component({
  /**
   * 组件的属性列表
   */
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多slot支持
  },
  properties: {
    // 授权开放能力 通过传递实现
    openType: {
      type: String
    }
  },
  /**
   * 组件的初始数据
   */
  data: {
   
  },
  /**
   * 组件的方法列表
   */
  methods: {
    // 用户信息获取方法
    onGetUserInfo(event) {   
      if (this.data.openType == "getUserInfo"){   
        console.log("-----------用户信息获取-----------")          
        console.log(event.detail)
      }           
    }, 
    // 地址信息的获取
    address(event){    
      if (this.data.openType == "primary") {
        console.log("-----------地址信息获取-----------")          
        this._addressInfo(event) 
      }      
    },
    _addressInfo(event){      
      wx.chooseAddress({
        success: (res) => { },
        fail:(err) =>{}, 
        complete:(e)=> {        
           console.log(e) // 用户授权和未授权都会执行 
        }
      })
    }
  }
})(2)使用组件
{
  "usingComponents": {
    "authorize-comp": "/components/authorize-button/index"
  }
}<!-- 授权 -->
  <view class="authorize-item">
    <authorize-comp class="authorize" open-type="getUserInfo">
      <text slot="info">用户信息</text>
    </authorize-comp>
    <authorize-comp class="authorize" open-type="primary">
      <text slot="address">获取地址</text>
    </authorize-comp>
    <authorize-comp class="authorize" open-type="share">
      <text slot="share">分享</text>
    </authorize-comp>
  </view>(3)运行效果

// components/class/index.js
Component({
  // 定义外部样式类
  externalClasses: ['box'],
  /**
   * 组件的属性列表
   */
  properties: {
  },
  /**
   * 组件的初始数据
   */
  data: {
  },
  /**
   * 组件的方法列表
   */
  methods: {
  }
})<!--components/class/index.wxml-->
<!--box 样式通过外部传递-->
<view class="container box"></view>(2)使用组件
{
  "usingComponents": {
    "class-comp": "/components/class/index"
  }
}<!-- 自定义组件样式  -->
<view class="class-item">
    <class-comp box="box1"></class-comp>
    <class-comp box="box2"></class-comp>
    <class-comp box="box3"></class-comp>
    <class-comp box="box4"></class-comp>
</view>.box1 {
  background-color: #ff3f34;
}
.box2 {
  background-color: #fbbf00;
}
.box3 {
  background-color: #44ac45;
}
.box4 {
  background-color: #2f84d5;
}(3)运行效果

WXS(WeiXin Script)是小程序的一套脚本语言,实现 wxml 数据的过滤。在使用的过程中需要需要结合官方文档,wxs 目前只是拥有部分 JavaScript 功能。官方给出的注意如下:
常用的使用情况有两种引入后缀 wxs 和 直接在 wxml 文件中使用。
示例代码如下:
(1)新建 wxs 文件,common/wxs/filter.wxs
var filters = {
    // 定义方法
    toFix: function (value) {
        // 防止 wxs 渲染数据没有定义 
        if (value == 0 || !value) {
            return 0
        }
        // toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。
        return parseFloat(value).toFixed(2)
    }
     // 多个方法使用逗号分隔
    ,
    substr: function (value, length) {
        if (!value) {
            return ''
        }
        // 对数据进行长度限制
        return value.substring(0, length) + "..."
    }
}
// 导出方法
module.exports = {
    toFix: filters.toFix,
    substr: filters.substr
}(2)引入使用 wxs
<!--引入 wxs 并指定标签的模块名-->
<wxs module="util" src="../../common/wxs/filter.wxs"></wxs>
    <view class="wxs-container">
    <view class="wxs-item">数字截取前:{{8.02231}}</view>
    <view class="wxs-item">数字截取后:{{util.toFix(8.02231)}}</view>
    <view class="wxs-item">订单状态页面直接引用:{{wUtil.orderStatus(1)}}</view>
</view>module 属性 module 属性是当前 <wxs> 标签的模块名。在单个 wxml 文件内,建议其值唯一。有重复模块名则按照先后顺序覆盖(后者覆盖前者)。不同文件之间的 wxs 模块名不会相互覆盖。
module 属性值的命名必须符合下面两个规则:
首字符必须是:字母(a-zA-Z),下划线(_)
剩余字符可以是:字母(a-zA-Z),下划线(_), 数字(0-9)
(3)页面直接使用 wxs
<view class="wxs-item">文字截取后:{{util.substr("这是一段超级长的文字需要截取",6)}}</view>
<view class="wxs-item">订单状态页面直接引用:{{wUtil.orderStatus(1)}}</view>
<!--当前页面直接使用 wxs, 通过 wxs 标签指定模块名-->
<wxs module="wUtil">
  var orderStatus = function(num) {
    var value = ""
    switch (num) {
      case 0:
        value = "未支付"
        break
      case 1:
        value = "已支付"
        break
      case 2:
        value = "已发货"
        break
      case 3:
        value = "已支付,但库存不足"
        break
      default:
        value = "未知状态"
    }
    return value
  }
 <!--导出模块-->
  module.exports = {
    orderStatus: orderStatus
  }
</wxs>(4)运行效果

但是无数个得云函数,我们管理又有问题了,如果想要一个函数处理多个任务 ,目前解决这一问题就是 tcb-router 。
tcb-router 小程序云函数高级玩法,在掘金开发者大会上,提到得一种云函数的用法,我们可以将相同的一些操作,比如用户管理、支付逻辑,按照业务的相似性,归类到一个云函数里,这样比较方便管理、排查问题以及逻辑的共享。甚至如果你的小程序的后台逻辑不复杂,请求量不是特别大,完全可以在云函数里面做一个单一的微服务,根据路由来处理任务。
下面三幅图可以看出:

比如这里就是传统的云函数用法,一个云函数处理一个任务,高度解耦。

第二幅架构图就是尝试将请求归类,一个云函数处理某一类的请求,比如有专门负责处理用户的,或者专门处理支付的云函数。

最后一幅图显示这里只有一个云函数,云函数里有一个分派任务的路由管理,将不同的任务分配给不同的本地函数处理。
想实现成最后一幅图,我们的使用咱们腾讯云 Tencent Cloud Base 团队开发了 tcb-router,云函数路由管理库。大家点击 tcb-router 进去看看,如果之前做过 node + koa 的同学应该很熟悉,后面我们也会有一个小得案例进一步讲解,官方例子如下:
// 云函数的 index.js
const TcbRouter = require('./router');
exports.main = (event, context) => {
    const app = new TcbRouter({ event });
  
    // app.use 表示该中间件会适用于所有的路由
    app.use(async (ctx, next) => {
        ctx.data = {};
        await next(); // 执行下一中间件
    });
    // 路由为数组表示,该中间件适用于 user 和 timer 两个路由
    app.router(['user', 'timer'], async (ctx, next) => {
        ctx.data.company = 'Tencent';
        await next(); // 执行下一中间件
    });
    // 路由为字符串,该中间件只适用于 user 路由
    app.router('user', async (ctx, next) => {
        ctx.data.name = 'heyli';
        await next(); // 执行下一中间件
    }, async (ctx, next) => {
        ctx.data.sex = 'male';
        await next(); // 执行下一中间件
    }, async (ctx) => {
        ctx.data.city = 'Foshan';
        // ctx.body 返回数据到小程序端
        ctx.body = { code: 0, data: ctx.data};
    });
    // 路由为字符串,该中间件只适用于 timer 路由
    app.router('timer', async (ctx, next) => {
        ctx.data.name = 'flytam';
        await next(); // 执行下一中间件
    }, async (ctx, next) => {
        ctx.data.sex = await new Promise(resolve => {
        // 等待500ms,再执行下一中间件
        setTimeout(() => {
            resolve('male');
        }, 500);
        });
        await next(); // 执行下一中间件
    }, async (ctx)=>  {
        ctx.data.city = 'Taishan';
        // ctx.body 返回数据到小程序端
        ctx.body = { code: 0, data: ctx.data };
    });
    return app.serve();
}tips: 小程序云函数的 node 环境默认支持 async/await 语法
小程序端
// 调用名为 router 的云函数,路由名为 user
wx.cloud.callFunction({
    // 要调用的云函数名称
    name: "router",
    // 传递给云函数的参数
    data: {
        $url: "user", // 要调用的路由的路径,传入准确路径或者通配符*
        other: "xxx"
    }
});(1)新建云函数 rubbish

(2)新建集合 rubbish

添加数据
{"_id":"19b1e8b9-2662-471e-9a2d-dfda08b8b50d","goodsType":"湿垃圾","goodsName":"猕猴桃"}
{"_id":"72eb8a02-53a6-450a-9588-37a4408a3ff3","goodsType":"湿垃圾","goodsName":"苹果"}(3)新建文件 utils/rubbish/ReturnUtil.js 用于对返回结果的处理
/**
 * 成功调用
 * @param {*} ctx
 * @retuen 
 */
const success = ctx => {
  return {
    code: 0,
    message: 'success',
    data: ctx.data
  }
}
/**
 * 调用失败 
 * @param {*} ctx
 * @param {*} msg
 * @retuen 
 */
const error = (ctx, msg) => {
  return {
    code: 400,
    message: msg,
    data: ctx.data
  }
}
module.exports = {
  success,
  error
}(4)加入 tcb-router 依赖
npm install --save tcb-router(5)rubbish/index.js
// 云函数入口文件
const cloud = require('wx-server-sdk')
// 引入 tcb-router
const TcbRouter = require('tcb-router')
const returnUtil = require('utils/ReturnUtil.js')
// 初始化
cloud.init({
  env: 'test-123'
})
// 获取数据库
const db = cloud.database();
// 操作 rubbish 集合公共部分
const rubbish_db = db.collection('rubbish');
// 云函数入口函数
exports.main = async (event, context) => {
  const app = new TcbRouter({ event });
  // app.use 表示该中间件会适用于所有的路由
  app.use(async (ctx, next) => {
    ctx.data = {};
    await next(); // 执行下一中间件
  });
  /***************************    垃圾分类   *****************************************/
  // 获取单个垃圾分类信息
  app.router('getRubbish', async (ctx, next) => {
    console.log(event)
    let data = JSON.parse(event.data)
    ctx.data = await rubbish_db.where({ goodsName:data.name }).get();
    ctx.body = await returnUtil.success(ctx)
    await next();
  })
  return app.serve();
}(1)打开云开发控制台 -> 云函数 ,点击 上面新建的 rubbish 的云端测试

(2)测试云函数
{
  // 路由  
  "$url": "getRubbish",
   // 传递参数
  "data":"{\"name\":\"苹果\"}"
}

(1)打开本地调式

(2)点击 rubbish ,勾上开启本地调式 查看依赖是否安装

(3)安装依赖
如有依赖没有安装,在(1)步的界面中,点击在终端中打开 执行 npm install 。
(4)编写请求参数
{
  // 路由  
  "$url": "getRubbish",
   // 传递参数
  "data":"{\"name\":\"苹果\"}"
}(5)调用云函数

网络请求的封装在小程中也能让代码更好修改,配置,维护。这里将进行网络请求和云开发请求的封装。
(1)新建 config.js
// 配置请求公用参数
const config = {
  // 网络请求
  api_http_url: 'https://www.mxnzp.com/api',
  // 云开发云函数
  api_cloud_route:"rubbish"
}
// 导出配置文件
export { config }(2)封装网络请求 utils/http-request.js
import { config } from "../config.js"
class HTTP {
  constructor() {
    // 通过构造方法 实现 url 的赋值
    this.api_http_url = config.api_http_url
  }
  //http请求
  request(params) {
    
    wx.request({
      url: this.api_http_url + params.url, // 接口地址拼装
      method: params.method,              // 接口类型 get/post/put/delete
      data: params.data,    // 传递给后台的参数
      header: {
        // 设置请求类型  
        'content-type': 'application/x-www-form-urlencoded'
      },
      success: (res) => {
        let code = res.data.code.toString()
        if (code.startsWith('1')) {
         // 将请求的参数回调给上层
          params.success(res)
        }
      },
      fail: (error) => {
        console.log(error)
      },
      complete: (res) => {
        
      }
    })
  }
}
export {
  HTTP
}(3)封装云开发请求 utils/cloud-request.js
// 导入配置文件
import { config } from "../config.js"
class CloudRequest{
  constructor(){
    this.api_cloud_route = config.api_cloud_route
  }
  request(params){
    
    wx.cloud.callFunction({
      // 要调用的云函数名称
      name: this.api_cloud_route,
      // 传递给云函数的参数
      data: {
        // 要调用的路由的路径,传入准确路径或者通配符*
        $url: params.url, 
        // 请求参数
        data: JSON.stringify(params.data)
      },
      success: res => {
        // 结果回调  
        params.success(res)
      },
      fail: err => {
        console.log(err)
      }
    })
  }
}
export { CloudRequest }下面我们将通过案例来分别使用我们的封装的请求。
(1)自定义 model 层,models/IndexModel.js 类
// 导入封装的 http 请求
import { HTTP } from '../utils/http-request.js'
// 继承 HTTP 直接调用其方法
class IndexModel extends HTTP {
  
  /**
   * 查询垃圾分类信息
   * @param {*} callBack
   */
  getRubbish(name,callback){
    // http 请求
    this.request({
      url: "/rubbish/type", // 接口地址
      data: {
        name: name // 传递参数
      },
      // 接收  params.success(res) 回调参数
      success: res => {
        // 将数据再次回调
        callback(res) 
      }
    })
  }
}
export { IndexModel }在这里可能比较难得就是回调,回调。通过类进行请求的封装,在很大的使得代码更加简洁。model 层作为一个中间层来使用,无论是代码的复用和维护程度好了很多。
(2)实现 model 类
// 第一步:导入
import { IndexModel } from '../../models/indexModel.js'
// 第二步:实例化
let indexModel = new IndexModel()
/ 第三步:调用方法,参数和方法中一一对应 res 接收callback回调参数
indexModel.getRubbish("苹果",res=>{
	console.log(res)
	this.setData({
        httpItem:res.data.data.aim
     })
}) <group-comp title="网络请求"></group-comp>
  <view class="http-container ">
    <view class="http-item">{{httpItem.goodsName}} : {{httpItem.goodsType}}</view>
  </view>(3)运行效果

(1)自定义 model 层,models/IndexCloudModel.js 类,其方法类似于 http
// 导入封装的 http 请求
import { CloudRequest } from '../utils/cloud-request.js'
// 继承 CloudRequest 直接调用其方法
class IndexCloudModel extends CloudRequest {
  /**
   * 查询垃圾分类信息
   * @param {*} callBack 
   */
  getRubbish(name,callBack) {
    this.request({
      url: "getRubbish",
      data:{name:name},
      success: res => {
        callBack(res)
      }
    })
  }
  
}
export { IndexCloudModel }(2)实现 model 类
// 第一步:导入
import { IndexCloudModel } from '../../models/indexCloudModel.js'
// 第二步:实例化
let indexCloudModel = new IndexCloudModel()
/ 第三步:调用方法,参数和方法中一一对应 res 接收callback回调参数
indexCloudModel.getRubbish("猕猴桃",res=>{
    console.log(res)
    this.setData({
        cloudItem: res.result.data.data[0]
    })
})	<group-comp title="云函数请求" ></group-comp>
  <view class="http-container ">
    <view class="http-item">{{cloudItem.goodsName}} : {{cloudItem.goodsType}}</view>
  </view>(3)运行效果

在这里就封装了两种请求,这里封装的代码在多人开发和后期维护节省了不少时间,大家日常在开发也可以直接使用。
如果云函数需要定时 / 定期执行,也就是定时触发,我们可以使用云函数定时触发器。配置了定时触发器的云函数,会在相应时间点被自动触发,函数的返回结果不会返回给调用方,详情进入官方网址,比如:两小时后取消订单、定点定时推送商品信息等。
右击 cloud 选择 新建 Node.js 云函数  命名为 triggers

云函数创建触发器,必须建一个 config.json 文件,因为只有 config.json 才会有上传触发器选项。


config.json 相比一般的 json 多出上传触发器和删除触发器 触发器也只有通过这样上传和删除后台才会执行。
config.json 解读
{
  // triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
  "triggers": [
    {
      // name: 触发器的名字,可以随意更改,规则见给出的官方链接
      "name": "myTrigger",
      // type: 触发器类型,目前仅支持 timer (即 定时触发器)
      "type": "timer",
      // config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见官方链接
      "config": "0 0 2 1 * * *"
    }
  ]
}示例
下面展示了一些 Cron 表达式和相关含义的示例:
*/5 * * * * * * 表示每5秒触发一次0 0 2 1 * * * 表示在每月的1日的凌晨2点触发0 15 10 * * MON-FRI * 表示在周一到周五每天上午10:15触发0 0 10,14,16 * * * * 表示在每天上午10点,下午2点,4点触发0 */30 9-17 * * * * 表示在每天上午9点到下午5点内每半小时触发0 0 12 * * WED * 表示在每个星期三中午12点触发接下来我们编写每十秒的触发器,进入 config.json
{
  "triggers": [
    {
      "name": "trigger",
      "type": "timer",
      "config": "*/10 * * * * * *"
    }
  ]
}注意:在创建触发器去掉触发器的注释 有注释上传在解析 json 会报错
编写 index.json
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  console.log("每十秒触发器执行一次测试。。。")
  
}目前作为测试所以我们输出一句话测试下是否成功 因为云函数不会返回 所以 return 没有必要返回数据 。
编写云函数 triggers 下的 index.json 和 config.json ,点击 triggers 上传并部署:云端安装依赖,上传成功后,代有击 config.json 上传触发器,成功后我就在云开发后台去看下,选择云函数函数名为 triggers  我们在日志控制面看可以看出输出时间是不是我们的触发时间,云函数的输出内容,如下图:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。