2020-03-26 考拉前端骨架屏生成技術(shù)揭秘

1. 為什么要使用骨架屏

骨架屏就是在頁面數(shù)據(jù)尚未加載前,先給用戶展示出頁面的大致結(jié)構(gòu)(灰色占位圖),直到請(qǐng)求數(shù)據(jù)返回后再渲染頁面,補(bǔ)充進(jìn)需要顯示的數(shù)據(jù)內(nèi)容,考拉H5購物車就使用了骨架屏技術(shù):

image

了解了骨架屏是什么,我們來看看為什么要使用骨架屏。 假如能在加載前把網(wǎng)頁的大概輪廓預(yù)先顯示,接著再逐漸加載真正內(nèi)容,這樣既降低了用戶的焦灼情緒,又能使界面加載過程變得自然通暢,不會(huì)造成網(wǎng)頁長時(shí)間白屏或者閃爍。骨架屏能給人一種頁面內(nèi)容“已經(jīng)渲染出一部分”的感覺,相較于傳統(tǒng)的 loading 效果,在一定程度上可提升用戶體驗(yàn)。尤其在下面場(chǎng)景中,骨架屏技術(shù)能極大提高用戶體驗(yàn):

  • 網(wǎng)絡(luò)較慢,需要長時(shí)間等待加載處理的情況下。

  • 圖文信息內(nèi)容較多的列表/卡片中。

  • 首屏加載數(shù)據(jù)量較大的時(shí)候。

2. 骨架屏技術(shù)總覽

目前主流的骨架屏生成技術(shù)主要包括以下三種:

  1. 通過設(shè)計(jì)師給出的骨架屏圖片。

  2. 通過 HTML+CSS 編寫骨架屏代碼。

  3. 非侵入式自動(dòng)生成骨架屏代碼。

前兩種情況由于變更成本和續(xù)維護(hù)成本高,且對(duì)業(yè)務(wù)代碼有一定侵入性,不進(jìn)行討論。業(yè)界對(duì)于自動(dòng)生成骨架屏有多種實(shí)踐,但是存在一些問題,有些配置較少,生成效果較差;有些操作繁瑣,項(xiàng)目集成成本高,且難以定制。

本文主要針對(duì)自動(dòng)生成骨架屏技術(shù)進(jìn)行了深入的探討,并開發(fā)了 awesome-skeleton,支持多種配置,以及骨架屏定制功能,并提供骨架圖生成和骨架圖模板注入能力。

3. 自動(dòng)生成骨架屏技術(shù)揭秘

谷歌瀏覽器在2017年自行開發(fā)了 Chrome Headless 特性,并與之同時(shí)推出了 Puppeteer。Puppeteer 是一個(gè) Node庫,提供了一組用來操縱 Chrome 的 API,默認(rèn) Headless 也就是無界面的chrome,俗稱“無頭瀏覽器”。我們?cè)跒g覽器中完成的大多數(shù)操作都可以在 Puppeteer 中完成,比如截圖、爬蟲、自動(dòng)化測(cè)試、性能分析等。

借助 Puppeteer,我們對(duì)自動(dòng)生成骨架屏方案進(jìn)行了如下設(shè)計(jì):

  • 使用 Puppeteer 進(jìn)行頁面渲染,獲取頁面 DOM 結(jié)構(gòu);

  • 命令行工具生成骨架屏代碼,并支持第三方平臺(tái)接入;

  • 支持頁面登錄態(tài)、不同設(shè)備頁面顯示、列表重復(fù)渲染、骨架屏灰色塊大小配置;

  • 支持骨架屏圖片壓縮和代碼自動(dòng)生成;

  • 通過 DOM 屬性進(jìn)行骨架屏細(xì)粒度的配置,指定節(jié)點(diǎn)配置,包括背景色、是否在骨架屏中顯示等。

我們可以通過傳入頁面地址,使用無頭瀏覽器打開頁面,對(duì)頁面首屏圖片和文本等節(jié)點(diǎn)進(jìn)行灰色背景處理,然后對(duì)頁面首屏進(jìn)行截圖,生成壓縮后的 base64 png 圖片,并注入 HTML + CSS,從而自動(dòng)生成頁面骨架屏。流程見下圖:

image

3.1 使用 Puppeteer 渲染頁面

使用 Puppeteer 可以指定不同設(shè)備模擬器來渲染頁面,從而獲取頁面的 DOM 結(jié)構(gòu)。關(guān)鍵代碼:

// 初始化無頭瀏覽器

