el-upload实现可替换、删除、预览的图、音、视(可切片)上传。js 往返缓存(可判断当前页面是不是返回的页面)

el-upload实现可替换、删除、预览的图片上传

在这里插入图片描述

  • 组件使用:
<template>
  <div>
    <UploadImage sendUrl='' :limit="1" :size="size" :gifSize="gifSize" v-model="images"></UploadImage> 
  </div>
</template>
<script>
  import Vue from 'vue'
  import UploadImage from './UploadImage.vue'
  export default {
      components: {
        UploadImage
      },
      data(){
        retun {
          images:'',
          size: 400 * 1024,
          ifSize: 1 * 1024 * 1024,
          // images:[]
        }
      }
  }
</script>
  • UploadImage.vue 组件:
<template>
  <div class="uploadFileMain" :style="{'--height':`${height}px`,'--width':`${width}px`}">
    <div v-for="(item,index) in fileList" :key="index" class="upload-list" :class="[limit==1?'upload-lis1':'']">
       <template  v-if="switchIndex == index && showProgress" >
          <div class="percentMain" v-if="limit == 1 && getAcceptArray.includes('video')">
           <el-progress type="circle" v-if="!percentNumLoading" :format="format" :width="Math.min(width,height)*0.8"  v-bind="percentNum==100?{status:'success'}:{}" :percentage="percentNum" ></el-progress>
           <div v-else v-loading="true" :style="{ 'height': `${height}px`, 'width': `${width}px` }" class="loadingView" element-loading-background="rgba(0, 0, 0, 0.8)" />
         </div>
          <div v-else v-loading="true" :style="{ 'height': `${height}px`, 'width': `${width}px` }" class="loadingView" element-loading-background="rgba(0, 0, 0, 0.8)" />
       </template>
       <template v-if="getAcceptArray.includes('audio')">
          <div class="txtMp"><span>{{ getName(item.url).nametxt1 }}</span>{{ getName(item.url).nametxt2 }}.{{ getName(item.url).ext }}</div>
       </template>
       <img v-else :id="`${idName}_image_${index}`" class="el-upload-listImg" :src="getShowImage(item.url, `${idName}_image_${index}`)"  alt="">
       <span class="el-actions" :class="[!showProgress && 'actionsHover']">
        <span
            v-if="getAcceptArray.includes('video')"
            class="el-upload-icon"
            @click="handlePlayVideo(index)"
          >
            <i v-if="audioObject[index] && audioObject[index].play" class="el-icon-video-pause" />
            <i v-else class="el-icon-video-play" />

        </span>
        <span
            v-else-if="getAcceptArray.includes('audio')"
            class="el-upload-icon"
            @click="handlePlayMp(index)"
          >
            <i v-if="audioObject[index] && audioObject[index].play" class="el-icon-video-pause" />
            <i v-else class="el-icon-video-play" />

        </span>
        <span
          v-else
          class="el-upload-icon"
          @click="handlePictureCardPreview(index,item.url)"
        >
          <i class="el-icon-zoom-in" />
        </span>
        <span
          class="el-upload-icon"
          @click="switchFn(index)"
        >
          <i class="el-icon-sort" style="transform: rotate(90deg);" />
        </span>
        <span
          class="el-upload-icon"
          @click="delRemove(index)"
        >
          <i class="el-icon-delete" />
        </span>
      </span>
    </div>
    <el-upload
      :class="[showProgress && 'noClick']"
      v-show="!limit || fileList.length<limit"
      :ref="`${idName}_upload`"
      :show-file-list="false"
      :multiple="multiple"
      :limit="limit?limit+1:limit"
      :action="sendUrl"
      list-type="picture-card"
      :headers="{Authorization: $utils.getToken()}"
      :accept="acceptArray.length>0?acceptArray.map(n=>this.acceptType[n]).join(',') :'*'"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-progress="progressFn"
      :on-success="uploadSuccess"
    >
       <template v-if="switchIndex == -1 && showProgress" >
         <div class="percentMain" v-if="limit == 1 && getAcceptArray.includes('video')">
          <el-progress type="circle" v-if="!percentNumLoading" :format="format" :width="Math.min(width,height)*0.8"  v-bind="percentNum==100?{status:'success'}:{}" :percentage="percentNum" ></el-progress>
          <div v-else v-loading="true" :style="{ 'height': `${height}px`, 'width': `${width}px` }" class="loadingView" element-loading-background="rgba(0, 0, 0, 0.8)" />
        </div>
         <div v-else v-loading="true" :style="{ 'height': `${height}px`, 'width': `${width}px` }" class="loadingView" element-loading-background="rgba(0, 0, 0, 0.8)" />
       </template>
      <div v-else  class="uploadClick" :id="`${idName}_uploadClick`" slot="trigger" @click="changeIndex(-1)">
        <i class="el-icon-plus"  />
      </div>
    </el-upload>
  </div>
