Skip to content

12渐进增强:小程序的更新策略

今天是模块三的最后一讲,我会从"更新"的角度出发,带你学习怎么搭建一套渐进增强的小程序版本更新策略。

小程序的资源可以笼统地分为前端和后端资源:前端资源也可以被称为端侧资源(包括脚本、样式文件等),后端资源指的是小程序的一些服务接口。

今天我就带你从这两个角度切入,学习小程序端侧资源的更新策略,以及后端服务的灰度发布策略。当然了,虽然前端开发者比较关心端侧,但还是有必要了解一些后端的知识,有两个原因。

  • 小程序的更新很多时候都是前后端整体的更新;

  • 后端的知识能够扩宽你的技术视野,当你想成为一名全栈工程师甚至架构师时就会用到这些知识。

所以这一讲我会侧重小程序的端侧更新策略,后端灰度发布的内容占比会相对少一些。

端侧更新策略

开发小程序的技术栈虽然和传统前端非常相似,但在端侧资源的更新策略上却有明显的差异,其根源在于小程序特殊的端侧资源管理机制,所以要理解更新策略就要先了解这些知识。

在这个过程中,我会对照传统前端(或者也可以认为是浏览器对网站的前端资源管理机制),讲解小程序端侧资源管理机制,这样能便于你理解,让你更清晰地知道二者的差异。

网站的前端资源可以分为动态资源和静态资源, 静态的资源包括 js、css、图片等文件,为了提高性能通常会将这些文件尽量缓存到本地。动态的资源只有 HTML 文件。

网站的HTML 文件最初是由服务端通过模板引擎渲染出来的,比如 freemarker、smarty 等,现在仍然有很多网站使用这种方式,不过更流行的是用 React/Vue SSR 以及 SPA 的静态 HTML。

虽然在 SPA 架构中,HTML 文件与 js 文件、css文件一样作为静态资源部署,但跟 js 和 css 不同的是,我们并不会让浏览器缓存 HTML 文件,而是通过服务器配置将 HTML 文件的 HTTP 请求的 Cache-Control Header 设置为 no-cache 。这是为了保证用户每次打开网站都会得到最新版的 HTML 文件,而其他静态资源都要通过 HTML 文件才会被引入,这保证了HTML 文件的实时性,也保证了网站所有静态资源的实时性。

跟网站不同的是,小程序的"所有"端侧资源都是静态的("所有"我加了引号是因为它指的是小程序代码包中的所有文件,至于代码中引用的外部文件不在我们的讨论范围之内)。

小程序的资源是托管在微信服务器上的,跟网站不同,微信不会在用户每次打开小程序时,从服务器拉取最新的小程序资源,而是尽可能地发挥缓存的优势(触发拉取新版本资源的时机有很多种,稍后我会一条条地讲)。先来看下面这张图:

小程序端侧资源管理机制

当用户打开小程序时,微信客户端会先从缓存中拉取小程序的端侧资源,有的话就展示给用户,没有的话会从微信服务器拉取,这时,拉取的肯定是最新版本,然后放入缓存并展示给用户。

以上就是小程序的端侧资源的管理机制。从这套流程里你会发现一个问题:既然优先使用缓存中的资源,那么当我发布了小程序新版本之后,怎么保证用户尽可能快 地更新为新版本呢?这就是我们要讨论的重点:小程序的端侧资源更新机制。

前面我提到"触发拉取新版本资源的时机有很多种"。本地没有缓存会触发是最简单的一种时机,除此之外还有两种时机。

  • 未启动时: 指的是小程序处于非活跃状态时(比如处于后台),但是请注意,这种状态是用户已经用过小程序后才会产生的,如果用户从来都没有用过你的小程序,就不存在状态的概念了,因为对于这个用户来说,你的小程序是无状态的。

  • 冷启动时: 小程序被销毁重新打开后会进入冷启动状态(我们在11讲也提到了这个机制,不记得了要及时复习)。

当你在小程序管理后台发布新版本的小程序之后,微信会根据用户设备上小程序的状态实施不同的更新策略。

如果小程序处于未启动状态, 微信客户端会在"若干个时机"去检查缓存中的小程序有没有新版本,如果有会默默把新版本资源拉取到本地缓存中。请注意,"若干时机"并不是我瞎说的,而是官方说明,而这部分信息对于开发者来说是不透明的,但是有一点可以确定,那就是当你发布了新版本小程序后,无法立刻让所有用户体验最新版(至于多久能覆盖所有用户,官方说明最晚24小时)。整个流程请看下图:

小程序端侧资源更新机制(未启动时)

如果小程序处于冷启动状态,微信客户端会主动检查是否有新版本,同时会向用户展示缓存中的旧版本。有新版本的话会默默地拉取到本地,然后在用户再次触发小程序冷启动时展示给用户。也就是说,需要两次冷启动才能将最新版本的小程序展示给用户。整个流程如下图所示:

小程序端侧资源更新机制(冷启动时)

从上述内容中,你可以得出一个结论:当你发布一个新版本后,用户并不能"立即"获得更新。

在传统前端领域,当网站发布新版本之后,用户下次打开或刷新之后就会"立即"体验到新版本,没有延迟。但是在小程序场景下,更新之后并不是"立即"让用户体验到新版,而是"尽可能快"。

