Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
popring committed Sep 23, 2024
1 parent 14f2cfc commit 43c6cec
Showing 1 changed file with 118 additions and 3 deletions.
121 changes: 118 additions & 3 deletions source/_posts/首屏骨架屏减少白屏.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,112 @@ categories: 性能优化
- 单独写一套骨架屏样式,然后注入
- 使用骨架屏图片
- 使用自动生成骨架屏形式
- `page-skeleton-webpack-plugin`不过这个插件目前已经没有维护了
- `page-skeleton-webpack-plugin`这个插件目前已经没有维护了,(而且我也运行不起来,不是很推荐)
- 使用 `chrome 插件` 生成骨架屏,比如 `@killblanks/skeleton-ext` ,试了好几个,目前只有这个还生效,不过生成之后还是需要微调一下样式
- 自己写一套,其实原理并不复杂,简而言之就是将页面的文字和图片替换为骨架图形式展示。这里我用 chatgpt 生成了一段代码供参考,下面展示下效果图,分别用 demo页、掘金、小红书、淘宝、京东测试了下生成的骨架图,可以看出对于结构复杂页面生成还是会有点问题,如果要实际用,还是需要微调一下样式。

## 注入代码
![](https://raw.githubusercontent.com/popring/assets-repo/master/img/202409240136523.png)
![](https://raw.githubusercontent.com/popring/assets-repo/master/img/202409240136518.png)
![](https://raw.githubusercontent.com/popring/assets-repo/master/img/202409240136520.png)
![](https://raw.githubusercontent.com/popring/assets-repo/master/img/202409240136521.png)
![](https://raw.githubusercontent.com/popring/assets-repo/master/img/202409240136522.png)

下面这段代码可以复制到浏览器中执行,并预览生成的效果;如果想要使用生成的骨架屏,可以只在页面中复制dom `#skeleton-overlay`

```js
// 递归生成骨架图
function processElement(el, skeletonOverlay, bodyRect) {
const children = el.children;

if (children.length > 0) {
for (let i = 0; i < children.length; i++) {
processElement(children[i], skeletonOverlay, bodyRect);
}
} else {
const rect = el.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
const skeletonElement = document.createElement('div');
skeletonElement.className = 'skeleton-box';
skeletonElement.style.position = 'absolute';
skeletonElement.style.top = rect.top - bodyRect.top + 'px';
skeletonElement.style.left = rect.left - bodyRect.left + 'px';
skeletonElement.style.width = rect.width + 'px';
skeletonElement.style.height = rect.height + 'px';
skeletonOverlay.appendChild(skeletonElement);
}
}
}

// 覆盖页面并生成骨架图
function generateSkeleton() {
if (document.getElementById('skeleton-overlay')) return;

const skeletonOverlay = document.createElement('div');
skeletonOverlay.id = 'skeleton-overlay';
skeletonOverlay.style.position = 'fixed';
skeletonOverlay.style.top = 0;
skeletonOverlay.style.left = 0;
skeletonOverlay.style.width = '100%';
skeletonOverlay.style.height = '100%';
skeletonOverlay.style.zIndex = 9999;
skeletonOverlay.style.backgroundColor = 'rgba(255, 255, 255, 1)';

const bodyRect = document.body.getBoundingClientRect();

document
.querySelectorAll('div, p, h1, h2, h3, h4, h5, h6, a, img, span, em')
.forEach((el) => {
processElement(el, skeletonOverlay, bodyRect);
});

insertSkeletonStyles(skeletonOverlay);
document.body.appendChild(skeletonOverlay);
}

// 骨架图样式函数
function insertSkeletonStyles(skeletonOverlay) {
const skeletonStyles = `
.skeleton-box {
background-color: #f0f0f0;
animation: skeleton-loading 1.5s infinite;
border-radius: 4px;
}
@keyframes skeleton-loading {
0% {
background-color: #f0f0f0;
}
50% {
background-color: #e0e0e0;
}
100% {
background-color: #f0f0f0;
}
}
`;

const styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerText = skeletonStyles;
skeletonOverlay.appendChild(styleSheet);
}

// 切换骨架图的显示和隐藏
function toggleSkeleton() {
const skeletonOverlay = document.getElementById('skeleton-overlay');
if (skeletonOverlay) {
skeletonOverlay.style.display =
skeletonOverlay.style.display === 'none' ? 'block' : 'none';
} else {
generateSkeleton();
}
}

// 将核心函数绑定到 window 对象上
window.generateSkeleton = generateSkeleton;
window.toggleSkeleton = toggleSkeleton;
```

## 注入代码 1 - 注入进 #app 内

比如这里我使用的是 `vite` 打包工具,'webpack' 同理,也有同类型的工具可以直接用。

Expand Down Expand Up @@ -120,9 +222,22 @@ document.write(content)
},
};
}

```

至此,该方案基本已经结束,但是实际应用时,发现还是白屏闪一下,是因为在框架侧加载页面时异步的,先进行渲染了根路由信息,然后才会渲染具体路由页面的信息,所以方案还是有待完善,于是出现了下面一种。


## 注入代码 2 - 显示在页面最上层

将骨架屏渲染在一个空 `div` 上,然后用样式 `fixed` 在页面的最上层,然后监听页面实际渲染的页面是否渲染完成,实际页面渲染完之后将骨架图隐藏,就可以在视觉上进行实现了。

实现效果如图

![](https://raw.githubusercontent.com/popring/assets-repo/master/img/202409240252167.gif)

**最终代码**
https://github.com/popring/vite-skeleton

> 参考
>
> [Vue项目骨架屏注入实践](https://juejin.cn/post/6844903661726859272)
Expand Down

0 comments on commit 43c6cec

Please sign in to comment.