</template>
<script>
import Vue from 'vue'
import axios from 'axios'
export default {
    props: {
      // 上传地址
      sendUrl: {
        type: String,
        default: ''
      },
      value: {
        type: [Array, String],
        default: ''
      },
      width: {
        type: [Number, String],
        default: 80
      },
      height: {
        type: [Number, String],
        default: 80
      },
      multiple: {
        type: Boolean,
        default: true
      },
      limit: {
        type: Number,
        default: null
        // default: 4
      },
      // 大小限制:10 * 1024 * 1024 = 10MB
      size: {
        type: Number,
        default: -1
      },
      //   限制类型,按照acceptType数组里面来
      acceptArray: {
        type: Array,
        default: () => {
          return ['png', 'jpg', 'jpeg', 'gif']
        }
      },
      gifSize: {
        type: Number,
        default: -1
      },
      // 分片上传
	  isSharding: {
	      type: Boolean,
	      default: false
	  },
	  // 切片大小
	  chunkSize: {
	      type: Number,
	      default: 5 * 1024 * 1024
	  },
	  // 合并地址
	  mergeUrl: {
	      type: String,
	      default: ``
	  }
    },
    data() {
      return {
         audioObject: {

         },
        acceptType: {
          'doc': 'application/msword',
          'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
          'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
          'xls': 'application/vnd.ms-excel',
          'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
          'pdf': 'application/pdf',
          'csv': '.csv',
          'txt': 'text/plain',
          'image': 'image/*',
          'png': 'image/png',
          'gif': 'image/gif',
          'jpg': 'image/jpg',
          'jpeg': 'image/jpeg',
          'audio': 'audio/*',
          'mp3': 'audio/mpeg',
          'video': 'video/*'
        },
        idName: `${Math.random().toString(36).slice(-8)}_${new Date().getTime()}`,
        switchIndex: -1,
        fileList: [],
        dataArray: {
          1: [],
          2: []
        },
        showProgress: false,
        percentNum: 0,
        percentNumLoading: false
      }
    },
    computed: {
	    getAcceptArray() {
	      const dataArray = this.acceptArray.map(n => this.acceptType[n]).filter(n => n)
	      if (this.acceptArray.indexOf('video') != -1) {
	        dataArray.push('video/*')
	      }
	      return dataArray.join(',')
	    }
	},
    watch: {
      value: {
        handler(value) {
          if (Array.isArray(value)) {
            this.fileList = value.filter(n => n != '').map(n => {
              return {
                url: n
              }
            })
          } else {
            this.fileList = value.split(',').filter(n => n != '').map(n => {
              return {
                url: n
              }
            })
          }
        },
        deep: true,
        immediate: true
      },
      fileList: {
        handler(value) {
          if (value.length > 0) {
            this.toFileImg(value)
          }
        },
        deep: true,
        immediate: true
      },
      dataArray: {
        handler(value) {
          // 1 和 2 相等表示这次上传成功的数量相同,会添加到数组里面
          if (value[1].length != 0 && value[2].length != 0 && value[1].length == value[2].length) {
            value[2].forEach(e => {
            	//成功的加进去
               	if (e.code == 0) {
	              // 有更换就变化
	              if (this.switchIndex != -1) {
	                this.fileList.splice(this.switchIndex, 1, {
	                  status: 'success',
	                  url: e.data
	                })
	              } else {
	              	// 添加时判断是否超出校址
	               if ((this.limit && this.fileList.length < this.limit) || !this.limit) {
	                  // 没有更好追加
	                  this.fileList.push({
	                    status: 'success',
	                    url: e.data
	                  })
	                }
	              }
	            } else {
	              this.$message.error(e.message || '上传失败')
	            }
            })
	        if (this.switchIndex != -1) {
	            setTimeout(() => {
	              this.switchIndex = -1
	              this.showProgress = false
	            }, 300)
	        } else {
	            this.showProgress = false
	            setTimeout(() => {
	              this.switchIndex = -1
	            }, 300)
	        }
            this.dataArray = {
              1: [],
              2: []
            }
        	// 清除组件上传类别
      		this.$refs[`${this.idName}_upload`].clearFiles()
          }
        },
        deep: true
      }
    },
    methods: {
      async lookImageViewer(url, list) {
        let listImg = list
        const thisIndex = list.indexOf(url)
        const firstArray = list.slice(thisIndex, list.length)
        const twoArray = list.slice(0, thisIndex)
        listImg = [...firstArray, ...twoArray]
        // this.$viewerApi({ images: listImg })//v-viewer组件
        const id = 'MyElImageViewer_' + new Date().getTime() + '_' + parseInt(Math.random() * 1000)
        // 引用组件(找到Elementui中image-viewer的位置)
        const ElImageViewer = (await import('element-ui/packages/image/src/image-viewer')).default
        const MyElImageViewer = Vue.component('MyElImageViewer', ElImageViewer)
        const MyElImageViewerNew = new MyElImageViewer({
          propsData: {
            urlList: listImg,
            onClose: () => {
              // 删除组件
              compDOm.$destroy()
              document.body.removeChild(document.getElementById(id))
            }
          }
        })
        const DOM = document.createElement('div')
        DOM.setAttribute('id', id)
        DOM.setAttribute('class', 'imageSwipeViewer_Show')
        document.body.appendChild(DOM)
        // 挂载组件
        const compDOm = MyElImageViewerNew.$mount(DOM.appendChild(document.createElement('template')))
        compDOm.$nextTick(() => {
          const showDom = document.getElementById(id)
          showDom.querySelector('.el-icon-circle-close').style = 'font-size:38px;color:#fff'
        })
      },
	  getName(url) {
	      let name = url.split('/').at(-1)
	      name = name.split('?')[0]
	      const nametxt = name.split('.')[0]
	      const ext = name.split('.')[1]
	      return {
	        nametxt1: nametxt.slice(0, nametxt.length - 3),
	        nametxt2: nametxt.slice(nametxt.length - 3, nametxt.length),
	        ext
	      }
	    },
      filterSize(size) {
        const pow1024 = (num) => {
          return Math.pow(1024, num)
        }
        if (!size) return ''
        if (size < pow1024(1)) return size + ' B'
        if (size < pow1024(2)) return (size / pow1024(1)).toFixed(0) + ' KB'
        if (size < pow1024(3)) return (size / pow1024(2)).toFixed(0) + ' MB'
        if (size < pow1024(4)) return (size / pow1024(3)).toFixed(0) + ' GB'
        return (size / pow1024(4)).toFixed(2) + ' TB'
      },
      // 上传之前放到1
      beforeUpload(e) {
        const fileSize = e.size
        if (this.gifSize > 0) {
	        if (e.type.indexOf('gif') != -1) {
	          if (fileSize > this.gifSize) {
	            this.$message.error(`gif最大上传${this.filterSize(this.gifSize)}`)
	            return false
	          }
	        } else {
	          if (this.size > 0 && fileSize > this.size) {
	            this.$message.error(`最大上传${this.filterSize(this.size)}`)
	            return false
	          }
	        }
	      } else {
	        if (this.size > 0 && fileSize > this.size) {
	          this.$message.error(`最大上传${this.filterSize(this.size)}`)
	          return false
	        }
	    }
        this.dataArray[1].push({
          status: 'uploading',
          ...e
        })
        return true
      },
      // 通过 slot="trigger" ,区分模拟点击,表示这次时人为点击的
      changeIndex(index) {
        if (index == -1) {
          this.switchIndex = -1
        }
      },
      progressFn(e) {
	      if (this.limit == 1 && this.getAcceptArray.includes('video')) {
	        this.percentNum = e.percent
	        if (this.percentNum < 100) {
	          this.percentNumLoading = false
	        } else {
	          setTimeout(() => {
	            this.percentNumLoading = true
	          }, 500)
	        }
	      }
      	this.showProgress = true
      },
      format(percentage) {
	     return Math.ceil(percentage) + '%'
	   },
      // 更换图片,模拟点击
      switchFn(index) {
        document.getElementById(`${this.idName}_uploadClick`).click(this.switchIndex)
        setTimeout(() => {
          this.switchIndex = index
        }, 0)
      },
      // 查看图片
      handlePictureCardPreview(index, url) {
        this.lookImageViewer(url, [url])
      },
     // 进入全屏
	  fullScreen(ele) {
	      if (ele.requestFullscreen) {
	        ele.requestFullscreen()
	      } else if (ele.mozRequestFullScreen) {
	        ele.mozRequestFullScreen()
	      } else if (ele.webkitRequestFullScreen) {
	        ele.webkitRequestFullScreen()
	      }
	  },
	  // 退出全屏
	  exitFullscreen() {
	      const de = document
	      if (de.exitFullscreen) {
	        de.exitFullscreen()
	      } else if (de.mozCancelFullScreen) {
	        de.mozCancelFullScreen()
	      } else if (de.webkitCancelFullScreen) {
	        de.webkitCancelFullScreen()
	      }
	  },
	  // 视频播放
	  handlePlayVideo(index) {
	      const videoDom = document.createElement('video')
	      videoDom.setAttribute('src', this.fileList[index].url)
	      videoDom.setAttribute('style', 'position: fixed;top: -200vh;')
	      document.body.appendChild(videoDom)
	      // 获取全屏相关方法
	      const getChangeFullscreen = () => {
	        const checkIsFullScreen = () => {
	          var isFullScreen = document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen
	          return isFullScreen == undefined ? false : isFullScreen
	        }
	        const fullscreenEvents = [
	          ['fullscreenchange', 'requestFullscreen', 'exitFullscreen'],
	          ['webkitfullscreenchange', 'webkitRequestFullScreen', 'webkitCancelFullScreen'],
	          ['mozfullscreenchange', 'mozRequestFullScreen', 'mozCancelFullScreen']
	        ]
	        for (let x = 0; x < fullscreenEvents.length; x++) {
	          if (document[fullscreenEvents[x][2]]) {
	            return { fullscreenEventsName: fullscreenEvents[x][0], fullScreenName: fullscreenEvents[x][1], exitFullscreenName: fullscreenEvents[x][2], checkIsFullScreen }
	          }
	        }
	      }
	      const { fullscreenEventsName, fullScreenName, exitFullscreenName, checkIsFullScreen } = getChangeFullscreen()
	      setTimeout(() => {
	        videoDom[fullScreenName]()
	      }, 0)
	      document.addEventListener(fullscreenEventsName, () => {
	        if (checkIsFullScreen()) {
	          videoDom.play()
	        } else {
	          document[exitFullscreenName]()
	          videoDom.pause()
	          setTimeout(() => {
	            videoDom.parentNode.removeChild(videoDom)
	          }, 0)
	        }
	      })
	  },
      // 音频播放
      handlePlayMp(index) {
      const _this = this
      if (!this.audioObject[index]) {
        const thisObje = {}
        thisObje[index] = {
          audio: new Audio(this.fileList[index].url),
          play: false
        }
        this.audioObject = { ...this.audioObject, ...thisObje }
        const play = () => {
          _this.$set(_this.audioObject[index], 'play', true)
        }
        const stop = () => {
          _this.$set(_this.audioObject[index], 'play', false)
        }
        this.audioObject[index].audio.addEventListener('play', play, true)
        this.audioObject[index].audio.addEventListener('pause', stop, true)
        this.audioObject[index].audio.addEventListener('ended', stop, true)
        this.audioObject[index].audio.play()
      } else {
        if (this.audioObject[index].play) {
          this.audioObject[index].audio.pause()
        } else {
          this.audioObject[index].audio.play()
        }
      }
    },
      // 成功后放到2
      uploadSuccess(e) {
        this.dataArray[2].push({
          ...e
        })
      },
      // 传递图片
      toFileImg(value) {
        if (Array.isArray(this.value)) {
          this.$emit('input', value.map(n => n.url))
        } else {
          this.$emit('input', value.map(n => n.url).join(','))
        }
      },
      delRemove(index) {
        this.fileList.splice(index, 1)
        if (this.fileList.length == 0) {
          this.toFileImg(this.fileList)
        }
      },
	  getVideoImg(url, time = 0) {
	      return new Promise((r, j) => {
	        const video = document.createElement('video') // 创建video对象
	        video.src = url // url地址
	        const canvas = document.createElement('canvas') // 创建 canvas 对象
	        const ctx = canvas.getContext('2d') // 绘制2d
	        video.crossOrigin = 'anonymous' // 解决跨域问题,也就是提示污染资源无法转换视频
	        video.currentTime = 1 // 第一秒帧
	        video.oncanplay = () => {
	          canvas.width = video.videoWidth
	          canvas.height = video.videoHeight
	          // 利用canvas对象方法绘图
	          ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
	          r(canvas.toDataURL('image/png'))
	        }
	      })
	  },
	  getShowImage(url, id) {
	      const ext = url.split('.').at(-1)
	      if (ext.indexOf('mp4') != -1 || ext.indexOf('avi') != -1) {
	        this.getVideoImg(url).then(res => {
	          if (document.getElementById(id)) {
	            document.getElementById(id).src = res
	          }
	        })
	         if (document.getElementById(id)) {
	           return document.getElementById(id).src
	         }
	      } else {
	        return url
	      }
	  },
	  // 上传图片
	  uploadFile(e) {
	      // 整个上传
	      // const uploadAll = (e) => {
	      //   const formData = new FormData()
	      //   for (const key in e.data) {
	      //     formData.append(key, e.data[key])
	      //   }
	      //   formData.append(e.filename, e.file)
	      //   return new Promise((resolve) => {
	      //     axios({
	      //       method: 'post',
	      //       headers: {
	      //         'Content-Type': 'multipart/form-data',
	      //         ...e.headers
	      //       },
	      //       url: e.action,
	      //       data: formData,
	      //       onUploadProgress: progressEvent => {
	      //         e.onProgress({ percent: Number((progressEvent.loaded / progressEvent.total * 100).toFixed(1)) })
	      //       }
	      //     }).then((res) => {
	      //       if (res.data.code == 200) {
	      //         e.onSuccess(res.data, e.file, this.fileList)
	      //       } else {
	      //         e.onError({ message: '上传失败' }, e.file, this.fileList)
	      //       }
	      //     }).catch(() => {
	      //       e.onError({ message: '上传失败' }, e.file, this.fileList)
	      //     })
	      //   })
	      // }
	      // uploadAll(e)
	
	      // 切片上传
	      const uploadLarge = async(e) => {
			// 分片上传
	        let percent = 0
	        const uploadChunk = (formData) => {
	          // 分片上传方法
	          return axios({
	            method: 'post',
	            headers: {
	              'Content-Type': 'multipart/form-data',
	              ...e.headers
	            },
	            url: e.action,
	            data: formData,
	            onUploadProgress: progressEvent => {
	              // 上传进度
	              const largeBili = 1 / chunkCount
	              const thisprogress = Number((progressEvent.loaded / progressEvent.total * 100))
	              if (thisprogress >= 100) {
	                percent = percent + thisprogress * largeBili
	              }
	              const allprogress = Number((percent + thisprogress * largeBili).toFixed(1))
	              e.onProgress({ percent: allprogress >= 100 ? allprogress : allprogress })
	            }
	          })
	        }
	        // 上传过程中用到的变量
	        const largeChunkSize = this.chunkSize
	        const chunkCount = Math.ceil(e.file.size / largeChunkSize) // 总片数
	        // 获取当前切片数据
	        const getChunkInfo = (file, index) => {
	          const start = index * largeChunkSize
	          const end = Math.min(file.size, start + largeChunkSize)
	          const chunkFile = file.slice(start, end)
	          const chunkSize = chunkSize
	          return { start, end, chunkFile }
	        }
	        // 针对单个切片上传封装,进行chunk上传
	        const readChunk = (index, uploadId = null, objectKey = null) => {
	          const { chunkFile } = getChunkInfo(e.file, index)
	          const formData = new FormData()
	          uploadId && formData.append('uploadId', uploadId)
	          objectKey && formData.append('objectKey', objectKey)
	          formData.append('partNumber', index + 1)
	          formData.append('sliceFile', chunkFile)
	          formData.append('filename', e.file.name)
	          for (const key in e.data) {
	            formData.append(key, e.data[key])
	          }
	          return uploadChunk(formData, index)
	        }
	        
	        // 开始上传加整合
	        let uploadId = null
	        let objectKey = null
	        const promiseList = []
	        // 示例时需要先请求第一张切片获取,uploadId 、objectKey (可自行修改)
	        const { data: firstData } = await readChunk(0)
	        console.log(firstData)
	        const parts = []
	        parts.push({
	          partNumber: firstData.data.partNumber,
	          etag: firstData.data.etag
	        })
	        if (firstData.code === 200) {
	          uploadId = firstData.data.uploadId
	          objectKey = firstData.data.objectKey
	          // 整体监听
	          for (let index = 1; index < chunkCount; ++index) {
	            promiseList.push(await readChunk(index, uploadId, objectKey))
	          }
	          const allRes = await Promise.all(promiseList)
	          allRes.forEach((item, index) => {
	            parts.push({
	              partNumber: item.data.data.partNumber,
	              etag: item.data.data.etag
	            })
	          })
	          const params = {
	            uploadId: uploadId,
	            objectKey: objectKey,
	            parts
	          }
	          // 全部上传完,合并切片
	          axios.post(this.mergeUrl, params)
	            .then(res => {
	              e.onSuccess(res.data, e.file, this.fileList)
	            })
	            .catch(() => {
	              e.onError({ message: '上传失败' }, e.file, this.fileList)
	            })
	        } else {
	          e.onError({ message: '上传失败' }, e.file, this.fileList)
	        }
	      }
	      uploadLarge(e)
	   }
    }
  }
