html 手机访问优化,移动端首屏优化

[TOC]

## 页面加载

为什么打开一个 H5 页面会有一长段白屏时间?因为它做了很多事情,大概是:

~~~

初始化 webview -> 请求页面 -> 下载数据 -> 解析HTML -> 请求 js/css 资源 -> dom 渲染 -> 解析 JS 执行 -> JS 请求数据 -> 解析渲染 -> 下载渲染图片

~~~

一些简单的页面可能没有 JS 请求数据 这一步,但大部分功能模块应该是有的,根据当前用户信息,JS 向后台请求相关数据再渲染,是常规开发方式。

一般页面在 dom 渲染后能显示雏形,在这之前用户看到的都是白屏,等到下载渲染图片后整个页面才完整显示,首屏秒开优化就是要减少这个过程的耗时。

## 前端优化

上述打开一个页面的过程有很多优化点,包括前端和客户端,常规的前端和后端的性能优化在桌面时代已经有最佳实践,主要的是:

* 降低请求量:合并资源,减少 HTTP 请求数,minify / gzip 压缩,webP,lazyLoad。

* 加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发。

* 缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存localStorage。

* 渲染:JS/CSS优化,加载顺序,服务端渲染,pipeline。

其中对首屏启动速度影响最大的就是网络请求,所以优化的重点就是缓存,这里着重说一下前端对请求的缓存策略。我们再细分一下,分成 HTML 的缓存,JS/CSS/image 资源的缓存,以及 json 数据的缓存。

HTML 和 JS/CSS/image 资源都属于静态文件,HTTP 本身提供了缓存协议,浏览器实现了这些协议,可以做到静态文件的缓存,具体可以参考[这里](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching),总的来说,就是两种缓存:

* 询问是否有更新:根据 If-Modified-Since / ETag 等协议向后端请求询问是否有更新,没有更新返回304,浏览器使用本地缓存。

* 直接使用本地缓存:根据协议里的 Cache-Control / Expires 字段去确定多长时间内可以不去发请求询问更新,直接使用本地缓存。

前端能做的最大限度的缓存策略是:HTML 文件每次都向服务器询问是否有更新,JS/CSS/Image资源文件则不请求更新,直接使用本地缓存。那 JS/CSS 资源文件如何更新?常见做法是在在构建过程中给每个资源文件一个版本号或hash值,若资源文件有更新,版本号和 hash 值变化,这个资源请求的 URL 就变化了,同时对应的 HTML 页面更新,变成请求新的资源URL,资源也就更新了。

json 数据的缓存可以用 localStorage 缓存请求下来的数据,可以在首次显示时先用本地数据,再请求更新,这都由前端 JS 控制。

这些缓存策略可以实现 JS/CSS 等资源文件以及用户数据的缓存的全缓存,可以做到每次都直接使用本地缓存数据,不用等待网络请求。但 HTML 文件的缓存做不到,对于 HTML 文件,如果把 Expires / max-age 时间设长了,长时间只使用本地缓存,那更新就不及时,如果设短了,每次打开页面都要发网络请求询问是否有更新,再确定是否使用本地资源,一般前端在这里的策略是每次都请求,这在弱网情况下用户感受到的白屏时间仍然会很长。所以 HTML 文件的“缓存”和跟“更新”间存在矛盾。

## 客户端优化

### HTML 缓存

先接着缓存说,在客户端有更自由的缓存策略,客户端可以拦截 H5 页面的所有请求,由自己管理缓存,针对上述 HTML 文件的“缓存”和“更新”之间的矛盾,我们可以用这样的策略解决:

在客户端拦截请求,首次请求 HTML 文件后缓存数据,第二次不发请求,直接使用缓存数据。

什么时候去请求更新?这个更新请求可以客户端自由控制策略,可以在使用本地缓存打开本地页面后再在后台发起请求询问更新缓存,下次打开时生效;也可以在 APP 启动时或某个时机在后台去发起请求预更新,提升用户访问最新代码的几率。

这样看起来已经比较完美了,HTML 文件在用客户端的策略缓存,其余资源和数据沿用上述前端的缓存方式,这样一个 H5 页面第二次访问从 HTML 到 JS/CSS/Image 资源,再到数据,都可以直接从本地读取,无需等待网络请求,同时又能保持尽可能的实时更新,解决了缓存问题,大大提升 H5 页面首屏启动速度。

### 问题

上述方案似乎已完整解决缓存问题,但实际上还有很多问题:

* 没有预加载:第一次打开的体验很差,所有数据都要从网络请求。