从官方描述中,小程序未启动时最慢 24 小时可以覆盖全部用户,或者需要经历两次冷启动,这对一些紧急的版本更新来说太慢了,所以在现实工作中往往要将小程序的更新提速,让用户尽可能快地获取到新版本。具体实施方法是通过小程序的UpdateManager对象,在代码里主动检查并应用更新信息。我们对照流程图和代码讲解,来看下面这张图:

小程序端侧资源更新策略(优化版)

以及如下代码:

javascript
const axios = require('axios')
const updateManager = wx.getUpdateManager()
updateManager.onCheckForUpdate(function (res) {
  // 将是否有新版本信息挂载到全局对象上
  this.globalData.hasUpdate = res.hasUpdate
})
updateManager.onUpdateReady(function () {
  if(!this.globalData.hasUpdate){
    return
  }
  const { miniProgram } = wx.getAccountInfoSync()
  // 获取当前小程序的版本号
  const currVersion = miniProgram.version
  // 从你的开发者服务器接口中获取是否有紧急版本需要更新
  axios.get(`${<your-url?}?currVersion=${currVersion}`).then(res=>{
    if(res.needUpdate){
      // 紧急版本立即重启小程序应用更新
      updateManager.applyUpdate()
    }
  })
})

首先在代码中创建一个 UpdateManager 对象,然后添加 onCheckForUpdate 和onUpdateReady 监听,当微信客户端从微信服务器中获取到小程序的更新信息后会触发 onCheckForUpdate 函数,入参携带 hasUpdate 属性标记是否有新版本未更新。我们将这个信息挂载到全局对象上以便后续使用。

当微信客户端从微信服务器中将最新版本的小程序端侧资源拉取到本地之后,会触发 onUpdateReady 函数,此时需要你的开发者服务器提供一个接口,对应上述代码中的 your-url。这个接口的入参是用户当前使用的小程序版本,然后根据这个版本号判断当前用户的小程序版本是否存在严重 Bug 需要更新到最新版本。你需要在小程序的脚本代码中,当onUpdateReady 函数被触发时调用这个接口,如果需要更新则通过调用 updateManager.applyUpdate() 强制重启小程序应用更新。

上述这套更新机制相比较需要两次冷启动的默认更新机制来说,能够减少一次冷启动的时间,能更快速地令用户获取最新版本的小程序,对于一些修复紧急 Bug 的版本是一种行之有效的方案。当然,我们只展示了端侧的调用流程,在后端发布小程序时,你需要记录每次发布版本的详细信息,包括是否有紧急 Bug 修复,这样才能够为端侧的调用提供数据来源。

后端服务灰度发布策略

作为一名前端开发者,大多数情况下不需要关注后端服务的发布策略,但对于一些实施大前端架构的技术团队来说,前端开发者也需要一定的服务端开发工作,比如 Node.js。即使你现在没有涉及 Node.js 的开发,了解一些后端服务的发布策略也能够增加你的竞争力。

后端服务的发布流程中有一个非常重要且通用的策略:灰度发布。所谓的灰度发布简单理解就是将新版本的服务只向一定比例的用户开放,而另一部分用户仍然使用旧版本的服务,然后观察新版本的状态,如果一切正常则慢慢扩大新版本的用户比例,直到全部用户都切入新版本,便完成了灰度发布的全流程。

灰度发布需要提前制定用户请求的转发策略,一般有两种:

  • 按照新旧服务所占用的服务器比例随机转发;

  • 按照用户的 ID 转发。

第一种简单粗暴,比如你有 10 台服务器,其中 2 台部署了新版本的服务,负载均衡器会在接收到用户请求时按照 20% 的概率随机转发到新版本服务器上,剩余的转发到旧版本服务器。

第二种需要进行一定的编码工作,比如 Nginx 配置 Lua 脚本,当接收到用户请求时,从请求中获取到用户的 ID ,在小程序场景下就是用户的 OpenId ,然后匹配转发策略中是否这个 ID 在新版本服务的白名单中,如果是的话便转发到新版本服务,否则转发到旧版本服务。如下图所示:

灰度服务转发机制

对于后端服务的灰度发布策略的讲解就到此为止,我们并没有深入到技术实现细节,主要的目的是为了让你了解灰度发布这个概念。**之所以讲这部分内容有两个目的:**一是为了让你学习一些后端服务的领域知识,提高竞争力;二是因为在下个模块我们将进入到云开发的学习中,你会学习到更多后端开发的内容,到那时候,后端服务的灰度发布就是你必不可少的一种能力。

总结

今天这节课我们学习了小程序的更新策略,主要是讲解端侧资源的更新机制,顺带学习了后端服务的灰度发布这个概念。通过今天的学习我希望你能够掌握以下几点:

  • 充分了解小程序的端侧资源更新机制;

  • 基于第一条指定更快速的端侧资源更新策略;

  • 了解灰度发布的概念。

今天的课后作业需要你动一动手:请尝试自己实现今天所学的使用 UpdateManager 更新小程序的流程。我相信动手之后,你能够更深入的理解这个对象的使用方法和背后的设计思想。