</script>
<style lang="scss" scoped>
  ::v-deep .el-upload--picture-card{
    width: var(--width);
    display:flex;
    justify-content:center;
    align-items:center;
    height: var(--height);
  }
  .uploadClick{
    width: var(--width);
    display:flex;
    justify-content:center;
    align-items:center;
    height: var(--height);
  }
  ::v-deep .el-upload-list--picture-card .el-upload-list__item{
    width: var(--width);
    display:flex;
    align-items:center;
    height: var(--height);
    transition: none !important;
  }
  ::v-deep .el-upload-list__item .el-icon-check{
    position: absolute;
    margin-top: 0px;
    top: 10px;
    right: 14px;
  }
  ::v-deep .el-loading-spinner{
    width: 100%;
    height: 100%;
    top: 0;
    margin-top: 0;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  ::v-deep .el-upload-list,::v-deep .el-upload-list--picture-card{
    //display: none;
  }
  .uploadFileMain{
    display: flex;
    flex-wrap: wrap;
    .upload-list{
      flex-shrink:0;
      width: var(--width);
      border:1px solid #0000005d;
      box-sizing: border-box;
      height: var(--height);
      margin-right: 20px;
      margin-bottom: 10px;
      &.upload-lis1{
        margin-bottom: 0px;
      }
      overflow: hidden;
      border-radius: 8px;
      position: relative;
      .el-actions{
        position: absolute;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        background-color: rgba(0,0,0,0.5);
        align-items: center;
        justify-content: center;
        display: none;
        .el-upload-icon{
          margin: 5px;
          i{
            color: #ffffff;
            cursor: pointer;
          }
        }
      }
      &:hover{
        .actionsHover{
          display: flex;
        }
      }
    }
    .el-upload-listImg{
      width: 100%;
      height: 100%;
      object-fit: contain;
    }
  }
  .loadingView{
    width: 100%;
    height: 100%;
  }
  .percentMain{
	  width: 100%;
	  height: 100%;
	  position: relative;
	  z-index:99;
	  display: flex;
	  align-items: center;
	  justify-content: center;
	  background-color: rgba(0,0,0,0.5);
}
  .txtMp{
  width: 100%;
  display: flex;
  justify-content: center;
  span{
    width: 50%;
    display: inline-block;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
}
.noClick{
  pointer-events: none;
}
</style>

往返缓存(BFCache)

BFCache是一种浏览器优化,可实现即时前进和后退载入页面。它改善了用户的浏览体验,尤其是那些网络或设备速度较慢的用户。*我们可以通过这个方法判断当前页面是不是返回的页面*

在APP站内嵌套h5页面,判断进入拨号页返回情况:

  • 我们需要通过visibilitychange

  • 通过在点击时修改一个状态值,回来时和上面的方法进行判断

    const isClick=false // 是否点击了离开页面按钮
    const isShowPop=false // 是否显示弹窗
    document.addEventListener('visibilitychange', () => {
        if (document.visibilityState == "visible") {
          if (isClick) {
            isShowPop = true
          }
          isClick = false
        } else {}
    })
    

如果是在浏览器里面,判断进入拨号页(三方客服链接)返回情况:

  • 在浏览器里面如果是离开了页面还是可以通过visibilitychange判断,但是跳转的是三方客服链接,那我我们回到页面,页面是会重新刷新的,我们需要知道是否返回了,就需要通过performance.getEntriesByType('navigation')[0].type

  • 同时通过在点击时修改一个状态值,回来时和上面的方法进行判断

    const isClick=false // 是否点击了离开页面按钮
    const isShowPop=false // 是否显示弹窗
    const pageshowFn=(event)=> {
      const navigationType = performance.getEntriesByType('navigation')[0].type
      const {persisted = null} = event
      if (persisted || navigationType == 'back_forward') {
        isShowPop = true
        isClick = false
    }
    }
    window.removeEventListener('pageshow', pageshowFn)
    window.addEventListener('pageshow', pageshowFn)
    // 页面初次进入需要调用
    pageshowFn({})
    
    

往返缓存(BFCache)具体详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值