浅谈前端骨架屏方案

article/2025/9/15 4:52:59

在图片与前端体验优化中,最重要的莫过于「骨架屏」了,因为它和“首屏体验”息息相关。

目前来说骨架屏基本上有两种方式:

  1. HTML + CSS:主流。基本是自己在项目中以侵入式方式围绕html“定制”;微信小程序的骨架屏生成方案本质上也是这种。
  2. 自动生成。利用一些手段在业务代码之外生成骨架屏,但最终还要依托架构插入到业务中。

CSS实现骨架屏

在近期的业务中,我遇到了一个场景:
说明

图中红色框内容在接口中分为三种级别。首先每个级别的活动都是固定的,后端只返回状态值。所以前端是三个数组。
其次需要考虑一个问题:是默认展示第一级别,如果状态发生改变,再切换到第二/三级别?还是默认空白,等到接口拿到数据后根据状态展示级别?

需要明确的是,这个页面并不只有这一个接口。而且这个接口的“优先级”是低级别的。

后者效果展示:
先空白拿到数据后再展示效果

不管是从视觉上还是我想采用的技术手段上,我都认为这个场景应该选择前者 —— 这样的话,骨架屏就有了“基准”。我就不需要采用额外的元素去实现,只需要用伪元素覆盖默认文案并展示动效即可:

/** 给所有需要展示骨架的元素都添加这行代码,变量默认为false,待接口拿到数据后变为true */
:class="{'cate-skeleton': !showPOSTData}"
.cate-skeleton {position: relative;&::after {content: "";position: absolute;top: 0;left: 0;width: 100%;height: 100%;background: lightgray;background: linear-gradient(100deg, rgba(220, 220, 220, 0) 40%, rgba(255, 255, 255, 0.2) 50%, rgba(220, 220, 220, 0) 60%) gainsboro;background-size: 200% 100%;background-position-x: 150%;animation: 1.5s loading ease-in-out infinite;opacity: 1 !important;z-index: 2;}
}@keyframes loading {to {background-position-x: -50%;}
}

这段代码中最重要的就是linear-gradient了。它其实就是将 background 分为三段。然后延长其 width ,并不断改变位置 position-x
骨架屏展示数据

因为有了骨架屏,用户就知道这段时间内页面并不是什么也没做。体验也就提升了。对我们来说,我们甚至可以把接口放到组件created里面处理(笔者所在组已然按照笔者之前提的“大小组件原则”封装业务代码) —— 这里还会有一个问题:如果网络和接口实在给力,而你什么也不做,可能会出现骨架闪动的效果。这可不是什么小问题,它甚至比“从空白到数据突然展示”更加令人难受。

let res = await this.$http({//参数
})
if(!res.data && !res.result) {// 兜底
}
// 延时300ms,不然瞬间灰色闪动更难受了
await this.promiseTimeout(300);

为此,笔者决定故意延长loading的时间,给用户更好的体验:

async promiseTimeout (time) {return await new Promise(function(resolve,reject){setTimeout(function(){console.log('骨架屏加载ing');resolve(time);},time);});
},

setTimeout 微任务的异步和请求的异步不同(机制就不一样)。setTimeout 不能直接触发async-await

node实现非侵入式骨架屏生成

在复杂场景下,我们可以把业务和骨架屏分离。比如在某种身份下其实进来是B布局,如果你在开发业务时采用第一种方案的话要么骨架固定,要么CV两份 HTML 代码去书写样式。
这好么?这不好。

我们可以以页面为基准“自动”生成骨架屏,然后通过配置注入到项目源码中。
这样就可以在页面生成之后再去对指定class/id进行骨架样式生成。对其余元素可以采取定制化生成,或是直接隐藏。

这是一种“后处理”。

既如此,我们应当要求:

  • 使用和维护成本低
  • 配置灵活
  • 还原度高
  • 尽量不影响加载性能

node中的puppeteer给我们提供了很好的方案:通过 puppeteer 获取页面、做骨架处理、截屏或获取源码、默认采用 base64 输出。

