乐趣区

关于h5页面:H5开屏从龟速到闪电企微是如何做到的

导读|H5 开屏龟速常是令开发者头疼的问题。腾讯企业微信团队对该景象进行剖析优化,最终 H5 开屏耗时 130ms,达到秒开成果!企微前端开发工程师陈智仁将分享可用可扩大的 Hybird H5 秒开计划。该团队应用离线包解决了资源申请耗时的问题,在这个根底上通过耗时剖析找到瓶颈环节,进一步采纳“预热”进行优化提速以解决了 WebView 初始化、数据预拉取、js 执行(app 初始化)耗时的问题。心愿这些通用办法对你有帮忙。

背景

服务端渲染(SSR)是 Web 支流的性能优化伎俩。SSR 直出相比传统的 SPA 利用加载渲染躲避了首屏拉取数据和资源申请的网络往返耗时。团队针对 Web 开发也曾经反对了 SSR 能力。近期出于动态化经营的思考,咱们抉择了 Web 开发,同时咱们也接到了晋升体验的诉求。

以企业微信要开发的页面为例:采纳 SSR 计划,从用户点击到首屏渲染的耗时均值约 600ms,白屏工夫的存在是能够感知到的。为了尽可能打消白屏达到秒开成果,咱们尝试做更多摸索。

计划思路

1) 计划选型

如何实现页面秒开呢?从最直观的渲染链路来动手剖析。下图列出了从用户点击到看到首屏渲染可交互,一个 SPA 利用次要环节的加载流程。咱们调研了业内相干计划,从渲染链路的视角来看下常见计划的优化思路。

  • 传统离线包

在加载渲染过程中,网络 IO 是很显著的一个耗时瓶颈。传统的离线包计划思路很间接,如果网络耗时那就将资源离线,很好地解决了资源申请的耗时。用 Service Worker 也能达到离线包的成果,同时也是 Web 规范。首次渲染优化个别须要联合客户端配置预启动脚本来达到缓存资源的成果。

  • SSR

SSR 则从另外的角度登程,在申请页面的时候就进行服务端数据拉取和页面直出,首屏得以在一个网络往返就能够展现,无效地躲避了后续须要期待 css/js 资源加载、数据拉取的工夫。性能体验有比拟大的晋升,在 BFF 遍及的状况下开发模式简略,很受欢迎。

  • 公司内相干工作

思考到 WebView 的初始化(冷启动 / 二次启动)、页面网络申请、首屏数据接口的耗时,白屏工夫还是可感知地存在的。以咱们要开发的页面为例采纳 SSR 首屏耗时均值~600ms,可交互工夫均值~1100ms。如何进一步打消白屏?这里为各位介绍公司内外针对 h5 首屏性能优化的优良计划。

手 Q 团队的 VasSonic 是集大成者,次要思路是 采纳 WebView 和数据预拉取并行的形式。这套计划须要客户端和服务端采纳指定协定革新接入,开发时也有肯定的革新工作。

微信游戏团队次要思路是 利用 jsCore 做客户端预渲染,用户点击后间接上屏。这个办法也达到了很好的成果,首屏 FCP 工夫从 1664ms 升高到了 411ms。

咱们做了一个简要的计划比照,能够看到每个计划都针对渲染链路的某个或多个环节做了优化,其中VasSonic 的成果比较显著。不过联合企业微信业务理论状况,咱们列出了如下几点思考:

首先,接入对客户端和服务端有肯定的革新老本,业务开发也有肯定的革新工作 。其次, 咱们曾经有一套的对立公布平台,心愿能复用这套公布能力。最初,性能上有没有进一步优化的空间呢?业务需要对体验上的要求是心愿达到更好的性能成果或者说尽可能齐全地打消白屏

基于以上思考,咱们在上述计划的根底上做了进一步的实际摸索,以冀望达到更好的性能成果。

2)计划架构

为了达到尽可能齐全打消白屏,咱们还是从初始问题登程,联合渲染链路进行剖析,思路上针对每个环节采取对应的优化办法。

每个环节的优化在具体落地时会存在着计划的利弊取舍。比方预拉取数据个别的思路是交给客户端来做,然而存在着客户端申请和 h5 申请两套机制(鉴权、申请通道等方面)如何协调的问题。在渲染链路剖析时,如果业务的 js 执行也奉献了不少耗时,有没有可能从通用根底计划的角度来解决这个问题,同时也能缩小业务对性能优化的关注?这是个值得各位思考摸索的问题。具体的内容会在前面开展来说。

如图展现了计划的优化思路和主流程。计划应用离线包解决了资源申请耗时的问题,在这个根底上通过耗时剖析找到瓶颈环节,进一步采纳预热的思路进行优化提速,解决了 WebView 初始化、数据预拉取、js 执行(app 初始化)耗时的问题,最终达到了现实的性能体验。

图 1 上屏流程

图 2 计划架构

上面咱们具体介绍下计划,包含:离线包技术、预热提速和进一步的优化工作。

离线包减速

为了躲避资源申请耗时,咱们应用了离线包技术。离线包技术是比拟成熟的计划,相干打包、公布拉取的计划这里不多说了,次要说下计划中一些设计上的考量。

