PkgLogo favicon 懒加载:IntersectionObserver 进入视口才发请求,大列表不再一次性并发数百个图标
CI / Backend tests (push) Canceled after 0s
CI / Frontend build (push) Canceled after 0s

This commit is contained in:
yinjun.chen1
2026-09-21 16:39:52 +08:00
parent 7f85c6438a
commit 60fa389def
+29 -4
View File
@@ -1,6 +1,7 @@
<script setup>
// 包 logo:优先取主页域名的 favicon(浏览器端加载,离线/加载失败回退首字母徽标)
import { computed, ref, watch } from 'vue'
// 懒加载:仅当组件进入视口(提前 120px)才发起图标请求,大列表不一次性并发数百个请求
import { computed, ref, watch, onMounted, onBeforeUnmount } from 'vue'
const props = defineProps({
name: { type: String, default: '' },
@@ -8,6 +9,30 @@ const props = defineProps({
homepage: { type: String, default: '' },
})
// —— 视口懒加载状态 ——
const inView = ref(false)
const rootEl = ref(null)
let io = null
onMounted(() => {
if (typeof IntersectionObserver === 'undefined' || !rootEl.value) {
inView.value = true // 不支持 IO 时退化为立即加载
return
}
io = new IntersectionObserver(
(entries) => {
if (entries[0] && entries[0].isIntersecting) {
inView.value = true
if (io) io.disconnect()
}
},
{ rootMargin: '120px 0px' },
)
io.observe(rootEl.value)
})
onBeforeUnmount(() => {
if (io) io.disconnect()
})
const letter = computed(() => {
const base = (props.name.split('/').pop() || props.name || '?').trim()
return (base[0] || '?').toUpperCase()
@@ -52,9 +77,9 @@ function onImgErr() { imgOk.value = false }
</script>
<template>
<span class="pkg-logo" :style="style" :title="name">
<!-- img 始终挂载以触发 load/error;加载成功后显示,失败回退字母徽标 -->
<img v-if="faviconUrl" class="fav" :class="{ on: imgOk }" :src="faviconUrl" alt="" @load="onImgOk" @error="onImgErr" />
<span ref="rootEl" class="pkg-logo" :style="style" :title="name">
<!-- 进入视口后才挂载 img 发起 favicon 请求;加载成功后显示,失败回退字母徽标 -->
<img v-if="faviconUrl && inView" class="fav" :class="{ on: imgOk }" :src="faviconUrl" alt="" @load="onImgOk" @error="onImgErr" />
<span v-show="!imgOk" class="lt">{{ letter }}</span>
</span>
</template>