该系列是为了让看过Vue官方文件或学过Vue但是却不知道怎麽下手去重构现在有的网站而去规画的系列文章,在这边整理了许多我自己使用Vue重构很多网站的经验分享给读者们。
我们在开发网站的时候常常会遇到说图片或是文字还没有透过 API 回传回来的时候,我们的画面的 UI 高度可能还没有,这时候图片跟文字载入完成後会突然的撑开 UI 的高度,整个 web 页面会有种闪一下的状况,会让看的人有种不舒服的体验感,所以在处理这种资料载入的方法,就延伸出一种叫做 Skeleton 的概念。
Skeleton 顾名思义就是骨架,我们可以看到上面范例左边的卡片当资料载入进来的时候,先把文字给放上去,然後等浏览器把图片给load完成之後,在把图片给render出来,这时候我们的DOM因为图片的关系,然後高度就被撑开来了,这样子的呈现方式如果你的网站设计结构复杂,而且很吃图片的话,其实很容易造成画面因为DOM撑开来的闪动
所以为了解决这个问题,我们可以先把因为非同步所载入的图片或是文字的范围先给他定义出来,将高度或是宽度定义好,以减少之後图片跟文字载入後撑开的闪动。
在这边我就简单的来带大家来看一下 Skeleton 该如何实作首先我们先来定义一下html
html
<div class="card" v-for="item in DataValue" :key="item.id">
<header>
<div class="photo">
<img :src="item.avatar">
</div>
<p>{{item.username}}</p>
</header>
<p>{{item.text}}</p>
<main>
<img :src="item.content">
</main>
</div>
这边我将整个卡片的架构先给他订出来,包含卡片的使用者图片,还有他的名字以及图片
CSS
.card {
width: 300px;
height: auto;
border-radius: 10px;
overflow: hidden;
background-color: #fff;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
margin-bottom: 50px;
> p {
font-size: 14px;
padding: 0 10px;
margin-bottom: 10px;
}
header {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
padding: 10px;
> div {
margin-right: 10px;
> img {
border-radius: 50%;
}
}
> p {
font-size: 13px;
}
}
img {
opacity: 1;
transition: opacity .3s;
}
}
再来看他的css会发现我们现在的写法是透过内容来撑开dom的高度,这是很常见的做法。
js
import { ref, onMounted } from "vue"
export default {
setup() {
const DataValue = ref([]);
const isLoad = ref(true);
const LoadImg = (imgUrl) => {
const imgArr = [...imgUrl];
let i = 0;
imgArr.forEach(src=> {
const img = new Image();
img.src = src;
img.onload = () => {
i += 1;
if(i === imgArr.length){
isLoad.value = false;
}
}
})
}
onMounted(()=> {
fetch('https://test.api/api/card')
.then(res => res.json()).then(res => {
DataValue.value = res;
const imgArr = res.map(item=> [item.avatar, item.content]);
LoadImg([].concat(...imgArr));
});
})
return {
isLoad,
DataValue,
};
},
}
在这边我就透过 fetch 去打API来拿到我卡片的资料,然後写了一个判断图片载入完成没有的函式去判断API给我的图片路径是否都载入完成了,然後给一个isLoad的状态来做判断依据。
我把所有的图片路径全部塞入一个阵列摊平,再透过图片载入的函式把图片全部载入。
现在就是我们完成的样子
你会发现到,虽然我的API资料已经回来了,但是我的图片却还没有载入完成,所以这个时候画面上面图片就会比文字晚出来,所以才会造成这样的时间差抖动感觉。
在这边要稍微重点提醒两个重点关键
API 是非同步的,API里面本身会包含图片的路径。
浏览器载入图片也是非同步的,所以文字比图片出来的快是正常的。
接下来我们要稍微调整一下CSS的部分
.card {
// 跟上面一样的code
}
.load {
width: 300px;
height: 380px;
border-radius: 10px;
overflow: hidden;
background-color: #fff;
> header {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
padding: 10px;
> div {
width: 30px;
height: 30px;
border-radius: 50px;
margin-right: 10px;
background-color: #ededed;
> img {
border-radius: 50%;
}
}
> p {
font-size: 13px;
display: block;
width: 40%;
height: 18px;
background-color: #ededed;
}
}
> p {
font-size: 14px;
margin: 0 10px 10px 10px;
display: block;
width: 70%;
height: 18px;
background-color: #ededed;
}
> main {
width: 100%;
height: 300px;
background-color: #ededed;
}
img {
opacity: 0;
}
}
@keyframes loading {
to{
background-position-x: -20%;
}
}
.load {
.photo, p, main {
background: linear-gradient(
100deg,
rgba(256, 256, 256, 0) 30%,
rgba(256, 256, 256, 0.5) 50%,
rgba(256, 256, 256, 0) 30%)
#ededed;
background-size: 200% 100%;
background-position-x: 180%;
animation: 2s loading ease-in-out infinite;
}
}
以下是几个重要的调整
.load
的 class ,然後里面的结构完全跟 .card
一模一样。然後来看 html 的部分
<div
:class="['card', {load: isLoad}]"
v-for="item in DataValue"
:key="item.id"
>
<header>
<div class="photo">
<img :src="item.avatar">
</div>
<p>{{ isLoad ? "" : item.username }}</p>
</header>
<p>{{ isLoad ? "" : item.text }}</p>
<main>
<img :src="item.content">
</main>
</div>
在这边原本的 card class上面多加了一个 .load 的 class,也就是说一开始的时候我们其实是透过 .load 去覆盖 .card 的 css,然後当我们图片都载入完成的时後,透过 isLoad 来拿掉 .load 在切换回 .card 的 css,这样就可以达到 Skeleton 的效果,在没有抖动的情况下完成画面的 render 。
Skeleton 开发说穿了就是把原本需要靠内容物撑开的高度先写好,去替换里面的内容就好,但是这种做法有几个需要注意的
我在codepen上面有新增了这个范例
有兴趣的朋友可以看看 https://codepen.io/MikeCheng1208/pen/PomyJNa?editors=1010
Ps. 购买的时候请登入或注册该平台的会员,然後再使用下面连结进入网站点击「立即购课」,这样才可以让我获得更多的课程分润,还可以帮助我完成更多丰富的内容给各位。
我有开设了一堂专门针对Vue3从零开始教学的课程,如果你觉得不错的话,可以购买我课程来学习
https://hiskio.com/bundles/9WwPNYRpz?s=tc
那如果对於JS基础不熟的朋友,我也有开设JS的入门课程,可以参考这个课程
https://hiskio.com/bundles/b9Rovqy7z?s=tc
Mike 的 Youtube 频道
Mike的medium
MIke 的官方 line 帐号,好友搜寻 @mike_cheng
<<: Kotlin Android 第2天,从 0 到 ML - Android Studio 开发工具安装及环境设定
>>: Material UI in React [Day 6] Theme (Globals) & Inputs (Buttons)
Callback Function 回呼函式 Callback Function 其实描述的就是一个...
ASP.NET Core 主要是一个跨平台、高性能之开源Web框架 就高性能这部分 於Web Fra...
在开发网页服务器之前,必须了解网际网路资料传输的基本背景知识。今天的内容是简单介绍常被忽略的网际网路...
又到了跑范例环节,这次要试着学习 Tensorflow Serving, Tensorflow Se...
今天大概会聊到的范围 Animation 上一次有聊到,我们可以透过 Gesture 和 Stat...