獲取到頁面 DOM 結(jié)構(gòu)之后,需要對(duì)其進(jìn)行處理,例如將圖片轉(zhuǎn)換為灰色色塊,隱藏大小小于一定閾值的節(jié)點(diǎn),這里我們通過向頁面中動(dòng)態(tài)插入一端 JavaScript 腳本,對(duì)頁面節(jié)點(diǎn)進(jìn)行處理,從而生成骨架屏。關(guān)鍵代碼:

// 獲取處理 DOM 節(jié)點(diǎn)的腳本代碼

3.2 頁面 DOM 處理

下面我們來重點(diǎn)看看如何對(duì)頁面節(jié)點(diǎn)進(jìn)行處理,主要包括以下內(nèi)容:

image

3.2.1 預(yù)處理

首先,對(duì)頁面所有節(jié)點(diǎn)進(jìn)行下列處理:

  • 置空所有大小小于指定閾值的節(jié)點(diǎn)

  • 所有節(jié)點(diǎn)背景圖設(shè)置為 none,并且將背景色設(shè)置為骨架屏主色調(diào)(灰色)

  • 所有節(jié)點(diǎn)陰影、邊框顏色設(shè)置為主色調(diào)

  • 置空所有標(biāo)簽中含有 “data-skeleton-empty” 屬性的節(jié)點(diǎn)

  • 若節(jié)點(diǎn)設(shè)置了“data-skeleton-bgcolo”,則選取其屬性值來設(shè)置背景色。### 文本處理 本文處理是所有節(jié)點(diǎn)中比較復(fù)雜的一種,需要考慮文本是一行還是多行、文本是位置是居中還是左對(duì)齊等,通過獲取文本位置和樣式,通過簡(jiǎn)單的計(jì)算來設(shè)置灰色色塊位置。下面只列出了關(guān)鍵代碼,全部代碼見:text.js。

// 獲取行高

3.2.2 列表處理

可以根據(jù)配置,對(duì)列表進(jìn)行重復(fù)處理,也就是根據(jù)列表的第一項(xiàng)重復(fù)渲染其他項(xiàng),關(guān)鍵代碼:

