当前位置: 首页 > 开发杂谈 >

[重构倒数第24天] - You should use Skeleton

前言

该系列是为了让看过Vue官方文件或学过Vue但是却不知道怎麽下手去重构现在有的网站而去规画的系列文章,在这边整理了许多我自己使用Vue重构很多网站的经验分享给读者们。

我们在开发网站的时候常常会遇到说图片或是文字还没有透过 API 回传回来的时候,我们的画面的 UI 高度可能还没有,这时候图片跟文字载入完成後会突然的撑开 UI 的高度,整个 web 页面会有种闪一下的状况,会让看的人有种不舒服的体验感,所以在处理这种资料载入的方法,就延伸出一种叫做 Skeleton 的概念。

vue 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的状态来做判断依据。

我把所有的图片路径全部塞入一个阵列摊平,再透过图片载入的函式把图片全部载入。

现在就是我们完成的样子

vue mike

你会发现到,虽然我的API资料已经回来了,但是我的图片却还没有载入完成,所以这个时候画面上面图片就会比文字晚出来,所以才会造成这样的时间差抖动感觉。

在这边要稍微重点提醒两个重点关键

  1. API 是非同步的,API里面本身会包含图片的路径。

  2. 浏览器载入图片也是非同步的,所以文字比图片出来的快是正常的。

接下来我们要稍微调整一下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;
    } 
}

以下是几个重要的调整

  1. 首先新增了一个 .load 的 class ,然後里面的结构完全跟 .card 一模一样。
  2. 原本要靠内容物撑开的高度,现在全部都设定了预设的高度,也给了预设的背景颜色,让使用者知道这个区块。
  3. 撰写反光渐层以及动画,让画面再载入的时候不是只有单纯的色块。

然後来看 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 。

mike vue

Skeleton 开发的前提

Skeleton 开发说穿了就是把原本需要靠内容物撑开的高度先写好,去替换里面的内容就好,但是这种做法有几个需要注意的

  1. 这不是只有前端需要对工法上面的处理,更应该要在事前设计的时候让设计师把 Skeleton 的概念给考虑进去,假设今天图片是让使用者随便上传的,那就会有宽高的问题,如果不能限制使用者上传的宽高,那就只能我们定义预设宽高度,当今天使用者上传的图片宽高超过或是未达预设宽高时该怎麽处理,也都是在设计师的范畴内,要做的更极端点很多资讯也需要後端提供,例如文字的多寡或是预设的高度等等,所以 Skeleton 的概念是一个会需要团队都有对 Skeleton 有共识的时候,才能完美实现的一个概念。
  2. Skeleton 取代了以往的整个页面的 loading 画面,不需要再让所有图片都载完才可以把画面秀出来,但是不代表整页的 loading 画面不需要,像是很多动态的网页就不适合做 Skeleton,所以 Skeleton 并不是一个通用所有页面的解法,而是需要再依照呈现的方式来决定透过哪种方式来给使用者看。
  3. Skeleton 的开发会增加许多的 code 来处理预设画面的部分,所以很多组件的预设呈现其实是没有办法共用的,所以制作上面还需要考虑是不是全部的 UI 都需要 Skeleton,并非整个页面都会需要 Skeleton。

我在codepen上面有新增了这个范例
有兴趣的朋友可以看看 https://codepen.io/MikeCheng1208/pen/PomyJNa?editors=1010

Mike Vue

那如果对於Vue3不够熟的话呢?

Ps. 购买的时候请登入或注册该平台的会员,然後再使用下面连结进入网站点击「立即购课」,这样才可以让我获得更多的课程分润,还可以帮助我完成更多丰富的内容给各位。

我有开设了一堂专门针对Vue3从零开始教学的课程,如果你觉得不错的话,可以购买我课程来学习
https://hiskio.com/bundles/9WwPNYRpz?s=tc

那如果对於JS基础不熟的朋友,我也有开设JS的入门课程,可以参考这个课程
https://hiskio.com/bundles/b9Rovqy7z?s=tc

订阅Mike的频道享受精彩的教学与分享

Mike 的 Youtube 频道
Mike的medium
MIke 的官方 line 帐号,好友搜寻 @mike_cheng


相关文章:

  • 亚马逊卖家美国站FBA入仓需要注意些什么?
  • wish中裤子的标签该如何撰写?
  • 亚马逊品牌授权指南
  • Day35 ( 电子元件 ) LED 显示温湿度 ( DHT11 )
  • 零信任架构-可扩展存取控制标记语言(XACML)是用於授权的最佳存取控制策略语言
  • Jumia产品定价逻辑
  • 速卖通平台广告之间的区别?
  • 【C# 群益 API 开发教学】帐号登入、取得下单帐号教学 #CH2 (附范例)
  • 从 IT 技术面细说 Search Console 的 27 组数字 KPI (17) :结构化资料(收录)
  • [Day 9] Vue的模板语法(Template Syntax)---插值(2)
  • [Day 28] 资料产品开发实务 - 非机器学习模型
  • 跨境电商退换标服务选择哪个荷兰海外仓比较好
  • lazada店铺服务质量下降影响
  • 搜索引擎付费营销的优点和缺点
  • Generate CSRF PoC 伪造跨站请求漏洞利用产生
  • 网站更换域名后副作用大吗?针对谷歌如何更换域名
  • Akaxin:开源社交软件/加密聊天/打造你自己的社交产品
  • 微信小程序搭建教程:怎么用CentOS搭建小程序服务器
  • 如何在Hositnger主机开启Cloudflare服务,Hositnger主机Cloudflare CDN使用教程
  • 简单做WordPress网站的方法:最快最便宜的做外贸网站的方法
  • WordPress SEO插件-Yoast V13.3 汉化中文版高级版
  • VPS优惠信息:阿里云/限时活动/新用户1C2G1M/69元/年起
  • Google:国际化网站即使有相同的英文内容也不属于重复内容
  • Gutenberg最新版如何添加导航间隔
  • Google SEO入门:如何做好谷歌网站排名SEO
  • 教程/Directory Lister逗比魔改版 Github开源(个人云)
  • 区块链是什么东西?区块链原理是什么
  • 教育邮箱:申请塔拉哈西社区学院edu邮箱
  • DNS是什么?DNS有什么用?为什么要用DNS解析域名
  • 俄取消商品知识产权限制 盗版游戏、电影等商标合法化