2020-03-26 考拉前端骨架屏生成技术揭秘

1. 为什么要使用骨架屏

骨架屏就是在页面数据尚未加载前,先给用户展示出页面的大致结构(灰色占位图),直到请求数据返回后再渲染页面,补充进需要显示的数据内容,考拉H5购物车就使用了骨架屏技术:

image

了解了骨架屏是什么,我们来看看为什么要使用骨架屏。 假如能在加载前把网页的大概轮廓预先显示,接着再逐渐加载真正内容,这样既降低了用户的焦灼情绪,又能使界面加载过程变得自然通畅,不会造成网页长时间白屏或者闪烁。骨架屏能给人一种页面内容“已经渲染出一部分”的感觉,相较于传统的 loading 效果,在一定程度上可提升用户体验。尤其在下面场景中,骨架屏技术能极大提高用户体验:

  • 网络较慢,需要长时间等待加载处理的情况下。

  • 图文信息内容较多的列表/卡片中。

  • 首屏加载数据量较大的时候。

2. 骨架屏技术总览

目前主流的骨架屏生成技术主要包括以下三种:

  1. 通过设计师给出的骨架屏图片。

  2. 通过 HTML+CSS 编写骨架屏代码。

  3. 非侵入式自动生成骨架屏代码。

前两种情况由于变更成本和续维护成本高,且对业务代码有一定侵入性,不进行讨论。业界对于自动生成骨架屏有多种实践,但是存在一些问题,有些配置较少,生成效果较差;有些操作繁琐,项目集成成本高,且难以定制。

本文主要针对自动生成骨架屏技术进行了深入的探讨,并开发了 awesome-skeleton,支持多种配置,以及骨架屏定制功能,并提供骨架图生成和骨架图模板注入能力。

3. 自动生成骨架屏技术揭秘

谷歌浏览器在2017年自行开发了 Chrome Headless 特性,并与之同时推出了 Puppeteer。Puppeteer 是一个 Node库,提供了一组用来操纵 Chrome 的 API,默认 Headless 也就是无界面的chrome,俗称“无头浏览器”。我们在浏览器中完成的大多数操作都可以在 Puppeteer 中完成,比如截图、爬虫、自动化测试、性能分析等。

借助 Puppeteer,我们对自动生成骨架屏方案进行了如下设计:

  • 使用 Puppeteer 进行页面渲染,获取页面 DOM 结构;

  • 命令行工具生成骨架屏代码,并支持第三方平台接入;

  • 支持页面登录态、不同设备页面显示、列表重复渲染、骨架屏灰色块大小配置;

  • 支持骨架屏图片压缩和代码自动生成;

  • 通过 DOM 属性进行骨架屏细粒度的配置,指定节点配置,包括背景色、是否在骨架屏中显示等。

我们可以通过传入页面地址,使用无头浏览器打开页面,对页面首屏图片和文本等节点进行灰色背景处理,然后对页面首屏进行截图,生成压缩后的 base64 png 图片,并注入 HTML + CSS,从而自动生成页面骨架屏。流程见下图:

image

3.1 使用 Puppeteer 渲染页面

使用 Puppeteer 可以指定不同设备模拟器来渲染页面,从而获取页面的 DOM 结构。关键代码:

// 初始化无头浏览器

获取到页面 DOM 结构之后,需要对其进行处理,例如将图片转换为灰色色块,隐藏大小小于一定阈值的节点,这里我们通过向页面中动态插入一端 JavaScript 脚本,对页面节点进行处理,从而生成骨架屏。关键代码:

// 获取处理 DOM 节点的脚本代码

3.2 页面 DOM 处理

下面我们来重点看看如何对页面节点进行处理,主要包括以下内容:

image

3.2.1 预处理

首先,对页面所有节点进行下列处理:

  • 置空所有大小小于指定阈值的节点

  • 所有节点背景图设置为 none,并且将背景色设置为骨架屏主色调(灰色)

  • 所有节点阴影、边框颜色设置为主色调

  • 置空所有标签中含有 “data-skeleton-empty” 属性的节点

  • 若节点设置了“data-skeleton-bgcolo”,则选取其属性值来设置背景色。### 文本处理 本文处理是所有节点中比较复杂的一种,需要考虑文本是一行还是多行、文本是位置是居中还是左对齐等,通过获取文本位置和样式,通过简单的计算来设置灰色色块位置。下面只列出了关键代码,全部代码见:text.js。

// 获取行高

3.2.2 列表处理

可以根据配置,对列表进行重复处理,也就是根据列表的第一项重复渲染其他项,关键代码:

const listHandler = (node, options) => {

3.2.3 其他节点

其他节点的处理较为简单,有兴趣可以在 Github 上查看代码,这里有几个需要关注的点:

  • a 标签需要移除 href 属性

  • button 标签需要设置宽高,保证在移除按钮文案后宽高不变

  • img 标签也需要设置宽高,并将 src 设置为 1px 的 base64

  • input 标签需要移除 placeholder

  • 伪元素同样需要处理。

3.2.4 文本类型特殊处理

需要注意的是,对于文本节点,由于存在下面的情况,所以需要进行特殊处理:

<span>111<a>222</a></span> 文本中包含超链接

文本类型进行处理的关键代码:

handleText(node) {

3.2.5 处理页面节点入口函数

有些上述单个节点的处理函数,我们可以遍历页面所有节点进行处理。关键代码:

handleNode(node) {

3.2.6 使用 rollup 打包脚本

由于对页面节点处理的脚本使用 es6 语法编写, 我们需要在插入页面之前,进行编译:

// rollup.config.js

3.3 生成骨架屏代码

生成处理页面节点脚本之后,插入到页面中,这个时候需要一些钩子来执行页面DOM的处理。我们定义一个全局对象 AwesomeSkeleton,其中包含 genSkeleton 方法,调用后会处理页面节点。

window.AwesomeSkeleton = {

在使用 puppeteer 打开页面之后,我们注入了 rollup 打包后的脚本,这时候我们需要执行脚本才能生成骨架屏:

await page.evaluate(async options => {

生成骨架屏后,我们通过调用 puppeteer 的截图接口,生成骨架屏图片,并使用 images 包进行图片压缩,生成 base64,从而生成骨架屏代码。

// First screen skeleton screenshot

4. 使用 awesome-skeleton

通过上述讨论的技术方案,我们实现了 awesome-skeleton 骨架屏生成工具。支持命令行生成骨架屏代码,同时也可以非常方便的在第三方平台接入。

4.1 参数配置

参数名称 必填 默认值 说明
pageUrl - 页面地址(此地址必须可访问)
pageName output 页面名称(仅限英文)
cookies
页面 Cookies,用来解决登录态问题
outputPath skeleton-output 骨架图文件输出文件夹路径,默认到项目 skeleton-output 中
openRepeatList true 默认会将每个列表的第一项进行复制
device PC 参考 puppeteer/DeviceDescriptors.js,可以设置为 'iPhone 6 Plus'
debug false 是否开启调试开关
debugTime 0 调试模式下,页面停留在骨架图的时间
minGrayBlockWidth 0 最小处理灰色块的宽度
minGrayPseudoWidth 0 最小处理伪类宽

例如添加 skeleton.config.json,生成考拉首页在 iphone X 下的骨架屏。

{

4.2 一键生成骨架屏

$ skeleton -c ./skeleton.config.json

页面 DomReady 之后,会在页面顶部出现红色按钮:开始生成骨架屏。

生成完成后,会在运行目录生成 skeleton-output 文件件,里面包括骨架屏 png 图片、base64 文本、html 文件:

  • base64-baidu.png # 骨架图图片

  • base64-baidu.txt # 骨架图 Base64 编码

  • base64-baidu.html # 最终生成 HTML

其中 html 文件可以直接拿来用,复制下面位置:

<html>

注意:

  • 骨架图默认在 onload 事件后销毁。

  • 手动销毁方式:javascript window.SKELETON && SKELETON.destroy(); 当然,你也可以在项目中直接使用生成的 Base64 图片。

4.3 解决登录问题

如果页面需要登录,则需要下载 Chrome 插件 EditThisCookie,将 Cookie 复制到配置参数中。Puppeteer 通过在打开页面的时候注入 Cookie 从而模拟登录态:

// Write cookies to solve the login status problem

4.4 DOM 节点配置

这是获取优质骨架图的要点,通过设置以下几个 dom 节点属性,在骨架图中对某些节点进行移除、忽略和指定背景色的操作,去除冗余节点的干扰,从而使得骨架图效果达到最佳。

参数名称 说明
data-skeleton-remove 指定进行移除的 dom 节点属性
data-skeleton-bgcolor 指定在某 dom 节点中添加的背景色
data-skeleton-ignore 指定忽略不进行任何处理的 dom 节点属性
data-skeleton-empty 将某dom的innerHTML置为空字符串

示例:

<div data-skeleton-remove><span>abc</span></div>

5. 开发骨架屏生成平台

有了骨架屏生成工具,我们可以非常方便的接入第三方平台,例如我们使用 egg.js 开发骨架屏生成平台,用户输入页面链接,自动生成对应骨架屏。关键代码:

const getSkeleton = require('awesome-skeleton');

页面效果:

image

骨架屏配置:

image

6. 参与贡献

Github:https://github.com/kaola-fed/awesome-skeleton

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容