Zey-如何借助客户端能力优化 H5 启动速度

共 3281字,需浏览 7分钟

 ·

2021-06-28 01:35

前端早早聊大会,前端成长的新起点,与掘金联合举办。加微信 codingdreamer 进大会专属周边群,赢在新的起跑线。


第二十八届|前端 WebGL专场,了解3D/可视化/渲染管线/动画等等的可能性,6-26 全天直播,9 位讲师(贝壳/阿里云/蚂蚁/奇安信/小米/UC/美团等等),点我上车👉 (报名地址):

所有往期都有全程录播,可以购买年票一次性解锁全部

👉更多活动



正文如下

本文是第十八届 - 前端早早聊性能优化专场,也是早早聊第 125 场,来自 腾讯-Zey 的分享。

Hybird Web 页面的性能问题一直都非常困扰开发者,本文讲述如何从客户端角度优化 App 内的 H5 页面启动性能。介绍一些常见的方法,供大家参考,希望可以有帮助。

从客户端角度的优化思路

可以先看下大致的启动流程,如下图,整个过程基本是串行的,HTML 需要在 Webview 加载完加载,JS 需要根据HTML 的内容加载,首屏数据也需要执行 JS 之后再请求。但是如果我们可以利用客户端的能力,可以让一些耗时的过程并行起来,这样可以节省用户等待的时间。

比如,说这里 WebView 的启动和 HTML 的下载都需要时间,但是像 WebView 启动的时候,其实并没有占用任何网络的资源,这时候网络是空闲的。那么,如果能够并行的去请求首屏需要的数据,那么我们就能够在 JS 加载完成之后,立刻拿到首屏的数据,从而提高用户首屏的速度。

同时,对于客户端而言,它还可以做一些资源的缓存,比如 HTML 下载和 JS 下载,这些资源如果我们下载过一次之后就把它存储在客户端。那么,等下次加载的时候,其实就不用重新下载。

有了缓存能力,那么我们其实也可以有预加载资源的能力。当还没有打开页面的时候,提前预加载一些资源。减少页面打开耗时。

所以,总结来说,就是利用客户端的能力来实现并行、预加载、缓存等功能,减少 H5 启动流程中的耗时阶段。

并行接口请求

如前面所说,H5 加载是一个串行的过程。我们利用客户端能力去异步请求首屏数据,然后再把首屏数据交给 H5,实现并行请求。

整个流程可以由一个 JsBridge 完成 getPreloadedData(callback)。

启动时,客户端并行执行。H5 启动和首屏数据请求。这里首屏数据的接口信息,可以通过一些配置关联起来。比如页面 URL 里或者一个单独的配置文件。

对客户端而言,H5 初始化的时间和数据请求的时间先后不确定。所以,如果客户端先拿到数据,会把数据缓存在内存里,等待 H5 来调用 JsBridge。如果 H5 先来询问数据,客户端会把 H5 的 callback 缓存下来,等数据获取之后再回调。

在 H5 启动中,不但会调用上面的 JsBridge 向客户端要数据,同时也会自己发起首屏请求,所以这里需要有个竞速逻辑,使用最先返回的数据渲染。

WebView 容器化

我们都知道如果页面有预加载渲染,用户打开时,页面速度就会很快展示,对于体验提升非常明显。但是,我们要慎用预加载:

  1. 预加载会占用更多内存,消耗更多流量;
  2. 预加载可能抢占客户端资源,导致用户当前的页面卡顿;
  3. 不可能所有页面都预加载,未预加载的页面还是很慢;

所以,我们在思考,是否有一种方式,只预加载公共的部分,业务的部分等打开页面的时候再去加载,这样对于页面的性能也会有提升,同时,也会减少预加载带来的副作用。

所以,我们提出了一个 WebView 容器化的方案。

只预加载公共的部分,减少因为这部分导致的耗时,比如:WebView 初始化时间、框架库加载时间等。如果业务页面之间相似度高,还可以有更深层的定制。

客户端会预创建缓存一个 WebView 容器,供使用。当加载一个页面后,会消耗一个容器,客户端会延迟2s再创建一个。

