fix(splash-screen): 启动屏等待首页内容就绪后再消失
问题描述: 首次启动时,启动屏已关闭但首页内容还是空白的,用户体验差。 解决方案: 1. 在 splash-state.js 中新增页面就绪等待机制 (_pageReady 标志、notifyPageReady 方法) 2. 修改软隐藏逻辑,必须同时满足组件已 mount 和页面已就绪才关闭启动屏 3. 在首页 onDiyGroupLoaded 回调中通知页面就绪 兜底保护: - 组件 1s 未 mount → 强制关闭 - 页面 2s 未就绪 → 强制关闭 - 总超时 8s → 强制关闭
This commit is contained in:
@@ -35,6 +35,7 @@ function useNativeLoading() {
|
|||||||
|
|
||||||
const DEFAULT_TIMEOUT = 8000; // 总兜底超时
|
const DEFAULT_TIMEOUT = 8000; // 总兜底超时
|
||||||
const MOUNT_GRACE_MS = 1000; // 软隐藏后等组件 mount 的宽限时间
|
const MOUNT_GRACE_MS = 1000; // 软隐藏后等组件 mount 的宽限时间
|
||||||
|
const PAGE_READY_GRACE_MS = 2000; // 软隐藏后等页面就绪的宽限时间
|
||||||
|
|
||||||
// 启动期阶段定义 (id → { percent, label })
|
// 启动期阶段定义 (id → { percent, label })
|
||||||
// percent: 阶段完成的百分比 (0-100)
|
// percent: 阶段完成的百分比 (0-100)
|
||||||
@@ -56,9 +57,12 @@ class SplashState {
|
|||||||
this._listeners = [];
|
this._listeners = [];
|
||||||
this._shownAt = 0;
|
this._shownAt = 0;
|
||||||
// 软隐藏相关
|
// 软隐藏相关
|
||||||
this._pendingHide = null; // 软隐藏请求的 opts, 等待组件 mount
|
this._pendingHide = null; // 软隐藏请求的 opts, 等待组件 mount
|
||||||
this._hasMounted = false; // 是否有 splash 组件 mount 过
|
this._hasMounted = false; // 是否有 splash 组件 mount 过
|
||||||
this._mountGraceId = null; // 软隐藏兜底定时器
|
this._mountGraceId = null; // 软隐藏兜底定时器 (等 mount)
|
||||||
|
// 页面就绪等待
|
||||||
|
this._pageReady = false; // 页面内容是否已就绪 (由首页通知)
|
||||||
|
this._pageReadyGraceId = null; // 页面就绪兜底定时器
|
||||||
// 进度上报
|
// 进度上报
|
||||||
this._stage = 'boot';
|
this._stage = 'boot';
|
||||||
this._percent = 0;
|
this._percent = 0;
|
||||||
@@ -77,6 +81,7 @@ class SplashState {
|
|||||||
this._shownAt = Date.now();
|
this._shownAt = Date.now();
|
||||||
this._pendingHide = null;
|
this._pendingHide = null;
|
||||||
this._hasMounted = false;
|
this._hasMounted = false;
|
||||||
|
this._pageReady = false;
|
||||||
this._stage = 'boot';
|
this._stage = 'boot';
|
||||||
// 初始化时直接显示 'boot' 阶段的 5%, 而不是 0%
|
// 初始化时直接显示 'boot' 阶段的 5%, 而不是 0%
|
||||||
// 避免首帧出现"进度条 0%"的视觉突兀
|
// 避免首帧出现"进度条 0%"的视觉突兀
|
||||||
@@ -91,6 +96,10 @@ class SplashState {
|
|||||||
clearTimeout(this._mountGraceId);
|
clearTimeout(this._mountGraceId);
|
||||||
this._mountGraceId = null;
|
this._mountGraceId = null;
|
||||||
}
|
}
|
||||||
|
if (this._pageReadyGraceId) {
|
||||||
|
clearTimeout(this._pageReadyGraceId);
|
||||||
|
this._pageReadyGraceId = null;
|
||||||
|
}
|
||||||
this._emit('splash:show');
|
this._emit('splash:show');
|
||||||
|
|
||||||
// H5 降级: 走系统 loading (Vue 还没挂载,组件层无法渲染)
|
// H5 降级: 走系统 loading (Vue 还没挂载,组件层无法渲染)
|
||||||
@@ -138,15 +147,16 @@ class SplashState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 软隐藏: 标记应该 hide, 等组件 mount
|
* 软隐藏: 标记应该 hide, 等组件 mount + 页面就绪
|
||||||
* 如果组件 1s 内没 mount, 兜底自动 hide
|
* 如果组件 1s 内没 mount, 兜底自动 hide
|
||||||
|
* 如果页面 2s 内没就绪, 兜底自动 hide
|
||||||
*/
|
*/
|
||||||
_softHide(opts) {
|
_softHide(opts) {
|
||||||
if (!this._visible) return; // 已经隐藏了
|
if (!this._visible) return; // 已经隐藏了
|
||||||
this._pendingHide = opts;
|
this._pendingHide = opts;
|
||||||
|
|
||||||
// 如果组件已经 mount, 立即隐藏
|
// 如果组件已经 mount 且页面已就绪, 立即隐藏
|
||||||
if (this._hasMounted) {
|
if (this._hasMounted && this._pageReady) {
|
||||||
this._flushPendingHide();
|
this._flushPendingHide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -162,6 +172,7 @@ class SplashState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 真正触发软隐藏
|
* 真正触发软隐藏
|
||||||
|
* 如果页面还未就绪, 等待页面就绪后再 hide
|
||||||
*/
|
*/
|
||||||
_flushPendingHide() {
|
_flushPendingHide() {
|
||||||
const opts = this._pendingHide;
|
const opts = this._pendingHide;
|
||||||
@@ -170,6 +181,20 @@ class SplashState {
|
|||||||
clearTimeout(this._mountGraceId);
|
clearTimeout(this._mountGraceId);
|
||||||
this._mountGraceId = null;
|
this._mountGraceId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果页面还未就绪, 等待页面就绪
|
||||||
|
if (!this._pageReady) {
|
||||||
|
// 兜底: 2s 内没就绪, 强制隐藏
|
||||||
|
if (this._pageReadyGraceId) clearTimeout(this._pageReadyGraceId);
|
||||||
|
this._pageReadyGraceId = setTimeout(() => {
|
||||||
|
if (this._visible) {
|
||||||
|
console.warn('[splash-state] auto-hide after page-ready timeout');
|
||||||
|
this._doHide(opts || { reason: 'page-ready-timeout' });
|
||||||
|
}
|
||||||
|
}, PAGE_READY_GRACE_MS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._doHide(opts || { reason: 'mount-completed' });
|
this._doHide(opts || { reason: 'mount-completed' });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +211,10 @@ class SplashState {
|
|||||||
clearTimeout(this._mountGraceId);
|
clearTimeout(this._mountGraceId);
|
||||||
this._mountGraceId = null;
|
this._mountGraceId = null;
|
||||||
}
|
}
|
||||||
|
if (this._pageReadyGraceId) {
|
||||||
|
clearTimeout(this._pageReadyGraceId);
|
||||||
|
this._pageReadyGraceId = null;
|
||||||
|
}
|
||||||
this._pendingHide = null;
|
this._pendingHide = null;
|
||||||
this._visible = false;
|
this._visible = false;
|
||||||
// H5 降级对应的关闭
|
// H5 降级对应的关闭
|
||||||
@@ -217,6 +246,25 @@ class SplashState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面内容就绪后通知 state 模块
|
||||||
|
* - 标记 _pageReady
|
||||||
|
* - 如果有 _pendingHide, 触发真关
|
||||||
|
* 调用时机: 首页 diy-group 渲染完成后调用
|
||||||
|
*/
|
||||||
|
notifyPageReady() {
|
||||||
|
if (this._pageReady) return;
|
||||||
|
this._pageReady = true;
|
||||||
|
if (this._pageReadyGraceId) {
|
||||||
|
clearTimeout(this._pageReadyGraceId);
|
||||||
|
this._pageReadyGraceId = null;
|
||||||
|
}
|
||||||
|
if (this._pendingHide) {
|
||||||
|
// 给页面一帧时间完成渲染, 再隐藏
|
||||||
|
setTimeout(() => this._flushPendingHide(), 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件 unmount 时通知 (用于 _hasMounted 状态管理)
|
* 组件 unmount 时通知 (用于 _hasMounted 状态管理)
|
||||||
* 目前保留 _hasMounted=true, 因为一般只有 tab 页有 splash 组件, 不会动态增减
|
* 目前保留 _hasMounted=true, 因为一般只有 tab 页有 splash 组件, 不会动态增减
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import splashState from '@/common/js/splash-state.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -124,8 +126,10 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
// diy-group 渲染完毕回调, 用于触发版本水印等延后显示元素
|
// diy-group 渲染完毕回调, 用于触发版本水印等延后显示元素
|
||||||
|
// 同时通知 splash-state 页面就绪, 启动屏可以关闭
|
||||||
onDiyGroupLoaded() {
|
onDiyGroupLoaded() {
|
||||||
this.diyGroupReady = true;
|
this.diyGroupReady = true;
|
||||||
|
try { splashState.notifyPageReady(); } catch (e) {}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 版本连击 3 次以上显示构建版本信息(供测试人员使用)
|
* 版本连击 3 次以上显示构建版本信息(供测试人员使用)
|
||||||
|
|||||||
Reference in New Issue
Block a user