Puppeteer 是一个控制 headless Chrome 的 Node.js API 。它是一个 Node.js 库,通过 DevTools 协议提供了一个高级的 API 来控制 headless Chrome。它还可以配置为使用完整的(非 headless)Chrome。
我们可以通过 puppeteer 操作网页:触发事件、截屏、爬取数据、检索 SPA 并生成预渲染内容(即 “SSR”)、甚至是创建一个能运行最新js特性的自动测试环境(浏览器)。

npm install puppeteer
const puppeteer = require('puppeteer');(async () => {const browser = await puppeteer.launch();const page = await browser.newPage();await page.setViewport({width: 骨架屏宽, height: 骨架屏高});// 事件监听,可用于事件通信page.on('console', msg => console.log('PAGE LOG:', msg.text()));page.on('warning', msg => console.log('PAGE WARN:', JSON.stringify(msg)));page.on('error', msg => console.log('PAGE ERR:', ...msg.args));// waitUntil:load/domcontentload/networkidle0/networkidle2await page.goto('页面的url!!!', {waitUntil: 'networkidle2'});// 对打开的页面进行操作// 将页面截图,输出为 pdf 或 图片await page.pdf({path: 'hn.pdf', format: 'A4'});await page.screenshot({path: 'example.png'});await browser.close();
})();

这种方案简化的并不是代码层面 —— 当然,你也可以封装成可视化。我们的处理思路和上面大致相同 —— 因为不是操作原页面,这里直接替换即可。以 Img 为例:

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

对于文字来说,也是如此:

await page.$eval('.xxx/#xxx(按class/id查找)',(el, value)=> el.setAttribute('style', value),'backgroundImage: linear-gradient(to bottom, #070b21, rgba(7, 11, 33, 0.5))'
)

或插入指定文案:

