简化导航状态管理

This commit is contained in:
jxxghp
2025-06-30 20:14:31 +08:00
parent 21d21aa438
commit 377998335b
3 changed files with 2 additions and 338 deletions

View File

@@ -21,7 +21,6 @@ import { CronVuetify } from '@vue-js-cron/vuetify'
import { isPWA } from './@core/utils/navigator'
import { loadRemoteComponents } from './utils/federationLoader'
import { fetchGlobalSettings } from './utils/globalSetting'
import { initializeImageOptimizer } from '@/utils/imageOptimizer'
// 5. 其他插件和功能模块
import Toast from 'vue-toastification'
@@ -106,9 +105,5 @@ initializeApp().then(() => {
})
.use(ConfirmDialog)
.use(i18n)
// 初始化全局图片优化器
initializeImageOptimizer()
app.mount('#app')
.mount('#app')
})

View File

@@ -1,7 +1,6 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import { configureNProgress } from '@/api/nprogress'
import { useAuthStore } from '@/stores'
import { setNavigatingState as setImageNavigatingState } from '@/utils/imageOptimizer'
import { setNavigatingState as setRequestNavigatingState } from '@/utils/requestOptimizer'
// Nprogress
@@ -212,8 +211,7 @@ const router = createRouter({
// 路由导航守卫
router.beforeEach(async (to: any, from: any, next: any) => {
// 设置导航状态 - 同时暂停图片加载和中断API请求
setImageNavigatingState(true)
// 设置导航状态 - 同时中断API请求
setRequestNavigatingState(true)
// 认证 Store
@@ -224,7 +222,6 @@ router.beforeEach(async (to: any, from: any, next: any) => {
if (to.meta.requiresAuth && !isAuthenticated) {
// 用户未登录,重定向到登录页
setImageNavigatingState(false)
setRequestNavigatingState(false)
next('/login')
} else {
@@ -235,11 +232,9 @@ router.beforeEach(async (to: any, from: any, next: any) => {
// 路由导航完成后
router.afterEach(() => {
setTimeout(() => {
setImageNavigatingState(false)
setRequestNavigatingState(false)
}, 100)
})
// 导出默认对象
export default router
// 延迟恢复图片加载,给页面一些初始化时间

View File

@@ -1,326 +0,0 @@
let isNavigating = false
const MAX_CONCURRENT_IMAGES = 8 // 降低并发数,避免阻塞
let currentLoadingCount = 0
const imageQueue: { img: HTMLImageElement; src: string; controller?: AbortController }[] = []
// 存储所有正在加载的图片请求控制器
const activeImageRequests = new Set<AbortController>()
// 监听路由状态
export function setNavigatingState(navigating: boolean) {
isNavigating = navigating
if (navigating) {
console.log('Navigation started - canceling all image loads')
// 路由切换时,立即取消所有正在加载的图片
cancelAllImageLoads()
// 清空队列
imageQueue.length = 0
} else {
// 路由切换完成后,恢复图片加载
setTimeout(() => {
console.log('Navigation ended - resuming image loads')
processImageQueue()
}, 300)
}
}
// 取消所有正在加载的图片
function cancelAllImageLoads() {
// 取消所有fetch请求
for (const controller of activeImageRequests) {
if (!controller.signal.aborted) {
controller.abort()
}
}
activeImageRequests.clear()
// 重置计数器
currentLoadingCount = 0
}
// 处理图片队列
function processImageQueue() {
while (imageQueue.length > 0 && currentLoadingCount < MAX_CONCURRENT_IMAGES && !isNavigating) {
const item = imageQueue.shift()!
startImageLoad(item.img, item.src)
}
}
// 开始加载图片
function startImageLoad(img: HTMLImageElement, src: string) {
if (isNavigating) {
return // 如果正在导航,不加载新图片
}
currentLoadingCount++
const originalOnLoad = img.onload
const originalOnError = img.onerror
img.onload = function (event) {
currentLoadingCount--
processImageQueue() // 加载完成后处理队列
if (originalOnLoad) originalOnLoad.call(this, event)
}
img.onerror = function (event) {
currentLoadingCount--
processImageQueue() // 出错时也要处理队列
if (originalOnError) originalOnError.call(this, event)
}
img.src = src
}
// 智能图片加载函数
function smartImageLoad(img: HTMLImageElement, src: string) {
if (isNavigating) {
// 路由切换时,加入队列等待
imageQueue.push({ img, src })
return
}
if (currentLoadingCount < MAX_CONCURRENT_IMAGES) {
// 直接加载
startImageLoad(img, src)
} else {
// 加入队列
imageQueue.push({ img, src })
}
}
// 优化的fetch图片加载函数
async function fetchImageWithCancel(url: string): Promise<string> {
if (isNavigating) {
throw new Error('Navigation in progress')
}
const controller = new AbortController()
activeImageRequests.add(controller)
try {
const response = await fetch(url, {
signal: controller.signal,
// 添加缓存控制
cache: 'force-cache',
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const blob = await response.blob()
const objectUrl = URL.createObjectURL(blob)
return objectUrl
} catch (error: any) {
if (error.name === 'AbortError') {
console.log('Image fetch was cancelled')
}
throw error
} finally {
activeImageRequests.delete(controller)
}
}
// 检查元素是否为VImg组件
function isVImgElement(element: Element): boolean {
if (!element.classList) return false
// 检查是否包含v-img相关的类名
const classList = Array.from(element.classList)
return classList.some(className => className.startsWith('v-img') || className.includes('v-img'))
}
// 全局VImg组件拦截
function interceptVImgComponents() {
// 使用MutationObserver监听DOM变化拦截所有VImg组件
const observer = new MutationObserver(mutations => {
if (isNavigating) return
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as Element
// 查找VImg组件Vuetify的VImg会生成特定的DOM结构
const vImgElements = element.querySelectorAll('.v-img, [class*="v-img"]')
vImgElements.forEach(vImgEl => {
interceptVImgElement(vImgEl as HTMLElement)
})
// 如果当前元素本身就是VImg
if (isVImgElement(element)) {
interceptVImgElement(element as HTMLElement)
}
}
})
})
})
observer.observe(document.body, {
childList: true,
subtree: true,
})
return observer
}
// 拦截单个VImg元素
function interceptVImgElement(vImgElement: HTMLElement) {
if (vImgElement.dataset.intercepted === 'true') {
return // 已经拦截过了
}
vImgElement.dataset.intercepted = 'true'
// 查找VImg内部的img元素
const findAndInterceptImg = () => {
const imgElements = vImgElement.querySelectorAll('img')
imgElements.forEach(img => {
if (img.dataset.intercepted !== 'true') {
img.dataset.intercepted = 'true'
// 保存原始的src设置方法
const originalSetAttribute = img.setAttribute.bind(img)
const originalSrcDescriptor =
Object.getOwnPropertyDescriptor(img, 'src') ||
Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, 'src')
// 拦截setAttribute方法
img.setAttribute = function (name: string, value: string) {
if (name === 'src' && value && !isNavigating) {
smartImageLoad(this, value)
return
} else if (name === 'src' && isNavigating) {
// 导航时不设置src
return
}
return originalSetAttribute(name, value)
}
// 拦截src属性直接赋值
if (originalSrcDescriptor) {
Object.defineProperty(img, 'src', {
get: originalSrcDescriptor.get,
set: function (value: string) {
if (value && !isNavigating) {
smartImageLoad(this, value)
} else if (!isNavigating && originalSrcDescriptor.set) {
originalSrcDescriptor.set.call(this, value)
}
},
enumerable: true,
configurable: true,
})
}
}
})
}
// 立即查找现有的img元素
findAndInterceptImg()
// 监听VImg内部的变化
const imgObserver = new MutationObserver(() => {
if (!isNavigating) {
findAndInterceptImg()
}
})
imgObserver.observe(vImgElement, {
childList: true,
subtree: true,
})
}
// 初始化图片优化器
export function initializeImageOptimizer() {
// 拦截原生img元素
const originalCreateElement = document.createElement
document.createElement = function (tagName: string, options?: ElementCreationOptions) {
const element = originalCreateElement.call(this, tagName, options)
if (tagName.toLowerCase() === 'img') {
const img = element as HTMLImageElement
const originalSetAttribute = img.setAttribute
img.setAttribute = function (name: string, value: string) {
if (name === 'src' && value) {
smartImageLoad(this, value)
return
}
return originalSetAttribute.call(this, name, value)
}
}
return element
}
// 启动VImg组件拦截
interceptVImgComponents()
// 为现有图片添加懒加载属性
document.addEventListener('DOMContentLoaded', () => {
const images = document.querySelectorAll('img:not([loading])')
images.forEach(img => {
img.setAttribute('loading', 'lazy')
})
})
// 监听新添加的图片元素
const observer = new MutationObserver(mutations => {
if (isNavigating) return // 导航时跳过处理
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as Element
// 为新添加的img标签设置懒加载
if (element.tagName === 'IMG' && !element.getAttribute('loading')) {
element.setAttribute('loading', 'lazy')
}
// 为子元素中的img设置懒加载
const imgs = element.querySelectorAll('img:not([loading])')
imgs.forEach(img => {
img.setAttribute('loading', 'lazy')
})
}
})
})
})
observer.observe(document.body, {
childList: true,
subtree: true,
})
// 监听页面可见性变化
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// 页面隐藏时暂停图片加载
setNavigatingState(true)
} else {
// 页面显示时恢复图片加载
setTimeout(() => {
setNavigatingState(false)
}, 500)
}
})
}
// 导出fetch图片函数供组件使用
export { fetchImageWithCancel }
// 获取当前状态信息(调试用)
export function getImageLoadingStatus() {
return {
isNavigating,
currentLoadingCount,
queueLength: imageQueue.length,
activeRequestsCount: activeImageRequests.size,
}
}