* 缓存不可控:缓存的存取由系统 webview 控制,无法控制它的缓存逻辑,带来的问题包括: i. 清理逻辑不可控,缓存空间有限,可能缓存几张大图片后,重要的 HTML/JS/CSS 缓存就被清除了。 ii.磁盘 IO 无法控制,无法从磁盘预加载数据到内存。

* 更新体验差:后台 HTML/JS/CSS 更新时全量下载,数据量大,弱网下载耗时长。

* 无法防劫持:若 HTML 页面被运营商或其他第三方劫持,将长时间缓存劫持的页面。

这些问题在客户端上都是可以被解决的,只不过有点麻烦,简单描述下:

* 可以配置一个预加载列表,在APP启动或某些时机时提前去请求,这个预加载列表需要包含所需 H5 模块的页面和资源,还需要考虑到一个H5模块有多个页面的情况,这个列表可能会很大,也需要工具生成和管理这个预加载列表。

* 客户端可以接管所有请求的缓存,不走 webview 默认缓存逻辑,自行实现缓存机制,可以分缓存优先级以及缓存预加载。

* 可以针对每个 HTML 和资源文件做增量更新,只是实现和管理起来比较麻烦。

* 在客户端使用 httpdns + https 防劫持。

上面的解决方案实现起来十分繁琐,原因就是各个 HTML 和资源文件很多很分散,管理困难,有个较好的方案可以解决这些问题,就是离线包。

### 离线包

既然很多问题都是文件分散管理困难引起,而我们这里的使用场景是使用 H5 开发功能模块,那很容易想到把一个个功能模块的所有相关页面和资源打包下发,这个压缩包可以称为功能模块的离线包。使用离线包的方案,可以相对较简单地解决上述几个问题:

* 可以预先下载整个离线包,只需要按业务模块配置,不需要按文件配置,离线包包含业务模块相关的所有页面,可以一次性预加载。

* 离线包核心文件和页面动态的图片资源文件缓存分离,可以更方便地管理缓存,离线包也可以整体提前加载进内存,减少磁盘 IO 耗时。

* 离线包可以很方便地根据版本做增量更新。

* 离线包以压缩包的方式下发,同时会经过加密和校验,运营商和第三方无法对其劫持篡改。

到这里,对于使用 H5 开发功能模块,离线包是一个挺不错的方案了,简单复述一下离线包的方案:

* 后端使用构建工具把同一个业务模块相关的页面和资源打包成一个文件,同时对文件加密/签名。

* 客户端根据配置表,在自定义时机去把离线包拉下来,做解压/解密/校验等工作。

* 根据配置表,打开某个业务时转接到打开离线包的入口页面。

* 拦截网络请求,对于离线包已经有的文件,直接读取离线包数据返回,否则走 HTTP 协议缓存逻辑。

* 离线包更新时,根据版本号后台下发两个版本间的 diff 数据,客户端合并,增量更新。

### 预加载 webview

无论是 iOS 还是 Android,本地 webview 初始化都要不少时间,可以预先初始化好 webview。这里分两种预加载:

* 首次预加载:在一个进程内首次初始化 webview 与第二次初始化不同,首次会比第二次慢很多。原因预计是 webview 首次初始化后,即使 webview 已经释放,但一些多 webview 共用的全局服务或资源对象仍没有释放,第二次初始化时不需要再生成这些对象从而变快。我们可以在 APP 启动时预先初始化一个 webview 然后释放,这样等用户真正走到 H5 模块去加载 webview时就变快了。

* webview 池:可以用两个或多个 webview 重复使用,而不是每次打开 H5 都新建 webview。不过这种方式要解决页面跳转时清空上一个页面,另外若一个 H5 页面上 JS 出现内存泄漏,就影响到其他页面,在 APP 运行期间都无法释放了。

### 预加载数据

理想情况下离线包的方案第一次打开时所有 HTML/JS/CSS 都使用本地缓存,无需等待网络请求,但页面上的用户数据还是需要实时拉,这里可以做个优化,在 webview 初始化的同时并行去请求数据,webview 初始化是需要一些时间的,这段时间没有任何网络请求,在这个时机并行请求可以节省不少时间。

具体实现上,首先可以在配置表注明某个离线包需要预加载的 URL,客户端在 webview 初始化同时发起请求,请求由一个管理器管理,请求完成时缓存结果,然后 webview 在初始化完毕后开始请求刚才预加载的 URL,客户端拦截到请求,转接到刚才提到的请求管理器,若预加载已完成就直接返回内容,若未完成则等待。

### Fallback

如果用户访问某个离线包模块时,这个离线包还没有下载,或配置表检测到已有新版本但本地是旧版本的情况如何处理?几种方案:

