feat: 项目加载优化

This commit is contained in:
Gsh
2026-02-01 00:30:44 +08:00
parent 3b6887dc2e
commit 11cbb1b612
29 changed files with 1490 additions and 299 deletions

View File

@@ -17,6 +17,14 @@
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<!-- DNS 预解析和预连接 -->
<link rel="dns-prefetch" href="//api.yourdomain.com">
<link rel="preconnect" href="//api.yourdomain.com" crossorigin>
<!-- 预加载关键资源 -->
<link rel="preload" href="/src/main.ts" as="script" crossorigin>
<link rel="modulepreload" href="/src/main.ts">
<style>
/* 全局样式 */
@@ -112,16 +120,172 @@
<body>
<!-- 加载动画容器 -->
<div id="yixinai-loader" class="loader-container">
<div class="loader-title">意心Ai 3.6</div>
<div class="loader-title">%APP_FULL_NAME%</div>
<div class="loader-subtitle">海外地址仅首次访问预计加载约10秒无需梯子</div>
<div class="loader-logo">
<div class="pulse-box"></div>
</div>
<div class="loader-progress-bar">
<div id="loader-progress" class="loader-progress"></div>
</div>
<div id="loader-text" class="loader-text" style="font-size: 0.875rem; margin-top: 0.5rem; color: #666;">加载中...</div>
</div>
<div id="app"></div>
<script>
// 资源加载进度跟踪 - 增强版
(function() {
const progressBar = document.getElementById('loader-progress');
const loaderText = document.getElementById('loader-text');
const loader = document.getElementById('yixinai-loader');
let progress = 0;
let resourcesLoaded = false;
let vueAppMounted = false;
let appRendered = false;
// 更新进度条
function updateProgress(value, text) {
progress = Math.min(value, 99);
if (progressBar) progressBar.style.width = progress.toFixed(1) + '%';
if (loaderText) loaderText.textContent = text;
}
// 阶段管理
const stages = {
init: { weight: 15, name: '初始化' },
resources: { weight: 35, name: '加载资源' },
scripts: { weight: 25, name: '执行脚本' },
render: { weight: 15, name: '渲染页面' },
complete: { weight: 10, name: '启动应用' }
};
let completedStages = new Set();
let currentStage = 'init';
function calculateProgress() {
let totalProgress = 0;
for (const [key, stage] of Object.entries(stages)) {
if (completedStages.has(key)) {
totalProgress += stage.weight;
} else if (key === currentStage) {
// 当前阶段完成一部分
totalProgress += stage.weight * 0.5;
}
}
return Math.min(totalProgress, 99);
}
// 阶段完成
function completeStage(stageName, nextStage) {
completedStages.add(stageName);
currentStage = nextStage || stageName;
const stage = stages[stageName];
updateProgress(calculateProgress(), stage ? `${stage.name}完成` : '加载中...');
}
// 监听资源加载 - 使用更可靠的方式
const resourceTimings = [];
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
resourceTimings.push(...entries);
// 统计未完成资源
const pendingResources = performance.getEntriesByType('resource')
.filter(r => !r.responseEnd || r.responseEnd === 0).length;
if (pendingResources === 0 && resourceTimings.length > 0) {
completeStage('resources', 'scripts');
} else {
updateProgress(calculateProgress(), `加载资源中... (${resourceTimings.length} 已加载)`);
}
});
try {
observer.observe({ entryTypes: ['resource'] });
} catch (e) {
// 降级处理
}
// 初始进度
let initProgress = 0;
function simulateInitProgress() {
if (initProgress < stages.init.weight) {
initProgress += 1;
updateProgress(initProgress, '正在初始化...');
if (initProgress < stages.init.weight) {
setTimeout(simulateInitProgress, 30);
} else {
completeStage('init', 'resources');
}
}
}
simulateInitProgress();
// 页面资源加载完成
window.addEventListener('load', () => {
completeStage('resources', 'scripts');
resourcesLoaded = true;
// 给脚本执行时间
setTimeout(() => {
completeStage('scripts', 'render');
}, 300);
checkAndHideLoader();
});
// 暴露全局方法供 Vue 应用调用 - 分阶段调用
window.__hideAppLoader = function(stage) {
if (stage === 'mounted') {
vueAppMounted = true;
completeStage('scripts', 'render');
} else if (stage === 'rendered') {
appRendered = true;
completeStage('render', 'complete');
}
checkAndHideLoader();
};
// 检查是否可以隐藏加载动画
function checkAndHideLoader() {
// 需要满足资源加载完成、Vue 挂载、页面渲染完成
if (resourcesLoaded && vueAppMounted && appRendered) {
completeStage('complete', '');
updateProgress(100, '加载完成');
// 确保最小显示时间,避免闪烁
const minDisplayTime = 1000;
const elapsed = Date.now() - startTime;
const remaining = Math.max(0, minDisplayTime - elapsed);
setTimeout(() => {
if (loader) {
loader.style.opacity = '0';
loader.style.transition = 'opacity 0.5s ease';
setTimeout(() => {
loader.remove();
delete window.__hideAppLoader;
}, 500);
}
}, remaining);
}
}
const startTime = Date.now();
// 超时保护:最多显示 30 秒
setTimeout(() => {
if (loader && loader.parentNode) {
console.warn('加载超时,强制隐藏加载动画');
vueAppMounted = true;
resourcesLoaded = true;
appRendered = true;
checkAndHideLoader();
}
}, 30000);
})();
</script>
<script type="module" src="/src/main.ts"></script>
</body>