工作中遇到一种场景,一个页面要加载多个组件,而且组件中逻辑较为复杂或者元素很多,占用了较多渲染或逻辑运算时间,给用户造成较长时间的白屏,这时候可以用到延迟装载的方式,让组件挨个加载.
虽然总的渲染时间没变,甚至更长一些,但是对用户更加友好.
延迟装载其实就是利用了浏览器的 requestAnimationFrame() 然后累计帧数,通过判断当前累计帧数和期望渲染帧数是否符合,再用v-if进行控制组件的装载
requestAnimationFrame() 告诉浏览器——你希望执行一个函数,并且要求浏览器在下次重绘之前调用指定的回调函数。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用 requestAnimationFrame()。requestAnimationFrame() 是一次性的。
这里使用了mixin(混入)实现延迟装载的功能,mixin代码如下
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
mixin.js
/**
* @param {Number} maxFrameCount最大帧数,浏览器渲染总帧数超过此变量时,就不在运行
* @returns
*/
export default function(maxFrameCount){
return {
data(){
return {
//当前累计帧数
frameCount: 0
};
},
mounted(){
// 每次渲染都累加帧数,当累加帧数超过设定的最大帧数后,就不在调用了
const refreshFrameCount = () => {
requestAnimationFrame(()=>{
console.log("帧数",this.frameCount)
this.frameCount++;
if(this.frameCount < maxFrameCount){
refreshFrameCount();
}
})
}
refreshFrameCount();
},
methods:{
/**
* 判断是否达到了指定的渲染帧数
* 当前累计帧数大于等于设定帧时,就返回true
* @param {Number} showInFrameCount 指定帧数
* @returns
*/
isShow(showInFrameCount){
return this.frameCount >= showInFrameCount;
}
}
};
}
再创建一个渲染耗时的组件,用来测试效果
这个组件没啥好说的,就是接受一个count,然后里面渲染对应数量的小div,用来增加渲染时间
ItemCom.vue
<template>
<div class="box">
<div class="item" v-for="(item,index) in count" :key="index"></div>
</div>
</template>
<script>
export default {
name:'ItemCom',
props:{
count:{
type: Number,
default: 100
}
},
data(){
return {
};
}
}
</script>
<style scoped>
.box{
border: 1px solid pink;
padding: 5px;
width: 300px;
min-height: 300px;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.item{
width: 10px;
height: 5px;
background-color: #367fd3;
margin: 3px;
}
</style>
然后是调用
FunctionCom这个组件是我测试函数式组件用的,可以忽略不管
函数式组件没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。
实际上,它只是一个接受一些 prop 的函数。在这样的场景下,我们可以将组件标记为
functional
,这意味它无状态 (没有响应式数据),也没有实例 (没有this
上下文)。
home.vue
<template>
<div class="hello">
<span>{{msg}}</span>
<function-com msg="大家好"></function-com>
<div class="box">
<!-- isShow 就是mixin中的isShow 用来判断当前累计帧数是否达到了预定的渲染帧数 -->
<item-com :count="800" v-for="(item,index) in 4" :key="index" v-if="isShow(index*10)"></item-com>
</div>
</div>
</template>
<script>
// 引入组件
import ItemCom from './ItemCom';
//这个函数式组件可以不引入,与此文章无关
//import FunctionCom from './FunctionCom'
// 引入mixin
import mix from '../mixin/mixin'
export default {
name: 'HelloWorld',
// 注册组件
components:{ItemCom},//,FunctionCom
// 注册mixin
// mix(XX)就是设定最多处理多少帧,超过就不在累计了
mixins: [mix(500)],
data () {
return {
msg: '我是标题'
}
},
methods:{
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
a {
color: #42b983;
}
.box{
display:flex;
flex-wrap: wrap;
}
</style>
运行后的效果就是组件一个个肉眼可见的渲染出来