* 简单的方案是如果本地离线包没有或不是最新,就同步阻塞等待下载最新离线包。这种用户打开的体验更差了,因为离线包体积相对较大。

* 也可以是如果本地有旧包,用户本次就直接使用旧包,如果没有再同步阻塞等待,这种会导致更新不及时,无法确保用户使用最新版本。

* 还可以对离线包做一个线上版本,离线包里的文件在服务端有一一对应的访问地址,在本地没有离线包时,直接访问对应的线上地址,跟传统打开一个在线页面一样,这种体验相对等待下载整个离线包较好,也能保证用户访问到最新。

第三种 Fallback 的方式还带来兜底的好处,在一些意外情况离线包出错的时候可以直接访问线上版本,功能不受影响,此外像公共资源包更新不及时导致版本没有对应上时也可以直接访问线上版本,是个不错的兜底方案。

上述几种方案策略也可以混着使用,看业务需求。

### 使用客户端接口

网路和存储接口如果使用 webkit 的 ajax 和 localStorage 会有不少限制,难以优化,可以在客户端提供这些接口给 JS,客户端可以在网络请求上做像 DNS 预解析/IP直连/长连接/并行请求等更细致的优化,存储也使用客户端接口也能做读写并发/用户隔离等针对性优化。

## 服务端渲染

早期 web 页面里,JS 只是负责交互,所有内容都是直接在 HTML 里,到现代 H5 页面,很多内容已经依赖 JS 逻辑去决定渲染什么,例如等待 JS 请求 JSON 数据,再拼接成 HTML 生成 DOM 渲染到页面上,于是页面的渲染展现就要等待这一整个过程,这里有一个耗时,减少这里的耗时也是白屏优化的范围之内。

优化方法可以是人为减少 JS 渲染逻辑,也可以是更彻底地,回归到原始,所有内容都由服务端返回的 HTML 决定,无需等待 JS 逻辑,称之为服务端渲染。是否做这种优化视业务情况而定,毕竟这种会带来开发模式变化/流量增大/服务端开销增大这些负面影响。手Q的部分页面就是使用服务端渲染的方式,称为动态直出,见文章。

参考资料

