摘要
文章先阐述前端减少页面加载时间提升用户体验的背景,分析现有骨架屏方案的优缺点,然后提出新的非侵入式骨架屏自动生成方案,包括设计原则、架构、优化点等,并对部分技术细节如 puppeteer、文本块处理等进行解析,还展示了效果和业务实践成果。
一、前端页面加载相关背景
前端领域中背景性能优化、减少页面加载时间、提升用户体验是重要话题。在前后端分离、异步渲染普遍应用的背景下,用户访问页面时易出现短时间白屏。目前有服务端同步渲染、增加页面 loading、增加页面首屏骨架屏等解决方案,它们各有优缺点。服务端同步渲染效果好但成本高;页面 loading 通用性强、成本低但信息量少;首屏骨架屏能提供较多信息但成本稍高。
二、现有骨架屏方案调研
(一)侵入业务式手写代码
这种方式在写业务代码时写骨架屏代码,代码是业务代码一部分,修改骨架屏代码相当于修改业务代码,对业务代码有侵入性,维护成本高。例如相关文献提到的方式。
(二)非侵入业务式手写代码
需要手写骨架屏代码,不过与业务代码分离,通过 webpack 注入到项目源码。好处是解耦,降低维护成本,但 webpack 有配置成本。如 Vue 相关的骨架屏实践。
(三)非侵入式骨架屏代码自动生成
无需手写骨架屏代码,自动生成且注入到项目源码,使用成本低。例如饿了么的相关方案。
三、新的非侵入式骨架屏自动生成方案
(一)设计原则
设计原则包括骨架屏自动生成、使用和维护成本低,配置灵活,还原度高,尽量不影响加载性能。
(二)方案设计
- 骨架屏生成
- 分为准备、处理和输出三个阶段。
- 准备阶段:使用
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 标签、文本、自定义属性等,获取首屏的
html
和style
代码。 - 输出阶段:将骨架屏以“base64 图片”和“HTML + 样式 style 代码”两种形式输出。
- 骨架屏注入
- 骨架屏生成有两种产出,考虑到 base64 图片体积小(如测试中只有 4k 大小,而 HTML 源码有 23k 大小)、可灵活作为页面背景图、注入量小、使用灵活等特点,默认注入 base64 图片作为页面背景图。
(三)优化点
- 以
npm
包为最终形态,支持node
和命令行两种使用方式,方便灵活。 - 骨架屏注入直接通过
node
文本写入,无需webpack
配置,降低使用门槛。 - 有 base64 图片和
html
源码两种生成物,适用于不同场景,如 base64 图片作背景图,html
源码用于问题定位。
四、部分技术细节解析
(一)puppeteer
- 是 Google Chrome 团队官方的无界面(Headless)Chrome 工具,提供高级 API 控制无头版 Chrome,也可配置为使用完整 Chrome。
waitUntil
参数很实用,有load/domcontentloaded/networkidle0/networkidle2
四个值,用于控制操作页面时的等待时间,如networkidle0
指 500ms 内无请求发出,networkidle2
指 500ms 内不多于 2 个请求发出,可保证页面充分加载。
(二)文本块处理
- 处理相对复杂,对于不同的文本容器(行内元素如
span
、块级元素如div
)处理多行文本时,直接增加灰色背景效果不佳。 - 采用
linear - gradient
可解决背景不美观的问题,给出了行内元素和块级元素处理的html
和css
示例及效果展示。
(三)图片块处理
逻辑较简单,将所有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 - remove
、data - skeleton - bgcolor
、data - skeleton - ignore
、data - skeleton - empty
四个自定义属性优化效果,给出了示例。
(六)首屏 HTML 和样式处理
- 为使生成的骨架屏
HTML
源码无冗余,对非首屏节点直接移除,给出了判断节点是否在视口内的函数示例。 - 样式处理同理,移除非首屏节点用到的样式,给出了获取、处理样式的核心代码示例。
五、效果演示与业务实践
- 方案优点有无代码侵入、接入成本低、两种产出使用灵活、自然过渡避免闪屏。
- 展示了业务实践效果。