骨架组件封装
1)定义基础骨架组件结构 components/Skeleton/index.vue
<template>
<div
class="xtx-skeleton shan"
:style="{ width: '60px', height: '30px' }"
>
<!-- 1 盒子-->
<div class="block" :style="{ backgroundColor: '#efefef'}"></div>
<!-- 2 闪效果 xtx-skeleton 伪元素 --->
</div>
</template>
<script>
export default {
name: 'XtxSkeleton'
}
</script>
<style scoped lang="less">
.xtx-skeleton {
display: inline-block;
position: relative;
overflow: hidden;
vertical-align: middle;
.block {
width: 100%;
height: 100%;
border-radius: 2px;
}
}
.shan {
&::after {
content: "";
position: absolute;
animation: shan 1.5s ease 0s infinite;
top: 0;
width: 50%;
height: 100%;
background: linear-gradient(
to left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0) 100%
);
transform: skewX(-45deg);
}
}
@keyframes shan {
0% {
left: -100%;
}
100% {
left: 120%;
}
}
</style>
2)props设计
我们把骨架的宽度(width)、高度(height)、背景色(backgroundColor)定义为props,由用户自定义传入定制效果
<template>
<div
class="xtx-skeleton shan"
+ :style="{ width: width + 'px', height: height + 'px' }"
>
<!-- 1 盒子-->
+ <div class="block" :style="{ backgroundColor: bg }"></div>
<!-- 2 闪效果 xtx-skeleton 伪元素 --->
</div>
</template>
<script>
export default {
name: 'XtxSkeleton',
// 容许定制的参数包括: 背景/宽度/高度
+ props: {
+ bg: {
type: String,
default: '#efefef'
},
+ width: {
type: String,
default: '100'
},
+ height: {
type: String,
default: '100'
}
+ }
}
</script>
3)测试组件props ------ `playground/index.vue`
<!-- 骨架组件测试 -->
<XtxSkeleton width="100" height="30" bg="blue"/>
骨架组件业务实现
1)以插件的形式注册为全局可用-------`src/components/index.js`
import Skeleton from './Skeleton'
export default {
install (app) {
app.component(Skeleton.name, Skeleton)
}
}
2)注册插件 ----------- `main.js`
import componentPlugin from '@/components'
createApp(App).use(store).use(router).use(componentPlugin).mount('#app')
3)业务组件使用
<template>
<div class="home-category">
<!-- 正式数据渲染 -->
<template v-if="list.length > 0">
<ul class="menu">
<li
v-for="item in list"
:key="item.id"
@mouseenter="mouseenter(item.id)"
>
<RouterLink to="/">{{ item.name }}</RouterLink>
<!--
在vue3里面
template 只有俩种情况使用
1. 根据某个状态渲染多个模板的 v-if v-else
2. 插槽的场景 <tempalte #default></template> <tempalte #footer></template>
-->
<template v-if="item.children.length > 0">
<RouterLink v-for="i in item.children" :key="i.id" to="/">{{
i.name
}}</RouterLink>
</template>
<!-- 弹层layer位置 -->
<div class="layer">
<h4>分类推荐 <small>根据您的购买或浏览记录推荐</small></h4>
<ul>
<li v-for="i in item.goods" :key="i.id">
<RouterLink to="/">
<img :src="i.picture" alt="" />
<div class="info">
<p class="name ellipsis-2">
{{ i.name }}
</p>
<p class="desc ellipsis">{{ i.desc }}</p>
<p class="price"><i>¥</i>{{ i.price }}</p>
</div>
</RouterLink>
</li>
</ul>
</div>
</li>
</ul>
</template>
<!-- 骨架屏占位 -->
<template v-else>
<ul class="menu">
<li v-for="i in 9" :key="i">
<XtxSkeleton
:width="40"
:height="20"
style="margin-right: 5px"
bg="rgba(255,255,255,0.2)"
/>
<XtxSkeleton
:width="50"
:height="20"
bg="rgba(255,255,255,0.2)"
style="margin-right: 5px"
/>
<XtxSkeleton :width="50" :height="20" bg="rgba(255,255,255,0.2)" />
</li>
</ul>
</template>
</div>
</template>