1)加载流程

咱们通过 offid 作为离线包利用的标识,fallback 机制保障离线资源不可达时用户也能够失常拜访页面,通过离线包预拉取和异步检测更新机制进步了离线包命中率,尽可能打消了网络资源加载的耗时。

2)fallback 机制

因为用户网络情况的不确定性,离线包加载可能存在失败的状况。为了保障可用性,咱们确定了离线包加载不阻塞渲染的思路。当用户点击入口 url,对应 offid 离线包在本地不存在时,会 fallback 申请现网页面,同时异步加载离线包。所以咱们针对离线包的打包构造,依照现网 URL path 来组织资源门路。这样客户端申请拦挡解决也会比拟不便,不须要了解映射规定。当发现离线包不匹配资源时,放过申请透到现网即可。如图展现了咱们的离线包构造示例。

3. 离线包生命周期

为了进步离线包命中率,咱们会配置一些机会(e.g. 入口曝光)来预拉取离线包。

离线包的更新机制:客户端加载时依据 offid 检测到本地离线包的存在,则间接应用拉起,同时启动异步版本检测和更新。如果新包版本号大于本地版本号则更新缓存,同时公布平台也反对辨别测试环境、正式环境以及按条件灰度。

上了离线包后,能够看到页面的首屏耗时均值从基准无优化的 1340ms 降到了 963ms,离线包的预拉取和更新策略则使离线包命中率达到了 95%。首屏耗时失去了肯定的升高,但也还有比拟大的优化空间,须要更一步的剖析优化。

预热提速

通过离线包的减速,咱们解决了资源申请耗时的问题,不过从整个渲染链路来看还有很大的优化空间,咱们做了 具体的耗时剖析,找出耗时瓶颈,针对耗时环节做了进一步的优化提速

1)耗时剖析

离线包技术躲避了资源申请耗时,然而从整个渲染链路来看还有很大的优化空间,咱们做了耗时剖析如下。

Hybird 利用中,WebView 初始化是比拟耗时的环节,这里咱们针对 iOS WebView 做了测试。

数据拉取方面,不同入口页面的耗时不一,某些入口页面比拟重的接口耗时超过了 1s。

此外,咱们发现 js 执行也奉献了不少耗时。以某入口页面为例,框架初始化工夫~10ms,app 初始化工夫~440ms。

2)渲染链路预热提速

  • 预热流程

咱们的指标是打消白屏,这里现实的计划是找到一种和业务无关的通用解法。计划的次要思路是预热,把能提前做的都做了。预热是不是就是把 WebView 提前创立进去就好了呢?不是的,这里的 预热波及到多个渲染环节的优化组合。如图展现了预热的整体流程,上面一个个来解。

2)WebView 预创立

为了打消 WebView 的耗时,咱们采取了全局的预创立 WebView,机会为配置入口曝光。不过全局复用预热 WebView 不可避免地会引入可能的业务内存泄露问题,下文会介绍对应的躲避计划。

  • 数据预拉取

数据拉取是页面渲染的一个耗时环节。为了打消数据预拉取耗时,在预创立 WebView 阶段咱们同时进行了数据预拉取。

数据预拉取常见的思路是交给客户端来做,然而存在着客户端申请和 h5 申请两套机制如何协调的问题,以申请鉴权为例,存在以下的问题:

第一,Web 团队本身有一层 node BFF,实现了相应的数据拉取业务逻辑,而客户端则走的公有协定通道申请 C ++ 后盾,二者是不同的鉴权机制。

第二,如果交给客户端来做,能够 接入 HTTP 申请这套机制,革新老本比拟大,如果复用原有通道,则一份数据业务逻辑须要两套实现。

如何设计一套通用可扩大的计划 ?咱们心愿做到客户端只关注容器的能力(预热、资源拦挡等),屏蔽掉更深刻的对 Web 的感知,这样的解耦能够无效管制计划的复杂度。 因而,这里咱们针对离线包配置项减少了 preUrl 字段,使客户端保护更通用的能力,数据预拉取交给业务团队来做,具体如下:

第一,客户端:拉取某个离线包配置项时会读取该字段,同时针对以后曝光的入口 url 可能存在多个有着不同的数据需要,这里会进行收集,将曝光 url 中的业务 key 参数拼接到 preUrl 来初始化 WebView,这些作为通用能力。

第二,业务:preUrl 页面在加载时会拉取相应的业务数据存到 localStorage,理论的数据预拉取申请放到业务方发动,也能够很好地兼容已有的技术栈。

  • JS 预执行

很靠近指标了,最初 js 执行的耗时能不能打消呢?首先来看下 440ms 的耗时具体在哪里,通过剖析看到,框架初始化仅须要不到 10ms 的工夫,而真正的大头在业务代码的执行,其中代码编译耗时~80ms,其余的都是业务 app 初始化执行工夫,这个是业务自身复杂度造成的。

