如何设计前端和移动端离线包方案

如何设计前端和移动端离线包方案

背景

  • 员工手机扫电磁门开门通行摆脱对网络的依赖(电磁门位置通常在WiFi覆盖边缘,扫码开门强依赖网络漫游速度)
  • 升级到离线的电子工卡,扣码开门

技术实现

以电子工卡接入离线包方案为代表的方案,属于 NSR(Native Side Rendering),这是大前端配合的典型案例

简单分析 从 0 到 1 分析一个前端+移动端离线包方案

目的

通过离线包方案的源起和落地,梳理整个 hybrid 页面的优化相关方案

如何设计前端和移动端离线包方案

如图:
第一步:从一个原生页面点击按钮,打开一个 hybrid 页面,首先经过原生页面路由,识别到”这是在访问一个 hybrid 页面”,此时原生会启动一个 WebView 容器,接着就是一个正常的前端加载并渲染页面的流程了;

第二步:前端 CSR 方式为例,首先请求并加载 HTML,接着以 HTML 为起点,请求 JavaScript、CSS 等静态资源,并由 JavaScript 发送数据请求(数据请求ajax依赖网络),最终完成页面内容的加载和渲染。

优化的路径有2个

客户端阶段

对于 WebView 容器的启动,客户端可以提前启动 WebView 容器池,这样在真正访问 hybrid 页面时,可以复用已储备好的 WebView 容器

如何设计前端和移动端离线包方案

前端阶段

前端渲染架构我们可以从 CSR 切换到 SSR,这样在一定程度上能保证首屏页面的直出,达到更好的 FMP、FCP 等时间。

如何设计前端和移动端离线包方案

离线包方案

核心思路

客户端提前下载好 HTML 模版,在用户交互时,由客户端完成数据请求并渲染 HTML,最终交给 WebView 容器加载。

离线包方案为代表的 NSR,就是客户端版本的 SSR。各个团队可能在实现思路的细节上有所不同,但主要流程基本如下图:

如何设计前端和移动端离线包方案

基本流程如下

  • 用户打开 hybrid 页面。
  • 在原生客户端路由阶段,判断离线包是否可用:
    • 如果内置的离线包版本不可用或已经落后线上版本,则走在线逻辑,即正常的 WebView 加载前端页面,由前端页面加载渲染页面的流程;
    • 如果内置的离线包版本可用,则走离线包流程。
      • 客户端启动 WebVeiw;
      • 客户端并行请求业务数据接口;
      • 客户端并行加载本地模版;
      • 接下来,客户端将执行权和必要数据交给前端,由 WebView 完成页面的渲染。

有几个主要环节需要我们思考

  • 如何检测离线包版本,如何维护离线包
  • 如何生产离线包模版
  • 客户端如何”知道”该页面需要请求哪些业务数据

离线包服务平台

为了有效检测和维护离线包的版本,开发者可以采用一种简单的方法,即手动生成离线包并将其内置到应用中,随着客户端的版本发布一起更新。然而,这种方式存在一些明显的缺点:

  • 更新周期过慢,必须依赖客户端版本发布;
  • 手动操作过多,缺乏自动化和工程化流程。

更合理的方案是构建一个”离线包平台”,该平台需满足以下需求:

获取离线包

获取离线包的方式可以分为主动模式和被动模式:

  • 被动模式:开发者在完成离线包构建后,手动将其上传至离线包平台。
  • 主动模式:更智能化的方式,将离线包流程与前端的 CI/CD 流程绑定。在前端每次发布上线时自动构建离线包,并在构建成功后由 CI/CD 流程主动调用离线包接口,将生成的离线包推送至平台。(目前如流的离线包发布就是这种方案)

提供离线包查询服务

提供一个 HTTP 服务用于查询离线包状态。客户端在每次启动应用时,可通过该服务获取各业务离线包的最新或稳定版本,并据此判断本地离线包资源是否可用。

离线包获取服务

离线包的下发方式可以基于完整版本下发,也可以将静态资源扁平化,进行增量下发。扁平化的增量下发能够最大化利用已有的离线包资源。

例如,在某次离线包构建中,如果 v1 和 v2 版本之间有大量未变更的静态资源,就可以复用这些资源,从而减少带宽和存储压力。

整体离线包服务可以抽象为下图:
如何设计前端和移动端离线包方案

离线服务平台,按照离线版本整体下发资源如下图(这种方式实现更简单—如流的实现
如何设计前端和移动端离线包方案

离线服务平台,扁平化增量下发离线资源如下图:
如何设计前端和移动端离线包方案

离线包构建能力

离线包和传统的静态资源会有区别,那么我们如何构建出一个离线包呢?

离线包就需要有一个 json 文件进行配置声明
如何设计前端和移动端离线包方案

离线包json文件配置

具体每个文件的详细说明:离线包接入流程

如流安卓需要单独配置一下客户端浏览器离线加载

如何设计前端和移动端离线包方案

其他的思考

离线包可用性和使用命中率

如果业务迭代频繁,离线包的版本更新也会相应增多,这将降低离线包的命中率,影响使用效果。同时,频繁的下载与解压过程中可能会出现错误,导致离线包不可用。

为此,通常可以设计重试机制和定时轮询策略。在网络条件允许的情况下,设置最大重试次数和 15 秒(或其他合适的时间间隔)进行下载重试,以减少因网络问题导致的失败。

离线包安全性考量

常可以设计灰度发布机制,即在全量分发某版本离线包前,先进行小流量测试,观察部分用户的使用情况。

如果检测到最新版本的离线包不可用,则可以快速切换到稳定版本,或回退到线上传统方案。常见的异常情况包括:

  • 离线包解压失败;
  • 离线包服务平台接口超时;
  • 使用增量 diff 时资源合并失败。

用户流量考量

为减少每次下载或更新离线包时的流量消耗,可以采用增量更新机制。一种方法是在客户端基于文件的哈希值执行增量更新;另一种方法则是借助类似 git-diff 的机制,根据文件变更情况生成增量包。

离线包资源的核心静态文件可以和图片等富媒体资源文件缓存分离,这样可以更便捷地管理缓存,同时将离线包的核心静态资源整体预加载至内存,减少磁盘 IO 耗时。

HTML 文件是否应该作为离线包资源的一部分

在主流方案中,通常会将 HTML 文件也纳入离线包资源的一部分。另一种策略是仅缓存 JavaScript 和 CSS 文件,而 HTML 则保持在线加载。

最后

性能优化是一个宏大的课题,不仅要求在前端领域实现最佳性能,还需要从更高的视角审视整个业务链路。

离线包方案就是一个典型的例子,它突破了传统的狭隘前端思维,强调各个业务团队的协调配合,包括客户端业务团队、前端团队和测试团队等。

要想让技术能力快速提升,不能仅仅从自己的视角看问题,也不能局限在自己的一亩三分地。

应该从全链路视角去理解和分析问题,深入了解技术的上游和下游环节。这不仅帮助我们掌握技术细节,还能增强对整体系统的把握,从而更有效地解决复杂问题。