骨架屏注入实践二:dps(draw-page-structure)
前提背景:
vue-skeleton-webpack-plugin通过预渲染手动书写的代码生成相应的骨架屏,通过 vueSSR 结合 webpack 在构建时渲染写好的 vue 骨架屏组件,将预渲染生成的 DOM 节点和相关样式插入到最终输出的 html 中,然后预渲染生成骨架屏所需的 DOM 节点,但由于该方案不够灵活、可控;因此针对Vue2.x项目骨架屏注入(上)的方案进行优化——网页骨架屏自动生成(dps)。
dps 具体操作步骤
1、安装dps
npm i draw-page-structure -D
2、在具体某个页面中需要生成骨架屏的页面添加:
mounted() {
// 注意:生成骨架屏后,把骨架屏代码贴到vue文件后,下面代码需要注释掉,否则每次进来又生成html插入页面
setTimeout(() => {
createSkeletonHTML({
// 此段代码实际无效
background: 'red',
animation: 'opacity 1s linear infinite;'
}).then((skeletonHTML) => {
// 此处输出来的就是骨架屏节点,可以自定义骨架屏节点内容
console.log(skeletonHTML);
}).catch((e) => {
console.error(e);
});
}, 5000);
},
3、npm run dev
在浏览器中运行后,可在控制台输出当前页面骨架屏节点,复制添加到应用页面,如:
src/commercialActivity/index.vue
<template lang="html">
<div class="commercialActivity">
<!--为了交互好看就用了transition-->
<transition name="skeleton">
<div v-if="showSkeleton">
<!--骨架屏节点-->
<div class="_ __" style="height:100%;z-index:990;background:#fff"></div><div class="_" style="height:2.099%;top:3.748%;left:6.667%;width:73.667%"></div><div class="_" style="height:2.099%;top:3.748%;left:81.067%;width:12.267%"></div><div class="_" style="height:1.799%;top:8.096%;left:6.667%;width:34.492%"></div><div class="_" style="height:1.799%;top:8.096%;left:74.133%;width:19.200%"></div><div class="_" style="height:4.498%;top:14.768%;left:9.333%;width:7.467%"></div><div class="_" style="height:2.249%;top:15.967%;left:26.975%;width:40.000%"></div><div class="_" style="height:2.249%;top:15.892%;left:69.333%;width:4.000%;border-radius:50%"></div><div class="_" style="height:2.099%;top:15.967%;left:76.000%;width:16.000%"></div><div class="_" style="height:2.249%;top:27.361%;left:1.867%;width:3.950%"></div><div class="_" style="height:10.195%;top:23.388%;left:8.483%;width:17.908%"></div><div class="_" style="height:2.249%;top:23.763%;left:29.058%;width:66.938%"></div><div class="_" style="height:2.399%;top:26.912%;left:30.125%;width:6.400%;border-radius:2px"></div><div class="_" style="height:1.799%;top:27.211%;left:74.246%;width:21.750%"></div><div class="_" style="height:2.249%;top:30.960%;left:29.058%;width:66.938%"></div><div class="_" style="height:2.399%;top:37.031%;left:40.858%;width:12.800%"></div><div class="_" style="height:1.150%;top:37.830%;left:55.942%;width:3.200%"></div><div class="_" style="height:2.849%;top:42.429%;left:35.067%;width:29.867%"></div><div class="_" style="height:2.249%;top:95.610%;left:1.867%;width:4.000%"></div><div class="_" style="height:6.447%;top:93.403%;left:6.975%;width:6.400%"></div><div class="_" style="height:6.447%;top:93.403%;left:73.333%;width:26.667%"></div>
</div>
</transition>
....
</div>
</template>
<style>
._{
position:fixed
z-index:999;
<!--由于createSkeletonHTML方法中,background: 'red',里边不生效,粘贴代码时手动修改一下样式,否则是undefined,看不到颜色及效果-->
background:#efefef;
}
.__{
top:0%;
left:0%;
width:100%;
}
</style>
app.styl
// 骨架过渡
.skeleton-enter-active
transition opacity .6s
.skeleton-enter
opacity 0
.skeleton-leave-active
transition opacity .6s
.skeleton-leave-to
opacity 0
骨架是在页面没渲染完成,就是接口没返回之前展示,接口返回骨架隐藏。因此需要把mounted
里边生成骨架屏的代码进行注释,否则每次页面运行完毕都会自动生成骨架屏节点,自动插入页面,导致多份骨架屏重叠的现象,也因此对骨架屏节点进行v-if="showSkeleton"
控制:
src/commercialActivity/commercialActivity.js
data() {
return {
showSkeleton: true, // 是否显示骨架
}
}
methods: {
// 滚动下拉下拉加载
infinite(val) {
return _.debounce(() => {
// 请求接口的地方
this.getProductInfo({
activity_id: this.activityId,
page: this.listQery.page,
ser: val ? val.search : ''
}).then(() => {
// 接口返回骨架隐藏
this.showSkeleton = false;
if (!this.errorType.type) {
this.$refs.infiniteLoading.stateChanger.loaded();
} else {
this.$refs.infiniteLoading.stateChanger.complete();
}
if (this.hasMore) {
this.distance = -Infinity;
} else {
this.$refs.infiniteLoading.stateChanger.complete();
}
}).catch((err) => {
// 接口返回骨架隐藏
this.showSkeleton = false;
console.log(err);
});
if (this.isAllChecked) {
this.isAllChecked = true;
}
}, 500);
},
}
4、dps是把当前可视的页面生成骨架 dps设计的规则如下:
- 只遍历可见区域可见的 DOM 节点,包括:非隐藏元素、宽高大于 0 的元素、非透明元素、内容不是空格的元素、位于浏览窗口可见区域内的元素等;
- 针对(背景)图片、文字、表单项、音频视频、Canvas、自定义特征的块等区域来生成颜色块;
- 页面节点使用的样式不可控,所以不可取 style 的尺寸相关的值,可通过 getBoundingClientRect 获取节点宽、高、距离视口距离的绝对值,计算出与当前设备的宽高对应的百分比作为颜色块的单位,来适配不同设备
5、由于dps生成的页面骨架是运行放在页面节点中,而不是index.html中的首屏骨架,所以看到的交互是看到'白屏->页面骨架->正常页面
',实际dps只是单纯的生成页面的骨架的的代码。所以在首屏index.html
页面加入loading
的交互(具体看项目是否需要首屏loading的交互):
<body>
<div id="app">
<!-- 样式写在该地方是为了能够当页面过来了这个app下的所有内容都给替换掉 -->
<style>
#app {
height: 100%;
margin: 0px;
padding: 0px;
}
#loader-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: red;
-webkit-animation: spin 2s linear infinite;
-ms-animation: spin 2s linear infinite;
-moz-animation: spin 2s linear infinite;
-o-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
#loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: red;
-webkit-animation: spin 3s linear infinite;
-moz-animation: spin 3s linear infinite;
-o-animation: spin 3s linear infinite;
-ms-animation: spin 3s linear infinite;
animation: spin 3s linear infinite;
}
#loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: red;
-moz-animation: spin 1.5s linear infinite;
-o-animation: spin 1.5s linear infinite;
-ms-animation: spin 1.5s linear infinite;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
#loader-wrapper .fengche-logo {
display: block;
position: absolute;
left: 50%;
top: 50%;
width: 60px;
height: 60px;
margin: -30px 0 0 -30px;
}
#loader-wrapper .fengche-logo img {
width: 100%;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
<div id="loader-wrapper">
<div id="loader"></div>
<div class="fengche-logo">
<img src="data:image/gif;base64,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" />
</div>
</div>
</div>
<!-- built files will be auto injected -->
</body>
运行后,页面的交互为:首屏loading->页面骨架屏->数据请求回来后的页面正常ui交互。
6、当项目中使用的手淘lib-flexible
或者px进行了rem的处理
和使用一些ui框架
,例如vant
,那么使用dps的话,直接生成的%骨架屏,就不适用了,就需要自定义骨架屏,把%处理为rem
,这样页面的骨架屏ui交互才正常
mounted() {
setTimeout(() => {
/* 此举为自定义骨架屏,处理%转为rem,项目的根字体大小=37.5px */
createSkeletonHTML({}).then(skeletonHTML => {
// 最好自定义高度比例较大的,兼容各种较长的屏幕,例如375 * 1000 (iphoneX - 375 * 812)
const skeletonWidth = document.body.clientWidth // 屏幕宽度
const skeletonHeight = document.body.clientHeight // 屏幕高度
const skeletonTop = 0 // 骨架屏从哪截取(width:375px下top的px值,不包括header的44px)
let cacheHtml = JSON.parse(JSON.stringify(skeletonHTML))
cacheHtml = cacheHtml.replace(/<style>\S+<\/style>/, '') // 去掉style,提取到公共
// 下面代码是将百分比转换为rem:height(px) = val * clientHeight/100
cacheHtml = cacheHtml.replace(/height:\d*\.*\d*%/g, (val) => {
return `height:${(skeletonHeight * val.slice(7, -1) / 3750).toFixed(6)}rem`
})
cacheHtml = cacheHtml.replace(/top:\d*\.*\d*%/g, (val) => {
return `top:${(skeletonHeight * val.slice(4, -1) / 3750).toFixed(6)}rem`
})
cacheHtml = cacheHtml.replace(/left:\d*\.*\d*%/g, (val) => {
return `left:${(skeletonWidth * val.slice(5, -1) / 3750).toFixed(6)}rem`
})
cacheHtml = cacheHtml.replace(/width:\d*\.*\d*%/g, (val) => {
return `width:${(skeletonWidth * val.slice(6, -1) / 3750).toFixed(6)}rem`
})
console.log('首屏骨架:', cacheHtml)
// 骨架屏截取,去掉header44px(屏幕宽度375px下)+自定义的skeletonTop
const skeletonCutTop = (skeletonTop + 44) / 37.5
cacheHtml = cacheHtml.replace(/<div class="_" style="\S+<\/div>/g, (val) => {
let isCut = true
val = val.replace(/top:\d*\.*\d*rem/g, (topRem) => {
if (topRem.slice(4, -3) < skeletonCutTop) {
isCut = false
return topRem
}
return `top:${(topRem.slice(4, -3) - (44 / 37.5)).toFixed(6)}rem`
})
return isCut ? val : ''
})
// 骨架屏背景截取+下移
cacheHtml = cacheHtml.replace(/<div class="_ __" style="\S+<\/div>/g, (val) => {
return `<div class="_ __" style="height:${((skeletonHeight - skeletonTop) / 37.5).toFixed(6)}rem;top:${(skeletonTop / 37.5).toFixed(6)}rem;z-index:990;background:#fff"></div>`
})
// 微信环境下header隐藏的处理
cacheHtml = `<div class="skeleton-box"><div :class="[$store.getters.isWx||$store.getters.isApp?'':'not-wx','skeleton-content']">${cacheHtml}</div></div>`
console.log('页面骨架:', cacheHtml)
}).catch(e => {
console.error(e)
})
}, 8000)
}
注意事项1:
lib-flexible方案限制font-size最大54px;更好的兼容是改为跟项目的max-width:1280px一样;max-width:1280px应是固定不应转rem,px改为大写PX
。
function refreshRem(){
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 1280) {
width = 1280 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
注意事项2:
顶部类似搜索框的vant组件可不用骨架遮挡
const skeletonTop = 48 // 骨架屏从哪截取(width:375px下不需截取的height,不包括header的44px)
注意事项3:
vant组件也需要对应转rem,postcss.config.js
改为:
module.exports = ({ file }) => {
let remUnit
if (file && file.dirname && file.dirname.indexOf('vant') > -1) {
remUnit = 37.5
} else {
remUnit = 75
}
return {
plugins: {
'postcss-px2rem-exclude': {
remUnit,
}
}
}
}
注意事项4:
一般静态页面是不加骨架屏的,因为没有接口请求,本身加载速度很快,没有必要骨架屏,而且骨架屏也是个静态,只是为了有请求的交互体验提高!