【云+社区年度征文】小程序·云开发综合提升篇

代码地址 : https://gitee.com/mtcarpenter/wechat-cloud.git

1 聊下云开发

1.1 从企业的角度

什么是小程序云开发,从前端解决后端的活。18 年以为很多大公司减员,小公司关闭,到底是互联网的寒冬时期,还是互联网换新时期,我认为是后者。小程序开发周期一般都相对比比较短,如果一个企业还是一个团队去开发一个博客、资讯、新闻这类的产品,支出的成本是还是相对大比较大的。其中可能大部分时间我们都用在联调这个事上了,代码的测试,数据校验,很大时间浪费在前后端相互对接等待等。像小程序这种无服务在以后会越来越多,只管写接口、写逻辑就好。总得来说,虽然你管的东西越来越少,但开发效率却越来越高,开发出来的轻应用、小程序却是具备高性能、高可用、高扩展的特性, 开发人员的较少必定资金和人力的需求可谓大大节省。

1.2 从开发者的角度

小程序云开发无疑是让前端开发者从前端开发到全栈开发,大前端目前是行业一个非常流行的趋势,这几年全栈的火爆主要有业务场景相对比较多,像之前那样前端只写静态页面,数据和模板都是通过后端来渲染,无法满足业务了,所以都出现前后端开始分离,前后两端是通过 API 的请求和返回去做一个数据交互,前端工程师比较贴近客户,所以他更能够去理解到一些业务逻辑,无论是业务的前台或是后台,交给前端工程师来做是更好的,所以现在很多公司选择使用 node.js ,做中台服务。企业的需要前端干的事越多,我们学习的东西就越多,掌握的技能就越多,如果在开发小程序的前端人员,在日常的闲暇时间了解下云开发,也是对自己后期做 node.js 做一个小小的铺垫,云开发无需自己在对环境的搭建,也能让我们不再额外购买服务器就能开发自己一个线上的作品。

1.3 什么是小程序云开发?

云开发是微信团队和腾讯云团队共同研发的一套小程序基础能力,简言之就是:云能力将会成为小程序的基础能力。

小程序云开发目前提供三大基础能力支持:

  • 云函数:在云端运行的代码,微信私有协议天然鉴权,开发者只需编写业务逻辑代码
  • 数据库:一个既可在小程序前端操作,也能在云函数中读写的 JSON 数据库
  • 文件存储:在小程序前端直接上传/下载云端文件,在云开发控制台可视化管理

小程序操作数据库可以在小程序端和服务端操作数据库,小程序本来相对于小,如果我们代码全部放在小程序端来操作,会占用不少的内存,另一方面就是直接在页面操作数据库安全性也无法得到保障,因此我们在对数据的操作就需要通过服务端操作,目前服务端要操作小程序我们就得通过云函数,云函数作为服务层对数据交互,在一定的层面保证了数据的安全性。

1.4 云开发项目搭建

1.4.1 准备工作

已经申请小程序,获取小程序 AppID 在小程序管理后台中, 设置的开发设置 下可以获取微信小程序 AppID 。

1.4.2 新建项目

进入云开发控制面板入口

1564795018136

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

  • 运营分析:资源使用是日常对云开发资源的使用的统计、用户访问是用户在小程序授权用户信息、监控图标是三大基础能力的请求统计。
  • 右上角设置:小程序云开发支持两个环境,在设置可以切换环境,数据库、存储、云函数根据环境显示当前的资源信息。

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

如果你之前没有玩过云开发,在这里上传云函数之后,就可以试着跟着官方的 demo 操作下,官方的数据库跟我们的 mongodb 数据库类似,把 demo 提供的功能练习下:

官方文档: https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/getting-started.html

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

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

2 灵活的自定义组件(插槽,behaviors,授权,自定义组件样式)

2.1 插槽(slot)

  • 使用场景 小程序中多处使用同一个自定义组件,组件中部分样式灵活显示、隐藏。
  • 使用方法 slot 标签定义在小程序自定义组件中,根据外部传入标签控制,动态的设置自定义组件的样式,在实际开发中提高了组件的复用性。
  • 示例代码如下:

(1)封装组件,components/title-bar

代码语言:txt
复制
<!-- 自定义组件,添加插槽,插槽的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)引入组件