await page.$$eval('nav>ul>li>.wired-rendered',nodes => nodes.map(n => {n.innerHTML = `<span class="eval-puppeteer-bg" style="background-image: #EEEEEE">${n.innerHTML}</span>`// return n;}))

因为骨架屏主要目标是“首屏”,我们就可以移除非首屏节点:

function inViewPort(ele) {try {const rect = ele.getBoundingClientRect()return rect.top < window.innerHeight &&rect.left < window.innerWidth} catch (e) {return true;}
}

style也是如此:

const styles = Array.from(document.querySelectorAll('style')).map(style => style.innerHTML || style.innerText);
// 移除非首屏样式
function handleStyles(styles, html) {const ast = cssTree.parse(styles);const dom = new JSDOM(html);const document = dom.window.document;const cleanedChildren = [];let index = 0;ast && ast.children && ast.children.map((style) => {let slectorExisted = false,selector;switch (style.prelude && style.prelude.type) {case 'Raw':selector = style.prelude.value && style.prelude.value.replace(/,|\n/g, '');slectorExisted = selectorExistedInHtml(selector, document);break;case 'SelectorList':style.prelude.children && style.prelude.children.map(child => {const children = child && child.children;selector = getSelector(children);if (selectorExistedInHtml(selector, document)) {slectorExisted = true;}});break;}if (slectorExisted) {cleanedChildren.push(style);}});ast.children = cleanedChildren;let outputStyles = cssTree.generate(ast);outputStyles = outputStyles.replace(/},+/g, '}');return outputStyles;
}function selectorExistedInHtml(selector, document) {if (!selector) {return false;}// 查询当前样式在 html 中是否用到let selectorResult, slectorExisted = false;try {selectorResult = document.querySelectorAll(selector);} catch (e) {console.log('selector query error: ' + selector);}if (selectorResult && selectorResult.length) {slectorExisted = true;}return slectorExisted;
}

http://chatgpt.dhexx.cn/article/0DDOfp0E.shtml

相关文章

前端骨架屏应用

什么是骨架屏 骨架屏可以理解为在页面数据尚未返回或页面未完成完全渲染前&#xff0c;先给用户呈现一个由灰白块组成的当前页面大致结构&#xff0c;让用户产生页面正在逐渐渲染的感受&#xff0c;从而使加载过程从视觉上变得流畅。 生成后的骨架屏页面如下图所示&#xff1…

如何实现骨架屏效果?

今天我们来用原生js实现一个骨架屏的效果&#xff0c;效果如下&#xff1a; 首先思考如何实现 思考实现方式 骨架屏的原理是在数据没加载出来的时候&#xff0c;使用滚动的背景颜色去替代&#xff0c;等到加载完毕后则显示对应内容 那么我们的核心就是实现一个.skeleton的样…

啥是骨架屏

&#xff08;一&#xff09;什么是骨架屏 骨架屏可以理解为是当数据还未加载进来前&#xff0c;页面的一个空白版本。在页面完全渲染完成之前&#xff0c;用户会看到一个样式简单&#xff0c;描绘了当前页面的大致框架的页面&#xff0c;然后骨架屏中各个占位部分被实际资源完…

前端骨架屏方案与实践

对于依赖接口渲染的页面&#xff0c;在拿到数据之前页面往往是空白的&#xff0c;为了提示用户当前正在加载中&#xff0c;往往会使用进度条、loading图标或骨架屏的方式。 对于前两种方案而言&#xff0c;实现比较简单&#xff1b;本文主要研究骨架屏的实现方案。 骨架屏(Ske…

网页骨架屏自动生成方案(dps)

来源&#xff1a;花满楼 https://zhuanlan.zhihu.com/p/74403911 什么是骨架屏&#xff1f; 什么是骨架屏呢&#xff1f;骨架屏(Skeleton Screen)是指在页面数据加载完成前&#xff0c;先给用户展示出页面的大致结构&#xff08;灰色占位图&#xff09;&#xff0c;在拿到接口数…

骨架屏

&#xff08;一&#xff09;什么是骨架屏 骨架屏可以理解为是当数据还未加载进来前&#xff0c;页面的一个空白版本。在页面完全渲染完成之前&#xff0c;用户会看到一个样式简单&#xff0c;描绘了当前页面的大致框架的页面&#xff0c;然后骨架屏中各个占位部分被实际资源完…

Vue中实现骨架屏的多种方式

vue-cli项目首页加载缓慢想要使用骨架屏效果&#xff0c;经过几天的实践&#xff0c;这里学习并记录一下vue项目自动生成骨架屏方法。 前言&#xff1a;骨架屏的作用主要是在网络请求较慢时&#xff0c;提供基础占位&#xff0c;当数据加载完成&#xff0c;恢复数据展示。这样给…

骨架屏原理——面试别再被挨打了

目录 前言 骨架屏是什么 骨架屏原理 用途&#xff1a; &#xff08;一&#xff09;简单实现 &#xff08;二&#xff09; vue项目中的构建 &#xff08;三&#xff09;自动化方案 前言 同样是之前练手项目中的&#xff0c;emmm,知道干嘛用的&#xff0c;没了解过具体原理…

性能测试总结之内存泄露和内存溢出

刚刚做完了一个项目的性能测试&#xff0c;“有幸”也遇到了内存泄露的案例&#xff0c;所以在此和大家分享一下。 主要从以下几部分来说明&#xff0c;关于内存和内存泄露、溢出的概念&#xff0c;区分内存泄露和内存溢出&#xff1b;内存的区域划分&#xff0c;了解GC回收机…

内存泄露与内存溢出的区别及解决方法

内存溢出与泄露的区别 内存溢出 out of memory&#xff0c;是指程序在申请内存时&#xff0c;没有足够的内存空间供其使用&#xff0c;出现out of memory&#xff1b;比如申请了一个integer,但给它存了long才能存下的数&#xff0c;那就是内存溢出。 内存泄露 memory leak&…

jvm故障 内存泄露和内存溢出总结

目录 内存泄漏memory leak 内存泄漏的分类&#xff08;按发生方式来分类&#xff09; 内存泄露的场景 静态集合类 / 长生命周期的对象持有短生命周期对象的引用 / 单例模式 /类加载器 各种连接&#xff0c;如数据库连接、网络连接和IO连接等 变量不合理的作用域 内部类持…

JVM——内存泄漏与内存溢出

内存泄漏与内存溢出 1. 面试题 什么是内存泄漏和什么是内存溢出 (陌陌) Java存在内存泄漏吗&#xff0c;内存泄漏的场景有哪些&#xff0c;如何避免(百度) Java 中会存在内存泄漏吗&#xff0c;简述一下&#xff1f;(猎聘) 内存泄漏是怎么造成的&#xff1f;(拼多多、字节跳动)…

Java中内存溢出和内存泄露详解

1、内存溢出&#xff08;OOM&#xff09; 在程序中导致程序崩溃的两种原因有&#xff1a; ①、空指针、下标越界等异常&#xff0c;这类问题主要原因是因为代码写的有问题 ②、还有一类问题是&#xff0c;代码看着也没有问题&#xff0c;在进行GC时&#xff0c;回收也没有空出足…

内存泄漏和内存溢出的区别

内存泄漏就是在jvm堆中生成的对象&#xff0c;经过垃圾回收器回收后也无法得到有效的释放&#xff0c;则会导致可用的虚拟机可分配的内存越来越少。最终可能导致内存溢出。 以发生的方式来分类&#xff0c;内存泄漏可以分为4类&#xff1a; 1、常发性内存泄漏。发生内存泄漏的…

什么是内存泄漏和内存溢出

目录 一、内存溢出 (OutOfMemory)二、内存泄漏 (Memory Leak) 一、内存溢出 (OutOfMemory) 它是指程序在申请内存时&#xff0c;没有足够的内存空间供其使用&#xff0c;抛出OutOfMemory异常。 比如申请了一个8MB空间&#xff0c;但是当前内存可用空间只有5MB&#xff0c;那么…

内存溢出和内存泄漏

内存溢出&#xff1a; 一、内存溢出相对于内存泄漏来说&#xff0c;尽管更容易被理解&#xff0c;但是同样的&#xff0c;内存溢出也是引发程序崩溃的罪魁祸首之一。 二、由于GC一直在发展&#xff0c;所有一般情况下&#xff0c;除非应用程序占用的内存增长速度非常快&#…

Vim替换命令substitute介绍

原文地址&#xff1a;再谈Vim substitute替换命令-Vim入门教程(54) 在Vim替换命令一文介绍过&#xff0c;substitute 命令的语法格式为&#xff1a;:[range]s[ubstitute]/{pattern}/{string}/[flags]。 [flags] 表示可选的标志位&#xff0c;常用的包括 g、c、n、e 等。其中&…

VIM替换命令%s

[rootlocalhost 09:03:42 /home/test]# cat vim_test.txt linux nginx aabb bbcc ccdd vim vim_test.txt 进入编辑模式 输入指令 :%s/bb/zz/gc 全局替换&#xff0c;带确认 按y对搜索项替换第一个 按y对搜索项替换第二个 这种操作方法 &#xff0c;查看替换过程与效果。避…

八皇后问题c语言算法

目录 [TOC] 问题分析&#xff1a; 相信八皇后规则的问题&#xff0c;大家都很熟悉&#xff0c;接下来是如何分析回溯法的应用。回溯法与图里面的深度优先遍历非常的类似&#xff0c;就是&#xff0c;在满足题目条件时候&#xff0c;它总是优先选择第一个&#xff0c;当不满足…

八皇后算法

两个皇后都不能处于同一行、同一列或同一斜线上 讨论如果是八个皇后&#xff0c;即存在于8*8的行列式中&#xff0c;每行每列一定存在一个皇后&#xff0c;现在我们用queen[row]col 表示在第row行的皇后处于第col列&#xff0c; 位置的选定&#xff0c;5人 然后判断此时皇后所…