一、前言

标题中写的“vue 项目...“,虽然确实是在vue项目中写的,但其实这篇文章主要讲的是一个思路,无论是不是在vue中,都是这个道理。

二、当前情况简单介绍

本文的懒加载是针对如下情况:通过调用接口,获取当页的用户信息(包括用户头像)。但这个时候数据中的头像不能直接通过<img>展示出来,而是需要先调用接口转为base64 ,例如:<img src=""/> 。这个时候有10条用户信息,就需要调用接口读取图片10次,等10次接口调完,赋值再展示太慢了(例如下面的动图),这时候就需要懒加载了。

Element Plus 图片异步加载_数据

 

以下是上面动图的关键代码,下面的优化将在此基础上进行修改。

子组件部分(内层加载出来的卡片):

用户信息通过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 
          })
        }
      })
    },

效果如下图。

Element Plus 图片异步加载_数据_02

 

上述代码的修改,用户信息加载完以后就能立刻显示在页面上了,已经能解决页面白屏时间过长的问题了。但是,能不能再优化一下,让页面获取到一个图片就加载一个图片,而不是等所有图片加载出来一起替换默认图片?当然可以。

这样就进入了优化的步骤二:

页面怎么显示,归根结底就是要对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) //此处注释
      })
    },

最终效果如下图。

Element Plus 图片异步加载_用户信息_03

大功告成!