如下是,WebView 容器化需要的能力,总体来说是,需要一个容器页面项目和业务项目。容器需要有一些通用的能力,核心是能够动态加载js然后执行渲染逻辑。业务项目,比普通的项目需要多一个编译入口,让页面能够编译成一个单独的js,并把组件暴露出来渲染。比如,Webpack 的 UMD 导出方式:文档链接

加载时,客户端主要工作就是从预加载的 WebView 池中取出一个可以用的 WebView,然后执行 JsBridge,通知对应的 JS 地址。这里可以有多种实现方式,比如传业务 JS 链接过去,然后 Web 代码请求 JS 内容。也可以客户端直接把 JS 的内容提前下载下来,直接把内容给 WebView。可以更快完成加载。

上图的 JS 伪代码是以动态加载 JS 链接为例。

WebView 容器化结合 SSR

当页面变成 SSR 直出场景,会有什么不同吗?

首先,客户端加载的内容不同。普通页面加载的内容是静态资源,不包含数据。而 SSR 里不但有数据,还有预渲染的 DOM 节点。

但是,在 SSR 场景下把用户的 HTML 缓存下来会有什么问题吗?比如,我们这样来做优化:每次把用户的 HTML缓存下来,等用户下载打开页面的时候,直接加载上次缓存的 HTML 内容,加快首屏速度。

因为直出的 HTML 中包含了用户的首屏数据和 DOM,所以,直接加载上次的 HTML 内容,会包含上次页面的数据,大概率和当前的数据不一样。就是说,每次用户打开页面的时候,都还需要请求一次新的直出 HTML,再触发页面整体重新渲染(类似页面 reload ),才能看到正确的数据。

这里有个优化的方式,就是把 HTML 拆分得细一点。分为数据和模板两部分。数据是每次变化的部分,而模板是不常变的,以此来减少页面的整体重新渲染。

模板和数据的拆分逻辑,可以参考: 开源库VasSonic

那么,分得细了之后,缓存命中情况也会复杂一些:

接下来,通过不同的缓存命中情况流程图,来详细分析:

首次加载本地没有缓存,需要全量从服务端拉取,加载后把下载的 HTML 分模块和数据缓存起来。

完全命中缓存的情况,因为没有任何变化,加载缓存即可。这里比对的是两个 hash,HTML 的 hash 和模板的hash,就是先判断整体内容有没有变化,没有变化就304。

仅数据变化的情况,就是 HTML 的 hash 发生了变化,然后对比模板的 hash,发现是一样的,就只需要返回数据给客户端。客户端再把数据透传给 H5,H5 通过修改业务数据,实现页面更新。这种只 JS 层面的数据变化,对页面的变化影响比较小,用户感知不明显。

未命中缓存的情况,就是 HTML 的 hash 和模板的 hash 都发生了变化。这是服务器会返回完整的 HTML 内容,客户端通过重新加载 HTML 来实现页面的更新。

整体流程图如上,主要增加了降级逻辑,在没有预加载 WebView 的降级情况下,客户端拉取数据会和 WebView 并行。

总结

主要讲了三种优化手段:1、首屏数据并行加载,WebView 初始化资源下载 与 首屏数据请求并行,缩短用户等待时间;2、WebView 容器化,预加载通用的 WebView,减少资源占用,同时达到预加载的目的;3、WebView 容器化+ SSR,结合 SSR 场景和缓存逻辑,实现更快的首屏速度。

当然,还有其他的一些优化手段,比如 React Native、PWA 等,也对首屏优化非常明显。欢迎有兴趣的同学一起交流学习。


别忘了 6-26(本周六) 的第二十八届|前端 WebGL专场,了解3D/可视化/渲染管线/动画等等的可能性,6-26 全天直播,9 位讲师(贝壳/阿里云/蚂蚁/奇安信/小米/UC/美团等等),点我上车👉 (报名地址):

所有往期都有全程录播,可以购买年票一次性解锁全部

👉更多活动


别忘了给文章点赞


浏览 67
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报