[移动 H5 首屏秒开优化方案探讨](https://mp.weixin.qq.com/s/i035lEHc2w2K-TBhbDtaLQ)

[WebView性能、体验分析与优化](https://tech.meituan.com/WebViewPerf.html)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/575628.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2014年10月末

迄今,我的第一个目标已达到。接下来就要为第二个目标做准备了,而且是长期,不可松懈的准备。大体上计划用10个月吧! 2014年,10月末,到时给出一个结果!转载于:https://www.cnblogs.com/zhangyabin…

html5 indexeddb 排序,html5 – 在IndexedDB中,有没有办法进行排序复合查询?

本回答中使用的术语“复合查询”是指在其WHERE子句中涉及多个条件的SQL SELECT语句。虽然indexedDB规范中没有提到这样的查询,但您可以通过创建一个包含一组属性名称的keypath的索引来近似复合查询的行为。这与创建索引时使用多条目标志完全无关。多条目标志调整ind…

Spark源码分析 -- SchedulableBuilder

SchedulableBuilder就是对Scheduleable tree的封装, 在Pool层面(中间节点), 完成对TaskSet的调度(FIFO, FAIR) 在TaskSetManager 层面(叶子节点), 完成对TaskSet中task的调度(locality)以及track(retry) TaskSetManager 用于封装TaskSet, 主要提供对单个TaskSet内部的tasks的t…

html设置照片模糊效果,CSS如何实现照片模糊?

在开发网页时,照片模糊处理会经常被使用,比如当我们背景图的模糊,当我们不想背景图片过于突出影响美观时,就可以设置将照片模糊处理,突出文字部分。那么 CSS 如何实现照片模糊呢?这篇文章 w3cschool 小编告…

rhel6用centos163 yum源

cd /etc/yum.repos.d/wget wget http://mirrors.163.com/.help/CentOS6-Base-163.reposed -i "s/\$releasever/6/" CentOS6-Base-163.repo 转载于:https://www.cnblogs.com/yanghuahui/p/3507313.html

女生学计算机未来出路,计算机真的已经烂大街了吗,女生学计算机没出路吗?...

假的。先说第一个问题:情况是,现在程序员是很多,但多半是中低端程序员,高端程序员供不应求,薪资一涨再涨。现在的程序员门槛一高再高,就是为了淘汰掉那些半吊子的程序员。如果你是靠从网上复制粘贴代码的“…

简明易懂的call apply

在iteye看到一篇对call解释得相当简明易懂,觉得得宣传一下 : http://uule.iteye.com/blog/1158829 一、方法的定义 call方法: 语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一…

计算机网络有哪些技能知识,网络基础知识及操作技能.ppt

网络基础知识及操作技能 主讲人 李春报 一、计算机网络概述 1、计算机网络的概念 计算机网络是把分布在不同地理位置上的计算机、终端,用通信设备和通信线路连结起来,再配以相应的网络软件,从而使众多计算机可以方便地互相传递信息&#xff0…

这些快捷键要学会使用啊

史上最全苹果键盘及Xcode快捷键整理 它们分别是command、option、control、shift以及esc除了command键上有标志之外其余四个可以通过这四个键对比比较出来。 谢谢原文作者提供较详细的快捷键说明。 常用的Xcode快捷键,工欲善其事,必先利其器。mac的键盘和…

目前计算机应用最广泛的区域是,自考《计算机应用基础》试题练习(一)

以下是关于2019年4月上海自考《计算机应用基础》串讲复习,由上海自考通整理发布,希望对即将参加自考的考生们能有帮助,更多的复习资料可点击查看历年真题。第一章一、选择1. 世界第一台电子计算机生产日期是( A )。A. 1946 B. 1945 C. 1947 D…

CSS元素选择器

1、在css元素选择器中飞,最重要的是HTML页面中的元素的定位; p { color: red; } h1{ color:blue; font-family:sans-serif; } 2、css选择器分组: 将 h2 和 p 选择器放在规则左边,然后用逗号分隔,就定义了一个规则。其右边的样式&a…

计算机基础知识二进制转换,计算机基础知识数制转换

二、数制转换1.十进制数到二进制数的转换(1)、整数部分 除2取余法(余数为0为止),最后将所取余数按逆序排列。实例:将十进制数23转换为二进制数2| 232| 11 余数 12| 5 余数 12| 2余数 12|1 余数 00 余数 1结果为 (23)10 (10111)2(2)、小数部分 乘2取整法…

iOS基础 - 控制器

一、当两个控制器互为父子关系的时候,它们的view一般也是互为父子关系 比如想添加A控制器的view到B控制器的view上,就应该让A控制器成为B控制器的子控制器,而B控制器就称为A控制器的父控制器 //[B addChildViewController:A]; // A就会存在于…

计算机专业英语第07章,计算机专业英语电子教案第07章.ppt

计算机专业英语电子教案第07章Computer English Chapter 7 Programming Languages 复杂定语(从句)的翻译技巧之二 四、句子结构调整法 有时,原文句中一个中心词带有若干修饰成分,但它们既不是纯“并列”头系,也不是规则的“连环”关系&#x…

关机计算机专业,电脑关机后自动重启怎么回事

日常生活中,大部分的网友朋友们都碰到过电脑在正常使用时,突然电脑自动重启了,并且多次尝试强制关机都无效,令人费神。下面小编针对此问题的原因给出了几种方法,希望对大家有所帮助,快来看看吧!…

PHP验证码常用的函数记录

1、绘制真彩画布,返回资源类型的图像标识符 resource imagecreatetruecolor ( int $width , int $height ) 例:$image imagecreatetruecolor( 30, 20 ); 2、为创建的画布分配背景颜色,参数中的颜色(分别是红绿蓝)&…

计算机无法打开打印机ip端口,讲述Win10电脑上无法选择打印机端口的解决方法...

我们在Win10电脑上使用打印机,进行打印材料是很平常的一件事,但是有用户在Win10电脑上,操作打印机时,出现了打印机端口无法选择的问题,不知道该怎么办?如果打印机端口无法正常选择的话,就代表我…

简单自定义标签步骤

自定义标签主要用于移除Jsp页面中的java代码。 使用自定义标签移除jsp页面中的java代码,只需要完成以下两个步骤:编写一个实现Tag接口的Java类(标签处理器类)。编写标签库描述符(tld)文件,在tld文件中对标签处理器类进…

计算机相关科幻小说,科幻小说家和计算机科学家总是用人工智能来迷惑我们,计算机可以...

科幻小说家和计算机科学家总是用人工智能来迷惑我们,计算机可以自我思考。相关句子1、从更广泛的意义上看,借助计算机科学,我们可以了解人类思想的本质和理性的意义,学会回答如何度过一生这个最古老的问题。把认知视为一种解决周围…

Android 4 学习(20):ActionBar

参考《Pro Android 4.0》 ActionBar 11.0之后,ActionBar在Activity中默认存在,可以在代码中设置其显示与否: ActionBar actionBar getActionBar(); // Hide the Action Bar actionBar.hide(); // Show the Action Bar actionBar.show(); …