一、前言
标题中写的“vue 项目...“,虽然确实是在vue项目中写的,但其实这篇文章主要讲的是一个思路,无论是不是在vue中,都是这个道理。
二、当前情况简单介绍
本文的懒加载是针对如下情况:通过调用接口,获取当页的用户信息(包括用户头像)。但这个时候数据中的头像不能直接通过<img>展示出来,而是需要先调用接口转为base64 ,例如:<img src=""/> 。这个时候有10条用户信息,就需要调用接口读取图片10次,等10次接口调完,赋值再展示太慢了(例如下面的动图),这时候就需要懒加载了。
以下是上面动图的关键代码,下面的优化将在此基础上进行修改。
子组件部分(内层加载出来的卡片):
用户信息通过doctorData数组传进来,循环进行展示。
<template>
<div class="cons-doctor" v-for="(item,index) in doctorData" :key="index">
<div class="avatar">
<img v-if="item.userPhoto" class="itemImg" :src="item.userPhoto">
<!--没有图片默认的图片 -->
<img v-else src="~@/assets/images/doctor_lg.png" />
</div>
<div class="info">
<p>
<span class="txt"> 姓名:{{item.userName}}</span>
</p>
...
</div>
</div>
</template>
<script>
export default {
props:{
doctorData: {
type: Array,
default: []
},
}
}
</script>
父组件部分(包含用户信息的那部分框框):
这部分主要是获取到用户信息,例如[{userName: "哲学家", userPhoto: "photo\2021-03-02\7b48de3b-8e9c-4500-80a3-31d489567d55.jpg"}],把所有用户信息里面的userPhoto转为base64,再赋值给doctorData。然后子组件拿到doctorData,渲染出一个个用户信息卡片。
<template>
<div>
...
<doctor-wrap :doctorData="doctorData"/></doctor-wrap>
...
</div
</template>
<script>
import doctorWrap from "@/components/consultant/doctorWrap"
import {getUserPages, getFileRead} from "@/api/api.js";
export default {
name: 'index',
components: {
doctorWrap,
},
data() {
return {
doctorData: [], //医生列表数据
totalCount: 0,
pageSize: 10,
currentPage: 1,
querylist:{
hospital:''
},
dataLoading: false,
}
},
methods:{
//获取用户信息
getUserInfo(){
let data = {
hosptalCode: this.querylist.hospital,
pageSize: this.pageSize, //页面大小
pageNumber: this.currentPage, //页码
}
this.dataLoading = true
// 调获取用户信息接口
getUserPages(data).then(res=>{
this.dataLoading = false
if(res && res.code == 10000){
this.totalCount = res.content.totalCount
//对获取到的用户信息进行图片处理
this.getImgDataList(res.content.list).then(data=>{
//获取到处理后的数据
this.doctorData = data
})
}
})
},
// 获取图片加载后的数据
getImgDataList(dataArr){
return new Promise(async resolve=>{
let _dataArr = [...dataArr]
if(_dataArr.length===0){
resolve(_dataArr)
}
let newDataArr = []
for(let i = 0; i < _dataArr.length; i++){
let tempObj = {..._dataArr[i]}
if(!_dataArr[i].userPhoto){
tempObj.userPhoto = ''
}else{
//获取base64
tempObj.userPhoto = await this.getImgConverted(_dataArr[i].userPhoto)
}
newDataArr.push(tempObj)
}
resolve(newDataArr)
})
},
//调接口将图片信息转为base64
getImgConverted(fileUrl){
return new Promise(async (resolve) => {
let base64Url = ''
await getFileRead(fileUrl).then(res => {
base64Url = res
});
resolve(base64Url)
})
},
}
}
</script>
三、懒加载思路和实现
上述的代码如上图所示,白屏时间过长。用户信息已经获取,还要等所有头像数据加载好,再一起展示在页面上。现在只有10条,如果有更多数据怎么办?
能不能等先设置一个默认的头像,让用户信息跟默认头像一起显示出来,然后头像加载完以后,再重新渲染一遍头像?可以!
这样就进入了优化的步骤一:
由于上面的子组件对图片进行判断的时候写的是userPhoto存在就显示图片,不存在就显示默认图片。那我们调用完用户信息接口的时候,直接给userPhoto都赋值空字符,让其显示默认图片,然后先把这个数据赋值给doctorData。这样界面就会先显示默认的图片。在调用完图片获取接口以后再赋值一次doctorData。这样界面就把有图片的数据渲染出来了。
这部分需要改动的代码(在上面代码的基础上):
//获取用户信息
getUserInfo(){
let data = {
hosptalCode: this.querylist.hospital,
pageSize: this.pageSize, //页面大小
pageNumber: this.currentPage, //页码
}
this.dataLoading = true
getUserPages(data).then(res=>{
this.dataLoading = false
if(res && res.code == 10000){
this.totalCount = res.content.totalCount
/**这部分是新增代码**/
let _res = JSON.parse(JSON.stringify(res.content.list))
let defaultData = _res.map(item => {
item.userPhoto =''
return item
})
// 先赋值一次
this.doctorData = defaultData
/**这部分是新增代码**/
//对获取到的用户信息进行图片处理
this.getImgDataList(res.content.list).then(data=>{
//获取到处理后的数据
this.doctorData = data
})
}
})
},
效果如下图。
上述代码的修改,用户信息加载完以后就能立刻显示在页面上了,已经能解决页面白屏时间过长的问题了。但是,能不能再优化一下,让页面获取到一个图片就加载一个图片,而不是等所有图片加载出来一起替换默认图片?当然可以。
这样就进入了优化的步骤二:
页面怎么显示,归根结底就是要对doctorData动手脚,所以只要在每次调用获取图片接口的时候改变doctorData相应索引位置的数据就行了!
这部分需要改动的代码(在上面修改代码的基础上):
//获取用户信息
getUserInfo(){
let data = {
hosptalCode: this.querylist.hospital,
pageSize: this.pageSize, //页面大小
pageNumber: this.currentPage, //页码
}
this.dataLoading = true
getUserPages(data).then(res=>{
this.dataLoading = false
if(res && res.code == 10000){
this.totalCount = res.content.totalCount
let _res = JSON.parse(JSON.stringify(res.content.list))
let defaultData = _res.map(item => {
item.userPhoto =''
return item
})
this.doctorData = defaultData
//对获取到的用户信息进行图片处理
this.getImgDataList(res.content.list).then(data=>{
//获取到处理后的数据
// this.doctorData = data //此处注释
})
}
})
},
// 获取图片加载后的数据
getImgDataList(dataArr){
return new Promise(async resolve=>{
let _dataArr = [...dataArr]
// if(_dataArr.length===0){ //此处注释
// resolve(_dataArr) //此处注释
// } //此处注释
// let newDataArr = [] //此处注释
for(let i = 0; i < _dataArr.length; i++){
// let tempObj = {..._dataArr[i]} //此处注释
if(!_dataArr[i].userPhoto){
// tempObj.userPhoto = '' //此处注释
this.doctorData[i].userPhoto = '' //此处新增
}else{
//获取base64
// tempObj.userPhoto = await this.getImgConverted(_dataArr[i].userPhoto) //此处注释
this.doctorData[i].userPhoto = await this.getFileRead(_DataArr[i].userPhoto) //图片逐个加载 此处新增
}
// newDataArr.push(tempObj) //此处注释
}
// resolve(newDataArr) //此处注释
})
},
最终效果如下图。
大功告成!