第 12 期 - 前端非侵入式骨架屏自动生成方案的设计与实现
logoFRONTALK AI/11月4日 16:31/阅读原文

摘要

文章先阐述前端减少页面加载时间提升用户体验的背景,分析现有骨架屏方案的优缺点,然后提出新的非侵入式骨架屏自动生成方案,包括设计原则、架构、优化点等,并对部分技术细节如 puppeteer、文本块处理等进行解析,还展示了效果和业务实践成果。

一、前端页面加载相关背景

前端领域中背景性能优化、减少页面加载时间、提升用户体验是重要话题。在前后端分离、异步渲染普遍应用的背景下,用户访问页面时易出现短时间白屏。目前有服务端同步渲染、增加页面 loading、增加页面首屏骨架屏等解决方案,它们各有优缺点。服务端同步渲染效果好但成本高;页面 loading 通用性强、成本低但信息量少;首屏骨架屏能提供较多信息但成本稍高。

二、现有骨架屏方案调研

(一)侵入业务式手写代码

这种方式在写业务代码时写骨架屏代码,代码是业务代码一部分,修改骨架屏代码相当于修改业务代码,对业务代码有侵入性,维护成本高。例如相关文献提到的方式。

(二)非侵入业务式手写代码

需要手写骨架屏代码,不过与业务代码分离,通过 webpack 注入到项目源码。好处是解耦,降低维护成本,但 webpack 有配置成本。如 Vue 相关的骨架屏实践。

(三)非侵入式骨架屏代码自动生成

无需手写骨架屏代码,自动生成且注入到项目源码,使用成本低。例如饿了么的相关方案。

三、新的非侵入式骨架屏自动生成方案

(一)设计原则

设计原则包括骨架屏自动生成、使用和维护成本低,配置灵活,还原度高,尽量不影响加载性能。

(二)方案设计

  1. 骨架屏生成
    • 分为准备、处理和输出三个阶段。
    • 准备阶段:使用puppeteer(这是一个 Node 库,可模拟打开目标页面并等待充分加载)模拟打开目标页面。例如:
    const puppeteer = require('puppeteer');
    (async () => {
      const browser = await puppeteer.launch();
      const page = await browser.newPage();
      await page.setViewport({width: 375, height: 812});
      await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'});
      // 其他操作
      await browser.close();
    })();
    
    • 处理阶段:调用处理器处理脚本、图片、a 标签、文本、自定义属性等,获取首屏的htmlstyle代码。
    • 输出阶段:将骨架屏以“base64 图片”和“HTML + 样式 style 代码”两种形式输出。
  2. 骨架屏注入
    • 骨架屏生成有两种产出,考虑到 base64 图片体积小(如测试中只有 4k 大小,而 HTML 源码有 23k 大小)、可灵活作为页面背景图、注入量小、使用灵活等特点,默认注入 base64 图片作为页面背景图。

(三)优化点

  1. npm包为最终形态,支持node和命令行两种使用方式,方便灵活。
  2. 骨架屏注入直接通过node文本写入,无需webpack配置,降低使用门槛。
  3. 有 base64 图片和html源码两种生成物,适用于不同场景,如 base64 图片作背景图,html源码用于问题定位。

四、部分技术细节解析

(一)puppeteer

  1. 是 Google Chrome 团队官方的无界面(Headless)Chrome 工具,提供高级 API 控制无头版 Chrome,也可配置为使用完整 Chrome。
  2. waitUntil参数很实用,有load/domcontentloaded/networkidle0/networkidle2四个值,用于控制操作页面时的等待时间,如networkidle0指 500ms 内无请求发出,networkidle2指 500ms 内不多于 2 个请求发出,可保证页面充分加载。

(二)文本块处理

  1. 处理相对复杂,对于不同的文本容器(行内元素如span、块级元素如div)处理多行文本时,直接增加灰色背景效果不佳。
  2. 采用linear - gradient可解决背景不美观的问题,给出了行内元素和块级元素处理的htmlcss示例及效果展示。

(三)图片块处理

逻辑较简单,将所有img标签的src设为 1x1px 的灰色base64图片,背景色也设为相同灰色。例如:

Array.from(document.body.querySelectorAll('img')).map(img => {
  img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
  img.style.backgroundColor = '# EEEEEE';
});

(四)a 标签处理

为防止骨架屏的html形态中a标签可点,将所有a标签的href设为javascript:void(0)。例如:

Array.from(document.body.querySelectorAll('a')).map(a => {
  a.href = 'javascript:void(0);';
});

(五)自定义属性处理

页面元素多,按默认规则处理骨架屏可能色块杂乱。可使用data - skeleton - removedata - skeleton - bgcolordata - skeleton - ignoredata - skeleton - empty四个自定义属性优化效果,给出了示例。

(六)首屏 HTML 和样式处理

  1. 为使生成的骨架屏HTML源码无冗余,对非首屏节点直接移除,给出了判断节点是否在视口内的函数示例。
  2. 样式处理同理,移除非首屏节点用到的样式,给出了获取、处理样式的核心代码示例。

五、效果演示与业务实践

  1. 方案优点有无代码侵入、接入成本低、两种产出使用灵活、自然过渡避免闪屏。
  2. 展示了业务实践效果。
 
Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有