用纯 DOM 的方式结合 Puppeteer 自动生成网页骨架屏

3,208 阅读4分钟

  骨架屏是在页面数据尚未加载完成前先给用户展示出页面的大致结构,直到请求数据返回后再显示真正的页面内容;随着单页应用( SPA )的越来越流行,单页应用的用户体验也越来越得到前端开发者的关注;为了优化用户体验,在数据到达用户之前,往往会在页面上加上 loading 的效果,而现在,越来越多的场景倾向于使用页面的骨架替代单一的 loading 效果;

为什么需要自动生成骨架屏?

  1. 提高效率,节约单独编写骨架屏代码的时间
  2. 替换原来单一的 loading 图片效果
  3. 可以优化用户体验,在页面数据尚未加载完成前先给用户展示出页面的大致结构,配合动画效果,给用户一种平滑切换的感觉

常见的方案:

  1. 手动编写骨架屏代码
  2. 通过预渲染手动书写的代码生成相应的骨架屏 比如:vue-skeleton-webpack-plugin
  3. 饿了么内部的生成骨架页面的工具 page-skeleton-webpack-plugin
  4. ..

a. 前两者的前提都是需要开发者自己编写骨架屏代码

b. 饿了么的做的比较强大了,还有 UI 界面专门调整骨架屏

  • 对于复杂的页面也会有不尽如人意的地方
  • 生成的骨架屏节点是基于页面本身的结构和 CSS,存在嵌套比较深的情况,体积不会太小
  • 只支持 history 模式.

我们的方案是:用纯 DOM 的方式结合 Puppeteer 自动生成网页骨架屏

  • 编写操作 DOM 的 Javascript 脚本
    • 遍历可见区域可见的 DOM 节点 包括:非隐藏元素、宽高大于 0 的元素、非透明元素、内容不是空格的元素、位于浏览窗口可见区域内的元素
    • 针对(背景)图片、文字、表单项、音频视频等区域,算出其所占区域的宽、高、距离视口的绝对距离等信息
    • 对于符合生成条件的区域,一视同仁,生成相应区域的颜色块
    • “一视同仁”即对于符合条件的区域,不区分具体元素,不用考虑结构层级,不考虑样式,统一生成 div 的颜色块
    • 该脚本的运行环境决定了获取到的元素尺寸与相关距离单位不可控,可能需要做转换,比如用的 rem、em、vh 等;我们采用比较简单的方式,不取 style 的尺寸相关的值,而是通过 getBoundingClientRect 获取宽、高、距离视口距离的绝对值,与当前设备的宽高,计算出相应的百分比作为颜色块的单位,这样来适配不同设备
    • 对于页面结构比较复杂或者大图片比较多的页面,会出现不尽如人意的地方,我们通过 includeElement(node, draw)和 init 两个钩子函数来支持自定义的微调
    • 以上就能够直接跑在浏览器生成骨架屏代码了,手动添加到应用页面
    const createSkeletonHTML = require('DrawPageStructure/evalDOM')

    createSkeletonHTML({
        // ...
        background: 'red',
        animation: 'opacity 1s linear infinite;'
    }).then(skeletonHTML => {
        console.log(skeletonHTML)
    }).catch(e => {
        console.error(e)
    })

  直接在浏览器端运行,在控制台打印当前页面骨架屏节点,复制添加到应用页面,但是该方式不够自动化,我们该让骨架屏自动生成并添加到应用页面

  • Puppeteer

Puppeteer 是谷歌官方出品的一个可以控制 headless Chrome 的 Node 库。可以通过 Puppeteer 的提供的 api 直接控制 Chrome 模拟大部分用户操作来进行 UI Test 或者作为爬虫访问页面来收集数据。

Puppeteer 提供运行环境和导出方式

  1. 使用 puppeteer 运行需要生成骨架屏的页面
  2. 将之前编写的 Javascript 脚本通过 puppeteer 提前注入到该页面,这样即可运行该脚本,并生成骨架屏所需的 DOM 节点
  3. 将自动生成的骨架屏 DOM 片段插入到应用页面的入口节点
const evalDOM = require('../evalDOM');

await page.goto(url, {waitUntil: 'networkidle0'});
const skeletonHTML = await page.evaluate.call(page, evalDOM, ...args);

小结

  1. 核心在于 DOM 操作,puppeteer 仅提供运行环境和导出方式
  2. 只要能访问的页面都能生成,history 与 hash 模式无限制
  3. 不受项目和框架的限制,vue 和 react 等项目零修改即可复用
  4. 生成色块的单位为百分比,不同设备自适应
  5. 不需要 css-tree 来提取样式,不依赖页面本身的布局结构,生成扁平的 DOM 节点体积特别小
  6. 支持自定义生成方式与导出方式

还有很多细节优化中,欢迎感兴趣的小伙伴一起加入!

详细代码和使用方式请移步: github.com/famanoder/D…

欢迎 star !,欢迎提 PR !