前端图片占位优化实践

二叶草 2020年2月2日19:52:56优化评论阅读模式

1 背景

在前端实现中,我们通常会使用“前端图片占位优化实践<img>”标签来加载图片,在渲染完首屏一小段时间后图片才会加载完成,然后从无到有完整呈现。这会导致用户浏览过程中页面发生抖动,造成令人不悦的用户体验。

于是,我们在真正的图片加载完成前默认将“前端图片占位优化实践<img>”标签的“src”属性指向一个1px宽高、体积很小的灰色图片,以显示一个用于占位的灰色色块。当图片加载完成后再把它指向真正的图片地址。但实践后我们发现,在做了占位处理后仍然没有达到我们希望的页面完全无抖动的效果,如下图:

前端图片占位优化实践

我们发现,在首屏渲染时和图片加载完成时均会发生页面抖动:

  1. 首屏渲染后,灰色占位本身被加载时,页面会有剧烈抖动;
  2. 由于图片真实高度与灰色占位高度不相等,页面轻微抖动。

基于以上问题实践探究后,本文将主要分享图片占位的优化过程。

2 现状分析

首屏剧烈抖动是由于图片占位的代码执行前需要经过两个 TTFB 时间。因为项目中有一个作为资源加载器的 JS 文件(loader.js)和一个页面主逻辑运行 JS 文件(main.js),mian.js 中包含占位的代码,只有当加载并执行 main.js 后,灰色占位才会出现。

前端图片占位优化实践

既然存在两个 TTFB 时间,那最简单的方法是将图片占位的逻辑内联到 index.html 中。内联之后仍有三个时间点可以选择来触发图片占位逻辑:

  1. 监听 window onload 后触发;
  2. 监听 DOMContentLoaded 后触发;
  3. 直接内联,不监听事件。

因为占位的逻辑里存在获取元素的 offsetTop、clientWidth 等属性的代码,所以需要判断这些属性在以上三种情况里能否被正确获取。这个探究涉及到浏览器渲染的原理。

2.1 浏览器渲染流程

前端图片占位优化实践

我们知道,浏览器渲染流程大致为:

  1. 浏览器获得 HTML构建 DOM Tree, 获得 CSS 构建 CSSOM(当遇到 <script> 时,DOM 构建过程会被吊起,需要等待 JS 执行完毕,当 JS 全部执行完后,会触发 DOMContentLoaded);
  2. 通过 DOMTree 和 CSSOM 构建 Render Tree;
  3. 计算Layout(size, position);
  4. 绘制;
  5. 渲染层合并(这步是GPU来实现)。

探究后发现,第三步计算 Layout 的过程里浏览器会计算元素的 size 和 position。那么何时浏览器会执行 Layout ?

前端图片占位优化实践

如图所示,当在 DOMContentLoaded 中加上获取元素 offsetWidth 属性的代码时,浏览器同步的触发了 Layout 过程。这启发我们浏览器是在我们想要取得元素的 size 和 position 属性时,就会触发 Layout 让我们获得正确的属性。即测量属性或方法的调用,会同步的触发Layout 。例如 getComputedStyle、elem.offset* 等。所以上述三个时间点都能正确获取offsetTop、clientWidth 等属性。 而占位的逻辑越早触发体验越好,所以可以监听DOMContentLoaded 来代替监听 window load 执行占位的逻辑。

既然去取 offsetTop 时浏览器都会触发 Layout, 那么直接内联呢?

实验发现 IOS WebView 会在 DOMContentLoaded 后执行首屏渲染,而 Android WebView 会在遇到</body>前第一个 script 标签时进行首屏渲染。所以 DOMContentLoaded 和直接内联对于 IOS 来说都能够解决页面抖动的问题(Android 在极短时间内还是会抖动一下)。但 DOMContentLoaded 不会影响后面内联 JS 代码的执行,因此选择 DOMContentLoaded 来触发图片占位。优化后的效果如下:

前端图片占位优化实践

可见,优化后在页面首屏渲染时灰色占位也被同步渲染出来了,首屏第一次剧烈抖动的问题被解决。

2.2 占位高度的更优计算

第二次页面抖动涉及到计算占位灰色块高度的计算方法。

前端图片占位优化实践

说明:data-w: 图片真实宽度;data-ratio: 图片高宽比。

原有的代码中:

占位高度 = data-ratio *  Math.min(屏幕宽度, data-w)

当 data-w 比屏幕宽度小时:

占位高度 = data-ratio * data-w

// 此时占位高度绝对正确

当 data-w 比屏幕宽度大时:

占位高度 = data-ratio * 屏幕宽度

//此时占位高度不一定正确

占位高度不一定正确是因为占位高度计算中使用了屏幕宽度作为了基准,只有当图片本身的宽度占据整个屏幕时其显示的真实高度和占位高度才会相同,而对于类似下面内嵌的小图片(外有边框或者有并行元素),其占位的高度比其真实高度大。

前端图片占位优化实践

于是我们需要使用另一个基准值来代替公式中的屏幕宽度。

图文中每一张图片都有一个“max-width: 100% !important”。如果图片没有设置 “position absolute” 或者 “fixed”,“width” 百分比值会找到外面最近的块 / 单元格 / 行内块的宽度作为基准进行计算。那么循环向上寻找第一个自身宽度不为0的父元素作为计算占位高度的基准就可以解决第二次抖动的问题。 注意这里的自身宽度是指其真实的宽度,会将其子节点的 padding / border / margin 都减掉。

做了如上优化后,灰色占位与图片实际高度终于完全一致。

3 总结

优化后的最终效果如下:

前端图片占位优化实践

可以看出,所有未加载完毕的图片都能拥有正确的占位,且图片加载完成后页面不会发生抖动,优化完成。

综上, 我们的占位方案分为3个步骤:

  1. 在主页面 index.html 中内联占位代码,通过在 DOMContentLoaded 中 使用元素的 offset* 属性触发浏览器 Layout;
  2. 循环向上查找图片第一个自身宽度不为 0 的父元素后,取该父元素宽度和图片真实宽度的最小值作为占位色块的宽度;
  3. 使用占位色块的宽度乘图片的高宽比得出占位色块的高度。

业界也存在另外一些占位方法,例如使用 PaddingTop + Position absolute 占位和通过 SVG 占位,开发者可以根据自身情况合理选择相应的占位方案。

本文来源于:前端图片占位优化实践-变化吧门户
特别声明:以上文章内容仅代表作者本人观点,不代表变化吧门户观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。

  • 赞助本站
  • 微信扫一扫
  • weinxin
  • 加入Q群
  • QQ扫一扫
  • weinxin
二叶草
nginx解析漏洞 优化

nginx解析漏洞

phpstudy(小皮模板存在nginx解析漏洞) 影响版本 phptsuy8.1.07的Nginx1.5.11版本影响版本 phptsuy8.1.07的Nginx1.5.11版本 phpstudy介...
网站SEO优化基础流程(新手必看) 优化

网站SEO优化基础流程(新手必看)

宝塔面板搭建一个获取网站的Favicon图标的APIgetFavicon是一个可以获取网站的Favicon图标并显示在你的网页上的项目。安装方法很简单,属于开箱即食。这篇文章还是基于宝塔面板来搭建。 ...

发表评论