From 865a10c7e51b0308c99d7387ab5740c7843e31b1 Mon Sep 17 00:00:00 2001
From: haoziqaq <357229046@qq.com>
Date: Wed, 23 Dec 2020 13:13:35 +0800
Subject: [PATCH] =?UTF-8?q?fix(ui/lazy):=20=E5=A2=9E=E5=8A=A01px=E5=83=8F?=
=?UTF-8?q?=E7=B4=A0=E5=8D=A0=E4=BD=8D=20=E5=A2=9E=E5=8A=A0=E5=9B=BE?=
=?UTF-8?q?=E7=89=87URL=E7=BC=93=E5=AD=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
affects: @varlet/ui
---
packages/varlet-ui/src/lazy/example/index.vue | 19 ++--
packages/varlet-ui/src/lazy/index.ts | 88 ++++++++++++-------
packages/varlet-ui/src/utils/shared.ts | 29 ++++++
3 files changed, 92 insertions(+), 44 deletions(-)
diff --git a/packages/varlet-ui/src/lazy/example/index.vue b/packages/varlet-ui/src/lazy/example/index.vue
index 30002416bab..4facb48d58d 100644
--- a/packages/varlet-ui/src/lazy/example/index.vue
+++ b/packages/varlet-ui/src/lazy/example/index.vue
@@ -3,13 +3,10 @@
@@ -28,34 +25,34 @@ export default defineComponent({
const images = reactive([
{
id: 1,
- url: 'https://cn.vuejs.org/images/logo.png',
+ url: 'https://ddtask.oss-cn-shanghai.aliyuncs.com/ddtask/201208ysl/images/1606982343202.jpg?Expires=1608703112&OSSAccessKeyId=TMP.3Kj8QoTDYVpdcwERRbtMRUh7wkoZvgG25VLcqMaqzYFdcqECUoupXbaFYu36z8tcVvCgwHxUo2RF1xdB46MJpNesxSzKsv&Signature=tmLF4Y65KgbR%2FeUXdZTGSMArxIo%3D&versionId=CAEQFBiBgMDNormesRciIGU5YmQ5MWQwZjZhZTQxNGY5Nzk5YjM1ZDRhY2Q0NzYw&response-content-type=application%2Foctet-stream',
},
{
id: 2,
- url: 'https://cn.vuejs.org/imagessss/logo.png',
+ url: 'https://ddtask.oss-cn-shanghai.aliyuncs.com/ddtask/201208ysl/images/1606982343202.jpg?Expires=1608703112&OSSAccessKeyId=TMP.3Kj8QoTDYVpdcwERRbtMRUh7wkoZvgG25VLcqMaqzYFdcqECUoupXbaFYu36z8tcVvCgwHxUo2RF1xdB46MJpNesxSzKsv&Signature=tmLF4Y65KgbR%2FeUXdZTGSMArxIo%3D&versionId=CAEQFBiBgMDNormesRciIGU5YmQ5MWQwZjZhZTQxNGY5Nzk5YjM1ZDRhY2Q0NzYw&response-content-type=application%2Foctet-stream',
},
{
id: 3,
- url: 'https://cn.vuejs.org/images/logo.png',
+ url: 'https://ddtask.oss-cn-shanghai.aliyuncs.com/ddtask/201208ysl/images/1606982343202.jpg?Expires=1608703112&OSSAccessKeyId=TMP.3Kj8QoTDYVpdcwERRbtMRUh7wkoZvgG25VLcqMaqzYFdcqECUoupXbaFYu36z8tcVvCgwHxUo2RF1xdB46MJpNesxSzKsv&Signature=tmLF4Y65KgbR%2FeUXdZTGSMArxIo%3D&versionId=CAEQFBiBgMDNormesRciIGU5YmQ5MWQwZjZhZTQxNGY5Nzk5YjM1ZDRhY2Q0NzYw&response-content-type=application%2Foctet-stream',
},
{
id: 4,
- url: 'https://cn.vuejs.org/images/logo.png',
+ url: 'https://ddtask.oss-cn-shanghai.aliyuncs.com/ddtask/201208ysl/images/1606982343202.jpg?Expires=1608703112&OSSAccessKeyId=TMP.3Kj8QoTDYVpdcwERRbtMRUh7wkoZvgG25VLcqMaqzYFdcqECUoupXbaFYu36z8tcVvCgwHxUo2RF1xdB46MJpNesxSzKsv&Signature=tmLF4Y65KgbR%2FeUXdZTGSMArxIo%3D&versionId=CAEQFBiBgMDNormesRciIGU5YmQ5MWQwZjZhZTQxNGY5Nzk5YjM1ZDRhY2Q0NzYw&response-content-type=application%2Foctet-stream',
},{
id: 5,
- url: 'https://cn.vuejs.org/images/logo.png',
+ url: 'https://ddtask.oss-cn-shanghai.aliyuncs.com/ddtask/201208ysl/images/1606982343202.jpg?Expires=1608703112&OSSAccessKeyId=TMP.3Kj8QoTDYVpdcwERRbtMRUh7wkoZvgG25VLcqMaqzYFdcqECUoupXbaFYu36z8tcVvCgwHxUo2RF1xdB46MJpNesxSzKsv&Signature=tmLF4Y65KgbR%2FeUXdZTGSMArxIo%3D&versionId=CAEQFBiBgMDNormesRciIGU5YmQ5MWQwZjZhZTQxNGY5Nzk5YjM1ZDRhY2Q0NzYw&response-content-type=application%2Foctet-stream',
}
])
return {
images,
fix() {
- images[1].url = 'https://cn.vuejs.org/images/logo.png'
+ images[1].url = 'https://ddtask.oss-cn-shanghai.aliyuncs.com/ddtask/201208ysl/images/1606982343202.jpg?Expires=1608703112&OSSAccessKeyId=TMP.3Kj8QoTDYVpdcwERRbtMRUh7wkoZvgG25VLcqMaqzYFdcqECUoupXbaFYu36z8tcVvCgwHxUo2RF1xdB46MJpNesxSzKsv&Signature=tmLF4Y65KgbR%2FeUXdZTGSMArxIo%3D&versionId=CAEQFBiBgMDNormesRciIGU5YmQ5MWQwZjZhZTQxNGY5Nzk5YjM1ZDRhY2Q0NzYw&response-content-type=application%2Foctet-stream'
},
push() {
images.push({
id: Date.now(),
- url: 'https://cn.vuejs.org/images/logo.png'
+ url: 'https://ddtask.oss-cn-shanghai.aliyuncs.com/ddtask/201208ysl/images/1606982343202.jpg?Expires=1608703112&OSSAccessKeyId=TMP.3Kj8QoTDYVpdcwERRbtMRUh7wkoZvgG25VLcqMaqzYFdcqECUoupXbaFYu36z8tcVvCgwHxUo2RF1xdB46MJpNesxSzKsv&Signature=tmLF4Y65KgbR%2FeUXdZTGSMArxIo%3D&versionId=CAEQFBiBgMDNormesRciIGU5YmQ5MWQwZjZhZTQxNGY5Nzk5YjM1ZDRhY2Q0NzYw&response-content-type=application%2Foctet-stream'
})
},
pop() {
diff --git a/packages/varlet-ui/src/lazy/index.ts b/packages/varlet-ui/src/lazy/index.ts
index 37e8cee5bea..025c08c05b8 100644
--- a/packages/varlet-ui/src/lazy/index.ts
+++ b/packages/varlet-ui/src/lazy/index.ts
@@ -1,7 +1,13 @@
import { App, Directive, Plugin } from 'vue'
import { DirectiveBinding } from '@vue/runtime-core'
import { inViewport } from '../utils/elements'
-import { checkIntersectionObserverAPI, createInViewportObserver, removeItem, throttle } from '../utils/shared'
+import {
+ CacheInstance,
+ checkIntersectionObserverAPI, createCache,
+ createInViewportObserver,
+ removeItem,
+ throttle
+} from '../utils/shared'
interface LazyOptions {
loading?: string
@@ -18,19 +24,22 @@ type Lazy = LazyOptions & {
attemptLock: boolean
}
-type LazyHTMLElement = HTMLElement & { lazy: Lazy }
+type LazyHTMLElement = HTMLElement & { _lazy: Lazy }
const BACKGROUND_IMAGE_ARG_NAME = 'background-image'
const LAZY_LOADING = 'lazy-loading'
const LAZY_ERROR = 'lazy-error'
const LAZY_ATTEMPT = 'lazy-attempt'
const EVENTS = ['resize', 'animationend', 'transitionend', 'touchmove', 'scroll']
+const PIXEL = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
const lazyElements: LazyHTMLElement[] = []
+const imageCache: CacheInstance = createCache(100)
+
let defaultLazyOptions: LazyOptions = {
- loading: '',
- error: '',
+ loading: PIXEL,
+ error: PIXEL,
attempt: 3,
throttleWait: 300,
}
@@ -42,21 +51,21 @@ let observer: IntersectionObserver | null = null
const useIntersectionObserverAPI: boolean = checkIntersectionObserverAPI()
function setLoading(el: LazyHTMLElement) {
- el.lazy.loading
- ? el.lazy.arg === BACKGROUND_IMAGE_ARG_NAME
- ? el.style.backgroundImage = `url(${el.lazy.loading})`
- : el.setAttribute('src', el.lazy.loading)
+ el._lazy.loading
+ ? el._lazy.arg === BACKGROUND_IMAGE_ARG_NAME
+ ? el.style.backgroundImage = `url(${el._lazy.loading})`
+ : el.setAttribute('src', el._lazy.loading)
: null
!useIntersectionObserverAPI && checkAll()
}
function setError(el: LazyHTMLElement) {
- if (el.lazy.error) {
- if (el.lazy.arg === BACKGROUND_IMAGE_ARG_NAME) {
- el.style.backgroundImage = `url(${el.lazy.error})`
+ if (el._lazy.error) {
+ if (el._lazy.arg === BACKGROUND_IMAGE_ARG_NAME) {
+ el.style.backgroundImage = `url(${el._lazy.error})`
} else {
- el.setAttribute('src', el.lazy.error)
+ el.setAttribute('src', el._lazy.error)
}
}
@@ -65,7 +74,7 @@ function setError(el: LazyHTMLElement) {
}
function setSuccess(el: LazyHTMLElement, attemptSRC: string) {
- el.lazy.arg === BACKGROUND_IMAGE_ARG_NAME
+ el._lazy.arg === BACKGROUND_IMAGE_ARG_NAME
? el.style.backgroundImage = `url(${attemptSRC})`
: el.setAttribute('src', attemptSRC)
@@ -86,10 +95,10 @@ function unbindEvents() {
}
function buildAttemptSRC(el: LazyHTMLElement): string {
- const { src } = el.lazy
+ const { src } = el._lazy
- if (el.lazy.currentAttempt === 0) {
- el.lazy.currentAttempt++
+ if (el._lazy.currentAttempt === 0) {
+ el._lazy.currentAttempt++
return src
}
@@ -98,7 +107,7 @@ function buildAttemptSRC(el: LazyHTMLElement): string {
const params: string = index > -1 ? src.slice(src.indexOf('?')) : ''
const searchParams = new URLSearchParams(params)
- searchParams.set('lazyAttempt', String(++el.lazy.currentAttempt))
+ searchParams.set('lazyAttempt', String(++el._lazy.currentAttempt))
return `${url}?${searchParams.toString()}`
}
@@ -110,7 +119,7 @@ function mergeLazyOptions(el: LazyHTMLElement, binding: DirectiveBinding
attempt: el.getAttribute(LAZY_ATTEMPT) ? Number(el.getAttribute(LAZY_ATTEMPT)) : defaultLazyOptions.attempt
}
- el.lazy = {
+ el._lazy = {
src: binding.value,
arg: binding.arg,
currentAttempt: 0,
@@ -118,33 +127,44 @@ function mergeLazyOptions(el: LazyHTMLElement, binding: DirectiveBinding
...lazyInnerOptions
}
- defaultLazyOptions.filter?.(el.lazy)
+ defaultLazyOptions.filter?.(el._lazy)
}
-function attemptLoad(el: LazyHTMLElement) {
- if (el.lazy.attemptLock === true) {
- return
- }
- el.lazy.attemptLock = true
-
- setLoading(el)
-
- const attemptSRC: string = buildAttemptSRC(el)
-
+function createImage(el: LazyHTMLElement, attemptSRC: string) {
const image: HTMLImageElement = new Image()
image.src = attemptSRC
image.addEventListener('load', () => {
- el.lazy.attemptLock = false
+ el._lazy.attemptLock = false
+
+ imageCache.add(attemptSRC)
setSuccess(el, attemptSRC)
})
image.addEventListener('error', () => {
- el.lazy.attemptLock = false
- ;(el.lazy.currentAttempt as number) >= (el.lazy.attempt as number)
+ el._lazy.attemptLock = false
+
+ ;(el._lazy.currentAttempt as number) >= (el._lazy.attempt as number)
? setError(el)
: attemptLoad(el)
})
}
+function attemptLoad(el: LazyHTMLElement) {
+ if (el._lazy.attemptLock === true) {
+ return
+ }
+ el._lazy.attemptLock = true
+
+ const attemptSRC: string = buildAttemptSRC(el)
+ if (imageCache.has(attemptSRC)) {
+ setSuccess(el, attemptSRC)
+ el._lazy.attemptLock = false
+ return
+ }
+
+ setLoading(el)
+ createImage(el, attemptSRC)
+}
+
function check(el: LazyHTMLElement) {
inViewport(el) && attemptLoad(el)
}
@@ -172,6 +192,8 @@ function observe(el: LazyHTMLElement) {
}
function mounted(el: LazyHTMLElement, binding: DirectiveBinding) {
+ !el.getAttribute('src') && el.setAttribute('src', PIXEL)
+
mergeLazyOptions(el, binding)
if (useIntersectionObserverAPI) {
@@ -187,7 +209,7 @@ function unmounted(el: LazyHTMLElement) {
}
function diff(el: LazyHTMLElement, binding: DirectiveBinding): boolean {
- const { src, arg, attempt, loading, error } = el.lazy
+ const { src, arg, attempt, loading, error } = el._lazy
return src !== binding.value ||
arg !== binding.arg ||
attempt !== Number(el.getAttribute(LAZY_ATTEMPT)) ||
diff --git a/packages/varlet-ui/src/utils/shared.ts b/packages/varlet-ui/src/utils/shared.ts
index fc1026c56ac..6d32e5e693a 100644
--- a/packages/varlet-ui/src/utils/shared.ts
+++ b/packages/varlet-ui/src/utils/shared.ts
@@ -1,3 +1,10 @@
+export interface CacheInstance {
+ cache: T[]
+ has(key: T)
+ add(key: T)
+ remove(key: T)
+}
+
export const isString = (val: unknown): val is string => typeof val === 'string'
export const isBaseObject = (val: unknown) => Object.prototype.toString.call(val) === '[object Object]'
@@ -44,3 +51,25 @@ export const createInViewportObserver = (handler: (el: T) => void): Intersect
})
})
}
+
+export const createCache = (max: number): CacheInstance => {
+ const cache: T[] = []
+
+ return {
+ cache,
+ has(key: T) {
+ return cache.includes(key)
+ },
+ add(key: T) {
+ if (this.has(key)) {
+ return
+ }
+
+ this.cache.length === max && cache.shift()
+ this.cache.push(key)
+ },
+ remove(key: T) {
+ this.has(key) && removeItem(this.cache, key)
+ }
+ }
+}