代码语言:txt
复制
{
  "usingComponents": {
    "title-comp": "/components/title-bar/index"
  }
}

(3)使用组件

代码语言:txt
复制
  <!-- 卡槽 -->
  <title-comp title="没有插槽"></title-comp>
  <title-comp title="使用插槽">
    <!--里面的<text>标签就是传递给插槽的 text 中 slot 的值需跟组件的 name 保持一致-->
    <text class="more" slot="more">更多</text>
  </title-comp>

(4)运行效果

2.2 behaviors

  • 使用场景 behaviors 是用于组件间代码共享的特性,例如商品基本信息在多处展示,布局不同,且有属性、数据、生命周期函数和方法。
  • 使用方法

新建 behavior.js(名字可自定义) 文件中有共享的 properties ,data ,methods 等。在组件中引入并继承即可。

示例代码如下:

(1)新建 behavior.js 文件中有共享的 properties ,data,methods 等

代码语言:txt
复制
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

代码语言:txt
复制
<!-- 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>
代码语言:txt
复制
// 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

代码语言:txt
复制
<!-- 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>
代码语言:txt
复制
// 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)使用组件

代码语言:txt
复制
{
"usingComponents": {
"product-column-comp": "/components/product-column/index",
"product-row-comp": "/components/product-row/index",
}
}
代码语言:txt
复制
<!-- behaviors -->
<product-column-comp product="{{product}}"></product-column-comp>
<product-row-comp product="{{product}}" bind:productDetails="productDetails"></product-row-comp>

(4)运行效果

2.3 自定义授权组件

  • 使用场景
    解决微信开放能力 button 的复用性。
  • 使用方法
    通过动态的传递 open-type 在组件中实现授权。
  • 示例代码如下:
    (1)封装组件,components/authorize-button
代码语言:txt
复制
<!--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>
代码语言:txt
复制
// 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)使用组件

代码语言:txt
复制
{
"usingComponents": {
"authorize-comp": "/components/authorize-button/index"
}
}
代码语言:txt
复制
<!-- 授权 -->
<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)运行效果

2.4 自定义组件样式

  • 使用场景
    在复用组件的时候,且样式想要不同。
  • 使用方法
    使用外部样式类可以让组件使用指定的组件外样式类,如果希望组件外样式类能够完全影响组件内部,可以将组件构造器中的 options.addGlobalClass 字段置为 true 。
  • 示例代码如下:
    (1)封装组件,components/authorize-button