const listHandler = (node, options) => {

3.2.3 其他節(jié)點(diǎn)

其他節(jié)點(diǎn)的處理較為簡(jiǎn)單,有興趣可以在 Github 上查看代碼,這里有幾個(gè)需要關(guān)注的點(diǎn):

  • a 標(biāo)簽需要移除 href 屬性

  • button 標(biāo)簽需要設(shè)置寬高,保證在移除按鈕文案后寬高不變

  • img 標(biāo)簽也需要設(shè)置寬高,并將 src 設(shè)置為 1px 的 base64

  • input 標(biāo)簽需要移除 placeholder

  • 偽元素同樣需要處理。

3.2.4 文本類型特殊處理

需要注意的是,對(duì)于文本節(jié)點(diǎn),由于存在下面的情況,所以需要進(jìn)行特殊處理:

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

文本類型進(jìn)行處理的關(guān)鍵代碼:

handleText(node) {

3.2.5 處理頁面節(jié)點(diǎn)入口函數(shù)

有些上述單個(gè)節(jié)點(diǎn)的處理函數(shù),我們可以遍歷頁面所有節(jié)點(diǎn)進(jìn)行處理。關(guān)鍵代碼:

handleNode(node) {

3.2.6 使用 rollup 打包腳本

由于對(duì)頁面節(jié)點(diǎn)處理的腳本使用 es6 語法編寫, 我們需要在插入頁面之前,進(jìn)行編譯:

// rollup.config.js

3.3 生成骨架屏代碼

生成處理頁面節(jié)點(diǎn)腳本之后,插入到頁面中,這個(gè)時(shí)候需要一些鉤子來執(zhí)行頁面DOM的處理。我們定義一個(gè)全局對(duì)象 AwesomeSkeleton,其中包含 genSkeleton 方法,調(diào)用后會(huì)處理頁面節(jié)點(diǎn)。

window.AwesomeSkeleton = {

在使用 puppeteer 打開頁面之后,我們注入了 rollup 打包后的腳本,這時(shí)候我們需要執(zhí)行腳本才能生成骨架屏:

await page.evaluate(async options => {

生成骨架屏后,我們通過調(diào)用 puppeteer 的截圖接口,生成骨架屏圖片,并使用 images 包進(jìn)行圖片壓縮,生成 base64,從而生成骨架屏代碼。

// First screen skeleton screenshot

4. 使用 awesome-skeleton

通過上述討論的技術(shù)方案,我們實(shí)現(xiàn)了 awesome-skeleton 骨架屏生成工具。支持命令行生成骨架屏代碼,同時(shí)也可以非常方便的在第三方平臺(tái)接入。

4.1 參數(shù)配置

參數(shù)名稱 必填 默認(rèn)值 說明
pageUrl - 頁面地址(此地址必須可訪問)
pageName output 頁面名稱(僅限英文)
cookies
頁面 Cookies,用來解決登錄態(tài)問題
outputPath skeleton-output 骨架圖文件輸出文件夾路徑,默認(rèn)到項(xiàng)目 skeleton-output 中
openRepeatList true 默認(rèn)會(huì)將每個(gè)列表的第一項(xiàng)進(jìn)行復(fù)制
device PC 參考 puppeteer/DeviceDescriptors.js,可以設(shè)置為 'iPhone 6 Plus'
debug false 是否開啟調(diào)試開關(guān)
debugTime 0 調(diào)試模式下,頁面停留在骨架圖的時(shí)間
minGrayBlockWidth 0 最小處理灰色塊的寬度
minGrayPseudoWidth 0 最小處理偽類寬

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

{

4.2 一鍵生成骨架屏

$ skeleton -c ./skeleton.config.json

頁面 DomReady 之后,會(huì)在頁面頂部出現(xiàn)紅色按鈕:開始生成骨架屏。

生成完成后,會(huì)在運(yùn)行目錄生成 skeleton-output 文件件,里面包括骨架屏 png 圖片、base64 文本、html 文件:

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

  • base64-baidu.txt # 骨架圖 Base64 編碼

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

其中 html 文件可以直接拿來用,復(fù)制下面位置:

<html>

注意:

  • 骨架圖默認(rèn)在 onload 事件后銷毀。

  • 手動(dòng)銷毀方式:javascript window.SKELETON && SKELETON.destroy(); 當(dāng)然,你也可以在項(xiàng)目中直接使用生成的 Base64 圖片。

4.3 解決登錄問題

如果頁面需要登錄,則需要下載 Chrome 插件 EditThisCookie,將 Cookie 復(fù)制到配置參數(shù)中。Puppeteer 通過在打開頁面的時(shí)候注入 Cookie 從而模擬登錄態(tài):

// Write cookies to solve the login status problem

4.4 DOM 節(jié)點(diǎn)配置

這是獲取優(yōu)質(zhì)骨架圖的要點(diǎn),通過設(shè)置以下幾個(gè) dom 節(jié)點(diǎn)屬性,在骨架圖中對(duì)某些節(jié)點(diǎn)進(jìn)行移除、忽略和指定背景色的操作,去除冗余節(jié)點(diǎn)的干擾,從而使得骨架圖效果達(dá)到最佳。

參數(shù)名稱 說明
data-skeleton-remove 指定進(jìn)行移除的 dom 節(jié)點(diǎn)屬性
data-skeleton-bgcolor 指定在某 dom 節(jié)點(diǎn)中添加的背景色
data-skeleton-ignore 指定忽略不進(jìn)行任何處理的 dom 節(jié)點(diǎn)屬性
data-skeleton-empty 將某dom的innerHTML置為空字符串

示例:

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

5. 開發(fā)骨架屏生成平臺(tái)

有了骨架屏生成工具,我們可以非常方便的接入第三方平臺(tái),例如我們使用 egg.js 開發(fā)骨架屏生成平臺(tái),用戶輸入頁面鏈接,自動(dòng)生成對(duì)應(yīng)骨架屏。關(guān)鍵代碼:

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

頁面效果:

image

骨架屏配置:

image

6. 參與貢獻(xiàn)

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 作者:jayzouhttps://segmentfault.com/a/1190000015876164 首屏 一...
    grain先森閱讀 3,663評(píng)論 0 5
  • 一、簡(jiǎn)介 1.1 骨架屏 SPA應(yīng)用,由于初始的html內(nèi)部只有一個(gè)空的div,需要等待 js、css加載編譯完成...
    Johnson杰閱讀 1,197評(píng)論 0 0
  • 背景 傳統(tǒng)的優(yōu)化用戶等待的體驗(yàn) - 白屏使用動(dòng)畫,菊花圖,加載進(jìn)度條等: It can be bad becaus...
    肉桂猿閱讀 823評(píng)論 0 0
  • Vue項(xiàng)目骨架屏注入實(shí)踐 相比于早些年前后端代碼緊密耦合、后端工程師還得寫前端代碼的時(shí)代,如今已發(fā)展到前后端分離,...
    萱萱暮雨閱讀 1,857評(píng)論 0 6
  • 你遇到過打開一個(gè)網(wǎng)站需要10秒以上的嗎?這種網(wǎng)頁響應(yīng)非常緩慢,占用大量的CPU和內(nèi)存,瀏覽起來常常有卡頓,頁面的動(dòng)...
    前端小澈閱讀 343評(píng)論 0 1

友情鏈接更多精彩內(nèi)容