咱们首先思考了创立两个 WebView 的计划,一个负责加载 preUrl 预拉取数据,另一个负责 loadUrl 上屏,这样设计上比拟简洁强壮,不过实际下来发现成果不现实,如图展现了该计划的成果,渲染不稳固能够感知到白屏的存在。在曾经有了预拉取数据和离线资源的状况下,实践上用户点击后须要期待的就只有渲染这块的耗时,理论咱们发现在简单利用初始化时存在 js 执行耗时较大的问题。

最终咱们做了一个预执行的解法。联合 SPA 的特点,将 preUrl 作为 SPA 的一个子页面 ,不须要 UI 展现, 只负责预拉取数据 ,这样子页面加载实现的同时也实现了 app 提前初始化。而相应的不同入口切换页面时,不同于复用预热 WebView 从新 reload 页面,为了保留 app 初始化的成果,咱们采取了 一套 Native 告诉 Web SDK,页面切换交给 WebView 管制的计划 。其中,Native 告诉则以 调用 SDK 全局办法 的形式。通过这种形式,入口页面间切换其实只是 hashchange 触发的子页面渲染,达到了不错的成果。流程图即预热计划的上屏局部。

该计划执行后咱们达到了预期指标成果,最大限度地打消了白屏靠近 Native 体验。需要上线后通过监控数据能够看到在命中预热和离线包逻辑的状况下,从用户点击到页面上屏可交互耗时均值约 130ms。

进一步优化

1)离线包平安

在离线包平安方面,为了避免包篡改,每咱们次打包公布时都会生成包签名和文件 md5。客户端在应用解析离线包时会校验完整性,在返回离线资源时会校验文件完整性。

2)稳定性

整体计划在性能上曾经达到目标了,保障稳定性对产品体验也很重要 咱们为了打消 js 执行的耗时,采取了 Native 告诉 Web SDK 管制页面切换的形式。尽管比拟灵便然而也带来了稳定性的问题。具体来说,如果 SDK 在做页面切换时异样,之后用户关上每个入口 url 都会看到雷同的页面。入口页面的业务在用户应用过程中如果跳转了非 SPA 的链接同时没有注入 SDK,之后的页面切换也会生效。

如何保障预热容器的可用性呢 ?咱们设计了一套告诉机制确保客户端感知到预热容器的可用状态,并在不可用时得以复原,如图。预热容器会保护 isInit 和 isInvokedSuc 两个状态。 只有当 preUrl 胜利加载和 SDK 执行胜利上屏时,两个状态才会置 true,此时的预热 WebView 才是可用的,否则会回退到一般容器模式进行 load url 来加载页面。

此外,在每次入口 url 曝光时,已有的预热容器也会销毁重建,也无效保障了容器的稳定性。

3)内存泄露

应用全局的预创立 WebView,不可避免的会引入可能的业务内存泄露问题。在测试过程中,咱们也发现了这种例子。能够看到当点开应用了预热容器的页面后搁置一段时间,整个内存在一直上涨,最终会导致 PC 端页面的白屏或者挪动端的 Crash,这个情况最终归因是业务逻辑的实现存在缺点。

不过在根底技术的角度而言,开发者也须要采取措施来尽可能躲避内存泄露的状况。次要思路是 缩小同一个预热容器的常驻,也就是对存活的容器设置有效期,在适当的机会查看并清理过期容器,咱们抉择的机会是App 前后台切换时

4)解决副作用

出于性能思考,咱们抉择了 通过 Web SDK 管制页面 的计划,同时应用了全局的预创立 WebView。这带来了副作用——当页面对容器做了全局的设置,可能会影响到下一个页面的体现。比方:设置 document.title、通过公有 JSAPI 设置了 WebView 导航栏的表 ……

当执行这些操作时,在下一个页面也复用预热容器的状况下,全局设置没有失去清理重置或者笼罩,用户会看到上个页面的体现。

为了解决上述问题,业务能够在每个页面被动申明须要的体现来笼罩上个页面的设置,现实的办法还是根底技术来躲避这个问题来保障业务开发的一致性。咱们在 SDK 管制切换页面时,进行了一系列的重置操作。

此外,在 Windows 和 Mac 端,咱们也设计了 双预热 WebView的计划来齐全解决这个问题。每次应用时同时创立新容器,得以保障每次关上入口页面都是应用新创建的容器。当然,计划的另一面则是会带来 App 内存的上涨。

总结

咱们从渲染链路动手,针对每个环节进行剖析优化,最终积淀了一套可用可扩大的 Hybird H5 秒开计划。从渲染链路的角度来看,计划通过离线包和预热一系列优化,将用户从点击到可交互的工夫缩短到了一个 SPA 路由切换上屏步骤的耗时。

上线后咱们监控发现,命中了预热离线逻辑的页面首屏耗时~130ms,相比于离线包、SSR 都有劣势,同时预热离线容器命中率也达到了 97%,达到了现实的体验成果。心愿本篇对你有帮忙。

腾讯工程师技术干货中转:

1、算法工程师深度解构 ChatGPT 技术

2、10 分钟!从架构视角读懂 K8s

3、探秘微信业务优化:DDD 从入门到实际

4、耗时减半?腾讯云 OCR 只做了 3 件事

退出移动版