• 微信小程序开发10 订阅消息:提高小程序用户留存


    你好,我是俊鹏,今天这一讲,我来带你学习怎么使用微信小程序的订阅消息功能。

    你能发现,我在标题里强调了订阅消息能提高小程序的留存率,为什么强调这一点呢? 站在小程序的角度上,订阅消息最普遍的应用场景有两种,一种是被动的,一种是主动的。

    被动的订阅消息指: 消息的发送需要用户的某些行为作为触发,然后小程序再针对性地向用户发送消息反馈。以电商类小程序为例,当用户支付成功之后,小程序会向用户推送一条消息,用户可以通过这条消息跟踪订单进度。

    主动的订阅消息是指: 小程序可以定期或不定期地向用户发送消息通知,不需要用户的某些行为作为触发点。比如学习类小程序在每天的固定时间向用户发送消息,通知用户打卡。

    在被动场景下,小程序会对用户的行为及时反馈,从而提升用户的产品体验,以及对小程序的认可度、依赖度。在主动场景下,小程序可以通过定期或不定期地发送通知,把离开的用户拉回来继续使用小程序。所以,这两种场景下的订阅消息都能帮小程序提高用户的留存率,这就是我们学习订阅消息的目的。

    我希望学完今天的内容,你能把订阅消息应用到工作中,让自己的小程序获得更多稳定的用户。

    现在,我们已经明确了学习的目的,接下来就一起学习怎么使用小程序的订阅消息功能。订阅消息属于微信提供的开放接口之一,所以跟 09 讲调用微信开放接口一样,我会介绍两种方法:

    • 传统的调用方式,掌握这种方式后,你会明白订阅消息完整链路的基本原理;

    • 云调用方式,教你用免鉴权的方式快速实现订阅消息的调用,提高研发效率。

    这两种方式有一些内容是相同的,包括消息模板配置和获取用户授权,所以我先带你学习这些内容,然后再掌握它们的差异点,也就是发送消息的逻辑。

    订阅消息完整的流程可以简单概况成下面这张图:

    Drawing 0.png

    1. 在发送消息之前必须获得用户授权,这是第一步也最基本的一步。

    2. 用户授权之后,你需要把授权信息记录下来,因为小程序的订阅消息能定义多种类型,每种都需要得到用户的授权后才可以发送。

    3. 消息的发送需要一个触发点,根据订阅消息的两种应用场景,触发点可以是用户的某个行为比如支付订单,也可以是小程序主动触发,比如定时发送。

    这三个步骤是小程序订阅消息的基本流程,我讲这个流程是希望你能先对整体的轮廓有所了解,然后再深入完整架构和实现细节。在这个流程中,有一个没有覆盖的必要步骤:先配置订阅消息的模板。

    配置消息模板

    登录微信公众平台之后,消息模板的配置入口在小程序管理后台中的“功能”-“订阅消息”里,你会看到下面这张图展示的内容:

    Drawing 2.png

    点击“添加”就可以配置消息模板了,微信官方提供了一些常用的模板类型,你可以自行选择:

    Drawing 3.png

    你可以看到,上图中有一个“一次性订阅”的提醒,说明这些模板库提供的都是一次性订阅消息。而小程序的订阅消息分为:一次性订阅消息和长期订阅消息。

    • 一次性你可以简单理解成得到用户的授权之后只能发送一条消息,如果再有发送需求还要再次申请用户的授权。

    • 长期订阅消息可以得到用户授权后发送多条消息。不过目前长期订阅消息只向政务民生、医疗、交通、金融、教育等线下公共服务开放(后面也许会开放出更多领域)。

    所以,我们今天的学习是围绕一次性订阅消息展开的。

    按照上述流程配置完消息模板后,你会拿到一个模板 ID,请记住这个名词,后续所有步骤都会用到它,而第一步就是用模板 ID 获取用户的授权。

    获取用户授权

    小程序SDK提供了一个用来获取用户授权的API:wx.requestSubscribeMessage。你要用这个 API 为每一个模板 ID 授权,调用方式如下:

    wx.requestSubscribeMessage({
      tmplIds: ['tmplId_1','tmplId_2','tmplId_3'],
      success (res) {
        console.log(res)
        / **
        {
          errMsg: 'requestSubscribeMessage:ok'tmplId_1: 'accept',
          tmplId_2: 'reject',
          tmplId_3: 'ban' 
        }
        */
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    其中参数 tmplIds 是一个数组,数组中的每个元素就是前面配置的消息模板 ID ,如果你需要一次性获取多个模板的用户授权就在 tmplIds 数组里写入多个元素。调用wx.requestSubscribeMessage 之后,用户会在小程序端收到一个弹窗:

    Drawing 4.png

    用户可以对列表中的每个消息模板分别授权,也可以全部勾选(也就是全部授权)。用户点击“允许”按钮之后,将会触发 wx.requestSubscribeMessage 的 success 回调函数,返回的数据结构可以参考上面的示例代码,每个模板 ID 都有一个状态标识,accept 代表得到授权、reject 代表未获得授权、ban 代表被后台封禁。当然,正常情况下是不会出现 ban 状态的,除非你的订阅消息涉嫌骚扰用户或内容违禁。

    既然学习的是一次性订阅消息,那么你要在每次得到用户授权后都要把信息记录下来。但不能把这些信息保存在前端,而是同步到服务端保存(因为发送消息是在服务端执行的),那怎么把这些信息与用户一一对应起来呢?

    还记得我们在 02 讲学习的小程序登录功能吗?登录成功后服务端会拿到用户的 openId,然后用openId 创建自定义登录态返回给小程序的前端,后续小程序的每次请求都需要携带这个自定义登录态,服务端在接收到请求后可以从自定义登录态反解析出用户的 openId,这样就可以将请求的数据与用户一一对应起来了。整个过程如图所示:

    Drawing 6.png

    这样我们就获取到了用户的授权,接下来就是向用户发送一条订阅消息,我们先学习传统的调用方式(这里会用到 09 讲中提到的使用缓存管理 access_token 的知识)。

    发送消息的传统调用方式

    学完 09 讲后我们知道了,调用微信开放接口需要临时凭证 access_token,发送订阅消息的接口作为微信开放接口的一员同样要遵循这条规则。

    服务端需要调用微信提供的subscribeMessage.send接口向用户发送订阅消息,这个接口有几个请求参数需要你特别关注:

    Drawing 8.png

    其中 template_id 是上一步得到用户授权的几个模板 ID 中的一个,请注意,你每次调用 subscribeMessage.send 接口只能发送一条消息给用户,而不能同时发送多条,这是微信的限制。touser 参数的值是接收消息的用户 openId,access_token 是临时凭证。

    而且我刚刚提到了,发送消息需要一个触发点(可以是用户的某个行为触发,也可以是服务端的定时任务,具体采用哪种方式需要根据业务场景来决定),我会以定时任务这种场景为例,对照下图讲解一下完整的发送消息流程:

    Drawing 9.png

    1. 首先,服务器需要设定一个定时任务,比如每天 9 点触发一次,发送消息。

    2. 触发之后,第一步先检查本地缓存中是否存在有效期之内的 access_token ,如果不存在就需要重新请求微信的服务获取一个新的。

    3. 拿到有效的 access_token 之后,第二步是通过 subscribeMessage.send 接口向微信服务器发送一个请求。

    4. 微信服务器收到请求之后,发送一条订阅消息给指定的 openId 对应的用户,便完成了整个发送消息的流程。

    以上就是发送订阅消息的传统调用方式。你也看到了,在这套流程中,需要比较繁重的服务端开发工作,比如设定定时任务、缓存管理等,如果你具备这些知识和能力的话当然是件很容易完成的事,但对于大多数小程序前端开发者来说,相对欠缺这部分知识,所以接下来我们就来学习效率更高的云调用方式。

    发送消息的云调用方式

    09 讲中我已经讲了云调用是什么,就不再重复了,如果你忘了记得复习一下。微信小程序的订阅消息功能提供了云调用的支持,你可以不用关注鉴权,也就是不需要关注 access_token,直接在云函数里调用微信 SDK 提供的openapi.subscribeMessage.sendAPI 就行,参数跟 subscribeMessage.send 接口一样,只是不再需要 access_token 了。

    const cloud = require('wx-server-sdk')
    cloud.init()
    exports.main = async (event, context) => {
      try {
        const result = await cloud.openapi.subscribeMessage.send({
            touser: 'OPENID',
            templateId: 'TEMPLATE_ID',
            // ...其他参数
          })
        return result
      } catch (err) {
        return err
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    除了避免管理 access_token 的麻烦以外,在前面传统调用方式中还用到了一项能力:定时任务。这项需求云函数同样可以满足,你可以给云函数配置一个定时触发器,使用简单的 JSON 配置文件就可以设定一个定时任务。比如我刚刚说的每天 9 点触发一次就可以写成下面这种格式的云函数定时触发器:

    {
      // triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
      "triggers": [
        {
          // name: 触发器的名字,规则见下方说明
          "name": "myTrigger",
          // type: 触发器类型,目前仅支持 timer (即 定时触发器)
          "type": "timer",
          // config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见下方说明
          "config": "0 0 9/23 1/1 * * *"
        }
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    通过上面两个步骤就完成了跟传统调用方式一样的功能,而整个发送消息的流程也得到了简化:

    Drawing 11.png

    对比这两种调用方式,你应该能够明显感受到不同。当然,这并不是说我们只学会用云调用而不用学习传统方式了。云调用的底层也是类似传统的调用方式,只不过帮你完成了定时任务、缓存管理这些服务端的开发工作,而且我们通过学习传统的调用方式能够明白底层的工作原理,也许有一天你能够成为一名架构师,那时候你就需要这些底层知识作为搭建复杂架构的基础了。

    总结

    今天这节课,我之所以带你了解小程序的订阅消息,是因为它能帮助小程序提高用户的留存率。通过这节课,我希望你能够将以下知识融会贯通:

    1. 小程序订阅消息的基本流程,包括模板配置、用户授权和发送消息;

    2. 知晓发送消息的传统调用方式,并且能够充分理解每一个步骤的作用;

    3. 学会并能够使用通过云调用实现订阅消息的发送,并且能够体会到云调用带来的效率提升。

    如果你能够完成上面三点要求,那么今天就交给你一个需要动动手的课后作业:使用云调用完成小程序的订阅消息功能。我相信在成功完成这个作业之后,你能够对今天这节课的知识有更深的理解和记忆。


    精选评论

  • 相关阅读:
    【云原生】Kubernetes核心技术(上)
    openssl: issue-10463: 对/dev/random的依赖
    统计信号处理基础 习题解答6-5
    docker安装duplicati备份工具
    Keil实现Flash升级跳转(STM32/GD32/HC32)
    单片机——使用P3口流水点亮8位LED
    小小开发板承载万千创新可能,小熊派的云上奇遇记
    6-Mysql子查询,多表连接(内连接,外连接,交叉连接)
    JDK与cglib动态代理
    Linux内核(一)
  • 原文地址:https://blog.csdn.net/fegus/article/details/126688162