代码语言:txt
复制
// components/class/index.js
Component({
// 定义外部样式类
externalClasses: ['box'],
/**
  • 组件的属性列表
    */
    properties: {
  • },

    /**

    • 组件的初始数据
      */
      data: {

    },

    /**

    • 组件的方法列表
      */
      methods: {

    }
    })

    代码语言:txt
    复制
    <!--components/class/index.wxml-->
    <!--box 样式通过外部传递-->
    <view class="container box"></view>

    (2)使用组件

    代码语言:txt
    复制
    {
    "usingComponents": {
    "class-comp": "/components/class/index"
    }
    }
    代码语言:txt
    复制
    <!-- 自定义组件样式  -->
    <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>
    代码语言:txt
    复制
    .box1 {
    background-color: #ff3f34;
    }

    .box2 {
    background-color: #fbbf00;
    }

    .box3 {
    background-color: #44ac45;
    }

    .box4 {
    background-color: #2f84d5;
    }

    (3)运行效果

    3 wxs 的使用

    WXS(WeiXin Script)是小程序的一套脚本语言,实现 wxml 数据的过滤。在使用的过程中需要需要结合官方文档,wxs 目前只是拥有部分 JavaScript 功能。官方给出的注意如下:

    • WXS 不依赖于运行时的基础库版本,可以在所有版本的小程序中运行。
    • WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。
    • WXS 的运行环境和其他 JavaScript 代码是隔离的,WXS 中不能调用其他 JavaScript
    • 文件中定义的函数,也不能调用小程序提供的API。
    • WXS 函数不能作为组件的事件回调。
    • 由于运行环境的差异,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异。

    常用的使用情况有两种引入后缀 wxs 和 直接在 wxml 文件中使用。

    示例代码如下:

    (1)新建 wxs 文件,common/wxs/filter.wxs

    代码语言:txt
    复制
    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

    代码语言:txt
    复制
    <!--引入 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

    代码语言:txt
    复制
    <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)运行效果

    4 云上 tcb-router(koa 风格)

    但是无数个得云函数,我们管理又有问题了,如果想要一个函数处理多个任务 ,目前解决这一问题就是 tcb-router 。

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

    下面三幅图可以看出:

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

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

    最后一幅图显示这里只有一个云函数,云函数里有一个分派任务的路由管理,将不同的任务分配给不同的本地函数处理。

    想实现成最后一幅图,我们的使用咱们腾讯云 Tencent Cloud Base 团队开发了 tcb-router,云函数路由管理库。大家点击 tcb-router 进去看看,如果之前做过 node + koa 的同学应该很熟悉,后面我们也会有一个小得案例进一步讲解,官方例子如下:

    代码语言:txt
    复制
    // 云函数的 index.js
    const TcbRouter = require('./router');

    exports.main = (event, context) => {
    const app = new TcbRouter({ event });

    // app.use 表示该中间件会适用于所有的路由
    app.use(async (ctx, next) =&gt; {
        ctx.data = {};
        await next(); // 执行下一中间件
    });
    
    // 路由为数组表示,该中间件适用于 user 和 timer 两个路由
    app.router([&#39;user&#39;, &#39;timer&#39;], async (ctx, next) =&gt; {
        ctx.data.company = &#39;Tencent&#39;;
        await next(); // 执行下一中间件
    });
    
    // 路由为字符串,该中间件只适用于 user 路由
    app.router(&#39;user&#39;, async (ctx, next) =&gt; {
        ctx.data.name = &#39;heyli&#39;;
        await next(); // 执行下一中间件
    }, async (ctx, next) =&gt; {
        ctx.data.sex = &#39;male&#39;;
        await next(); // 执行下一中间件
    }, async (ctx) =&gt; {
        ctx.data.city = &#39;Foshan&#39;;
        // ctx.body 返回数据到小程序端
        ctx.body = { code: 0, data: ctx.data};
    });
    
    // 路由为字符串,该中间件只适用于 timer 路由
    app.router(&#39;timer&#39;, async (ctx, next) =&gt; {
        ctx.data.name = &#39;flytam&#39;;
        await next(); // 执行下一中间件
    }, async (ctx, next) =&gt; {
        ctx.data.sex = await new Promise(resolve =&gt; {
        // 等待500ms,再执行下一中间件
        setTimeout(() =&gt; {
            resolve(&#39;male&#39;);
        }, 500);
        });
        await next(); // 执行下一中间件
    }, async (ctx)=&gt;  {
        ctx.data.city = &#39;Taishan&#39;;
    
        // ctx.body 返回数据到小程序端
        ctx.body = { code: 0, data: ctx.data };
    });
    
    return app.serve();
    

    }

    tips: 小程序云函数的 node 环境默认支持 async/await 语法

    小程序端

    代码语言:txt
    复制
    // 调用名为 router 的云函数,路由名为 user
    wx.cloud.callFunction({
    // 要调用的云函数名称
    name: "router",
    // 传递给云函数的参数
    data: {
    $url: "user", // 要调用的路由的路径,传入准确路径或者通配符*
    other: "xxx"
    }
    });

    5 简单实现云开发服务端

    (1)新建云函数 rubbish

    (2)新建集合 rubbish

    添加数据

    代码语言:txt
    复制
    {"_id":"19b1e8b9-2662-471e-9a2d-dfda08b8b50d","goodsType":"湿垃圾","goodsName":"猕猴桃"}
    {"_id":"72eb8a02-53a6-450a-9588-37a4408a3ff3","goodsType":"湿垃圾","goodsName":"苹果"}

    (3)新建文件 utils/rubbish/ReturnUtil.js 用于对返回结果的处理

    代码语言:txt
    复制
    /**

    • 成功调用
    • @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 依赖

    代码语言:txt
    复制
    npm install --save tcb-router

    (5)rubbish/index.js

    代码语言:txt
    复制
    // 云函数入口文件
    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();
    }

    6 本地和服务器测试

    6.1服务器测试

    (1)打开云开发控制台 -> 云函数 ,点击 上面新建的 rubbish 的云端测试

    (2)测试云函数

    代码语言:txt
    复制
    {
    // 路由
    "$url": "getRubbish",
    // 传递参数
    "data":"{&#34;name&#34;:&#34;苹果&#34;}"
    }

    6.2 本地调试

    (1)打开本地调式

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

    (3)安装依赖

    如有依赖没有安装,在(1)步的界面中,点击在终端中打开 执行 npm install

    (4)编写请求参数

    代码语言:txt
    复制
    {
    // 路由
    "$url": "getRubbish",
    // 传递参数
    "data":"{&#34;name&#34;:&#34;苹果&#34;}"
    }

    (5)调用云函数

    7 Http 请求封装

    网络请求的封装在小程中也能让代码更好修改,配置,维护。这里将进行网络请求和云开发请求的封装。

    (1)新建 config.js

    代码语言:txt
    复制
    // 配置请求公用参数
    const config = {
    // 网络请求
    api_http_url: 'https://www.mxnzp.com/api',
    // 云开发云函数
    api_cloud_route:"rubbish"
    }
    // 导出配置文件
    export { config }

    (2)封装网络请求 utils/http-request.js

    代码语言:txt
    复制
    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: {
        // 设置请求类型  
        &#39;content-type&#39;: &#39;application/x-www-form-urlencoded&#39;
      },
      success: (res) =&gt; {
        let code = res.data.code.toString()
        if (code.startsWith(&#39;1&#39;)) {
         // 将请求的参数回调给上层
          params.success(res)
        }
    
      },
      fail: (error) =&gt; {
        console.log(error)
      },
      complete: (res) =&gt; {
        
      }
    
    })
    

    }

    }

    export {
    HTTP
    }

    (3)封装云开发请求 utils/cloud-request.js

    代码语言:txt
    复制
    // 导入配置文件
    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 =&gt; {
        // 结果回调  
        params.success(res)
      },
      fail: err =&gt; {
        console.log(err)
      }
    })
    

    }

    }
    export { CloudRequest }

    下面我们将通过案例来分别使用我们的封装的请求。

    8 客户端、服务端通信

    8.1 网络请求

    (1)自定义 model 层,models/IndexModel.js 类

    代码语言:txt
    复制
    // 导入封装的 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 类

    代码语言:txt
    复制
    // 第一步:导入
    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
    })
    })
    代码语言:txt
    复制
     <group-comp title="网络请求"></group-comp>
    <view class="http-container ">
    <view class="http-item">{{httpItem.goodsName}} : {{httpItem.goodsType}}</view>
    </view>

    (3)运行效果

    8.2 云开发请求

    (1)自定义 model 层,models/IndexCloudModel.js 类,其方法类似于 http

    代码语言:txt
    复制
    // 导入封装的 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 类

    代码语言:txt
    复制
    // 第一步:导入
    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]
    })
    })
    代码语言:txt
    复制
    <group-comp title="云函数请求" ></group-comp>
    <view class="http-container ">
    <view class="http-item">{{cloudItem.goodsName}} : {{cloudItem.goodsType}}</view>
    </view>

    (3)运行效果

    在这里就封装了两种请求,这里封装的代码在多人开发和后期维护节省了不少时间,大家日常在开发也可以直接使用。

    9 函数实现定时器

    如果云函数需要定时 / 定期执行,也就是定时触发,我们可以使用云函数定时触发器。配置了定时触发器的云函数,会在相应时间点被自动触发,函数的返回结果不会返回给调用方,详情进入官方网址,比如:两小时后取消订单、定点定时推送商品信息等。

    右击 cloud 选择 新建 Node.js 云函数 命名为 triggers

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

    • 一般 json 文件
    • config.json

    config.json 相比一般的 json 多出上传触发器和删除触发器 触发器也只有通过这样上传和删除后台才会执行。

    config.json 解读

    代码语言:txt
    复制
    {
    // 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

    代码语言:txt
    复制
    {
    "triggers": [
    {
    "name": "trigger",
    "type": "timer",
    "config": "
    /10 * * * * * *"
    }
    ]
    }

    注意:在创建触发器去掉触发器的注释 有注释上传在解析 json 会报错

    编写 index.json

    代码语言:txt
    复制
    // 云函数入口文件
    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 我们在日志控制面看可以看出输出时间是不是我们的触发时间,云函数的输出内容,如下图: