refactor(splash&diy): 重构启动页逻辑,优化白屏问题
1. 统一全平台启动页为strict模式,仅在12s兜底超时才强制关闭 2. 新增网络自适应超时策略,根据网络环境调整兜底时长 3. 补充diy加载失败时的启动页关闭逻辑,避免严格模式下无限等待 4. 优化代码格式与注释,修复部分代码细节问题
This commit is contained in:
@@ -57,10 +57,10 @@ export default {
|
||||
diyLoadError: false,
|
||||
diyLoadErrorMsg: '',
|
||||
diyLoadErrorUrl: '', // 出错的网络请求地址
|
||||
|
||||
|
||||
//启动广告
|
||||
adv:{},
|
||||
|
||||
adv: {},
|
||||
|
||||
};
|
||||
},
|
||||
onLoad(option) {
|
||||
@@ -123,10 +123,10 @@ export default {
|
||||
// 关闭定位
|
||||
this.enterDefaultStore();
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
onShow() {
|
||||
|
||||
|
||||
this.init();
|
||||
},
|
||||
onHide() {
|
||||
@@ -211,12 +211,12 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
play(){
|
||||
play() {
|
||||
},
|
||||
async init() {
|
||||
|
||||
// 定位信息过期后,重新获取定位
|
||||
if(this.mapConfig.wap_is_open == 1 && this.locationStorage && this.locationStorage.is_expired) {
|
||||
if (this.mapConfig.wap_is_open == 1 && this.locationStorage && this.locationStorage.is_expired) {
|
||||
this.$util.getLocation({
|
||||
fail: (res) => {
|
||||
// 拒绝定位,进入默认总店
|
||||
@@ -289,7 +289,7 @@ export default {
|
||||
}, 50);
|
||||
// #endif
|
||||
},
|
||||
async getDiyAdv(){
|
||||
async getDiyAdv() {
|
||||
//启动广告
|
||||
let res = await this.$api.sendRequest({
|
||||
url: '/api/diyview/getstartadv',
|
||||
@@ -298,13 +298,13 @@ export default {
|
||||
});
|
||||
this.adv = res.value
|
||||
// 弹框形式,首次弹出 1,每次弹出 0
|
||||
if(this.adv.advshow == 1){
|
||||
if (this.adv.advshow == 1) {
|
||||
setTimeout(() => {
|
||||
if (res.value.advtype == 1) {
|
||||
var popwindow_count = uni.getStorageSync(this.id + this.name + '_popwindow_count');
|
||||
if ((this.$refs.uniPopupWindow && popwindow_count == '') || (
|
||||
this.$refs.uniPopupWindow && popwindow_count == 1)) {
|
||||
|
||||
|
||||
this.$refs.uniPopupWindow.open();
|
||||
uni.setStorageSync(this.id + this.name + '_popwindow_count', 1);
|
||||
}
|
||||
@@ -314,15 +314,22 @@ export default {
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
// 无法从网络获取 diy 数据时:在页面内渲染错误横幅(各端可靠、一定可见)
|
||||
// 同时通过 uni.$emit 通知 splash-state 关闭启动屏 (否则 strict 模式永远不关)
|
||||
// @param {Object|string} [err] 网络异常对象 {errMsg, statusCode} / 服务端消息字符串
|
||||
// @param {string} [url] 出错的请求地址(完整 URL),用于横幅展示
|
||||
_showDiyLoadError(err, url) {
|
||||
// @param {string} [errorType] 错误类型: 'network' | 'no-response' | 'business'
|
||||
_showDiyLoadError(err, url, errorType) {
|
||||
this.diyLoadErrorMsg = this._buildDiyErrorMsg(err);
|
||||
this.diyLoadErrorUrl = url || '';
|
||||
this.diyLoadError = true;
|
||||
// 通知 splash: diy 加载失败, 启动屏应该让位给错误横幅
|
||||
// 用 uni.$emit 而非直接 import splash-state, 避免循环依赖
|
||||
// reason 拼成 'diy-{errorType}-error' 方便业务方区分 (splash:diy-error 事件)
|
||||
const reason = errorType ? `diy-${errorType}-error` : 'diy-load-error'
|
||||
try { uni.$emit && uni.$emit('diy:load-error', { reason, errorType, err, url }); } catch (e) { }
|
||||
},
|
||||
// 根据错误信息组织更详细的提示文案
|
||||
// 说明:remote-only 模式下 $lang 可能返回空串,故统一用 `|| 中文兜底` 保证非空
|
||||
@@ -383,7 +390,8 @@ export default {
|
||||
if (this.$refs.loadingCover) this.$refs.loadingCover.hide();
|
||||
this.diyInfoLoading = false;
|
||||
// 无法从网络获取数据时,页面内显示错误横幅并提供重试(带详细原因 + 请求地址)
|
||||
this._showDiyLoadError(e, diyInfoFullUrl);
|
||||
// errorType='network': 网络异常 (uni.request catch)
|
||||
this._showDiyLoadError(e, diyInfoFullUrl, 'network');
|
||||
return;
|
||||
}
|
||||
this.diyInfoLoading = false;
|
||||
@@ -393,7 +401,8 @@ export default {
|
||||
|
||||
// 完全没有拿到网络响应,视为无法从网络获取数据,页面内显示错误横幅
|
||||
if (!res) {
|
||||
this._showDiyLoadError(null, diyInfoFullUrl);
|
||||
// errorType='no-response': 完全没有 res 对象
|
||||
this._showDiyLoadError(null, diyInfoFullUrl, 'no-response');
|
||||
// 保持原有数据结构,仅清空 value,避免鸿蒙下重新布局
|
||||
this.diyData = { ...(this.diyData || {}), value: [] };
|
||||
return;
|
||||
@@ -410,7 +419,8 @@ export default {
|
||||
|
||||
// 拿到响应但业务失败 / 无数据(如接口 404 返回 200+错误码、code!=0)
|
||||
// 同样视为无法获取到 diy 数据,页面内显示错误横幅
|
||||
this._showDiyLoadError(res && res.message, diyInfoFullUrl);
|
||||
// errorType='business': 业务失败 (code!=0 / 消息提示)
|
||||
this._showDiyLoadError(res && res.message, diyInfoFullUrl, 'business');
|
||||
// 保持原有数据结构,仅清空 value,避免鸿蒙下重新布局
|
||||
this.diyData = { ...(this.diyData || {}), value: [] };
|
||||
return;
|
||||
@@ -434,22 +444,22 @@ export default {
|
||||
|
||||
this.mpCollect = this.diyData.global.mpCollect;
|
||||
this.setPublicShare();
|
||||
/* if (this.diyData.global.popWindow && this.diyData.global.popWindow.imageUrl) {
|
||||
// 弹框形式,首次弹出 1,每次弹出 0
|
||||
setTimeout(() => {
|
||||
if (this.diyData.global.popWindow.count == 1) {
|
||||
var popwindow_count = uni.getStorageSync(this.id + this.name + '_popwindow_count');
|
||||
if ((this.$refs.uniPopupWindow && popwindow_count == '') || (
|
||||
this.$refs.uniPopupWindow && popwindow_count == 1)) {
|
||||
/* if (this.diyData.global.popWindow && this.diyData.global.popWindow.imageUrl) {
|
||||
// 弹框形式,首次弹出 1,每次弹出 0
|
||||
setTimeout(() => {
|
||||
if (this.diyData.global.popWindow.count == 1) {
|
||||
var popwindow_count = uni.getStorageSync(this.id + this.name + '_popwindow_count');
|
||||
if ((this.$refs.uniPopupWindow && popwindow_count == '') || (
|
||||
this.$refs.uniPopupWindow && popwindow_count == 1)) {
|
||||
this.$refs.uniPopupWindow.open();
|
||||
uni.setStorageSync(this.id + this.name + '_popwindow_count', 1);
|
||||
}
|
||||
} else if (this.diyData.global.popWindow.count == 0) {
|
||||
this.$refs.uniPopupWindow.open();
|
||||
uni.setStorageSync(this.id + this.name + '_popwindow_count', 1);
|
||||
uni.setStorageSync(this.id + this.name + '_popwindow_count', 0);
|
||||
}
|
||||
} else if (this.diyData.global.popWindow.count == 0) {
|
||||
this.$refs.uniPopupWindow.open();
|
||||
uni.setStorageSync(this.id + this.name + '_popwindow_count', 0);
|
||||
}
|
||||
}, 500);
|
||||
}*/
|
||||
}, 500);
|
||||
}*/
|
||||
|
||||
// 修改diy数据结构排序
|
||||
let searchIndex = -1;
|
||||
|
||||
@@ -33,7 +33,16 @@ function useNativeLoading() {
|
||||
// #endif
|
||||
}
|
||||
|
||||
const DEFAULT_TIMEOUT = 8000; // 总兜底超时
|
||||
// 总兜底超时档位 (show 时可覆盖):
|
||||
// 12000 (12s): 默认档, 启动体验优先 (大多数网络环境够用, 略比 10s 宽松)
|
||||
// 17000 (17s): 中档, 慢网/冷启动 (1s+ 启动 + 3-5s i18n 远端 + 1-3s diy 拉取 + 缓冲)
|
||||
// 22000 (22s): 慢网档, 极弱网环境 (1s+ 启动 + 5-8s i18n 远端 + 3-5s diy 拉取 + 缓冲)
|
||||
// 低于 12000 不推荐: 用户感知的"启动卡"窗口期不够, 但业务方仍可传 < 12000 测试用
|
||||
// 业务方覆盖: splashState.show({ timeout: 17000 })
|
||||
// 注意: DEFAULT_TIMEOUT 仍是兜底 (无 opts.timeout), 不能被业务方传更低
|
||||
// 严防因为 timeout 太短导致用户看到白屏.
|
||||
// 设计思路: 各档位比"刚好够用"多 2s 缓冲, 弱网/慢网络环境兜底更稳
|
||||
const DEFAULT_TIMEOUT = 12000; // 总兜底超时 (默认 12s)
|
||||
const MOUNT_GRACE_MS = 1000; // 软隐藏后等组件 mount 的宽限时间
|
||||
const PAGE_READY_GRACE_MS = 2000; // 软隐藏后等页面就绪的宽限时间
|
||||
|
||||
@@ -71,6 +80,111 @@ class SplashState {
|
||||
// 避免多通道并发时, splash 组件 re-render 风暴
|
||||
this._pendingProgress = null; // 待 emit 的最新进度
|
||||
this._flushTimer = null; // 节流定时器
|
||||
// 网络环境 (用于 show() 自动选档)
|
||||
// _networkType: 'wifi' | '4g' | '5g' | '3g' | '2g' | 'none' | 'unknown'
|
||||
// _networkReady: false 表示还没检测完 (splash 显示中第一次构造时常见)
|
||||
// _networkReady 后, 网络变化时通过 onNetworkStatusChange 实时更新
|
||||
this._networkType = 'unknown';
|
||||
this._networkReady = false;
|
||||
|
||||
// 启动网络检测 (异步, 不阻塞 splash 显示)
|
||||
this._initNetworkDetect();
|
||||
|
||||
// 监听 diy 加载失败事件 (diy.js 通过 uni.$emit 触发)
|
||||
// 收到后调 notifyDiyLoadError (严格模式下必须能关闭 splash)
|
||||
// 解耦: splash-state 不直接 import diy.js
|
||||
// 透传 reason / errorType / err / url 给业务方监听 splash:diy-error 使用
|
||||
try {
|
||||
if (typeof uni !== 'undefined' && uni.$on) {
|
||||
uni.$on('diy:load-error', (payload) => {
|
||||
try { this.notifyDiyLoadError(payload || {}) } catch (e) {}
|
||||
})
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化网络检测
|
||||
* - 调 uni.getNetworkType 异步获取当前网络类型
|
||||
* - 监听 uni.onNetworkStatusChange 实时更新
|
||||
* - H5 平台降级: 调 navigator.onLine, 不支持细粒度网络类型
|
||||
* 失败保护: try-catch 包, 任意失败都保持 _networkType='unknown' (不会破坏 splash)
|
||||
*/
|
||||
_initNetworkDetect() {
|
||||
try {
|
||||
// #ifdef H5
|
||||
// H5 平台: navigator.onLine 是粗粒度, 只能判断在线/离线
|
||||
// 仅在 navigator.onLine 是明确 boolean (true/false) 时才是 H5
|
||||
// Node 24+ 有 navigator 但 onLine 是 undefined, 不算 H5 (走下面 uni.getNetworkType)
|
||||
if (typeof navigator !== 'undefined' && typeof navigator.onLine === 'boolean') {
|
||||
// 初始检测: 用 _updateNetworkType (内部会触发事件)
|
||||
this._updateNetworkType(navigator.onLine ? 'wifi' : 'none', { source: 'h5-initial' })
|
||||
// 监听 online/offline 事件
|
||||
if (typeof window !== 'undefined' && window.addEventListener) {
|
||||
window.addEventListener('online', () => {
|
||||
this._updateNetworkType('wifi', { source: 'h5-online', isConnected: true })
|
||||
})
|
||||
window.addEventListener('offline', () => {
|
||||
this._updateNetworkType('none', { source: 'h5-offline', isConnected: false })
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
// navigator.onLine 不是 boolean (Node/SSR) - 走 uni.getNetworkType
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
if (typeof uni === 'undefined' || !uni.getNetworkType) {
|
||||
// 不可用: 用 _updateNetworkType 设 unknown (会触发事件)
|
||||
this._updateNetworkType('unknown', { source: 'no-uni-api' })
|
||||
return
|
||||
}
|
||||
// 异步获取当前网络类型
|
||||
uni.getNetworkType({
|
||||
success: (res) => {
|
||||
this._updateNetworkType((res && res.networkType) || 'unknown', { source: 'uni-initial' })
|
||||
},
|
||||
fail: () => {
|
||||
this._updateNetworkType('unknown', { source: 'uni-initial-fail' })
|
||||
},
|
||||
})
|
||||
// 监听网络变化
|
||||
if (uni.onNetworkStatusChange) {
|
||||
uni.onNetworkStatusChange((res) => {
|
||||
const newType = (res && res.networkType)
|
||||
|| (res && res.isConnected === false ? 'none' : 'unknown')
|
||||
this._updateNetworkType(newType, {
|
||||
source: 'uni-change',
|
||||
isConnected: res && typeof res.isConnected === 'boolean' ? res.isConnected : undefined,
|
||||
})
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
} catch (e) {
|
||||
// 任意失败都用 unknown, 不影响 splash
|
||||
this._updateNetworkType('unknown', { source: 'init-error' })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前网络环境选档
|
||||
* - 离线 (none): 4000 (4s, 让 diy 错误信号有时间触发)
|
||||
* - 弱网 (2g/3g): 22000 (22s 慢网档, 多 2s 缓冲)
|
||||
* - 正常 (4g/5g/wifi): 12000 (12s 默认档, 多 2s 缓冲)
|
||||
* - 未知 (unknown): 17000 (17s 中档, 保守)
|
||||
* - 网络未就绪: 12000 (DEFAULT_TIMEOUT, 保守)
|
||||
*/
|
||||
_pickTimeoutByNetwork() {
|
||||
if (!this._networkReady) return DEFAULT_TIMEOUT // 网络检测未就绪, 用默认
|
||||
switch (this._networkType) {
|
||||
case 'none': return 4000 // 离线: 4s 兜底
|
||||
case '2g':
|
||||
case '3g': return 22000 // 弱网: 22s 慢网档
|
||||
case '4g':
|
||||
case '5g':
|
||||
case 'wifi': return 12000 // 正常: 12s 默认档
|
||||
case 'unknown':
|
||||
default: return 17000 // 未知: 17s 中档 (保守)
|
||||
}
|
||||
}
|
||||
|
||||
show(opts = {}) {
|
||||
@@ -111,7 +225,34 @@ class SplashState {
|
||||
try { uni.showLoading({ title: opts.title || '加载中...', mask: false }); } catch (e) {}
|
||||
}
|
||||
|
||||
const timeout = opts.timeout || DEFAULT_TIMEOUT;
|
||||
// 业务方可通过 opts.timeout 覆盖, 但不能低于 5000ms (低于此值给 warn, 仍生效)
|
||||
// 原因: 启动期 i18n/theme/remote-config 远端加载最少 1-2s, diy 加载 1-3s
|
||||
// timeout 低于 5s 会经常触发兜底强关, 用户看到白屏
|
||||
// 测试场景允许传 < 5000ms (因为 mock 环境快), 仍生效但 warn
|
||||
// 优先级: opts.timeout (业务方显式) > _pickTimeoutByNetwork() (自动选档) > DEFAULT_TIMEOUT
|
||||
const MIN_RECOMMENDED_TIMEOUT = 5000
|
||||
let timeout
|
||||
if (opts.timeout) {
|
||||
timeout = opts.timeout
|
||||
if (opts.timeout < MIN_RECOMMENDED_TIMEOUT) {
|
||||
console.warn(
|
||||
`[splash-state] timeout=${opts.timeout}ms 低于推荐下限 ${MIN_RECOMMENDED_TIMEOUT}ms, ` +
|
||||
`可能导致启动期白屏. 生产环境建议 10000-20000ms.`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// 自动选档: 根据当前网络环境选 timeout
|
||||
timeout = this._pickTimeoutByNetwork()
|
||||
}
|
||||
this._armTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置/重设 splash 总超时兜底
|
||||
* - 由 show() 初始化, 或 _updateNetworkType() 在网络变化时调用
|
||||
* - 关键: 网络从好→差时延长, 差→好时缩短, 保证用户不会过早/过晚看到白屏
|
||||
*/
|
||||
_armTimeout(timeout) {
|
||||
if (this._timeoutId) {
|
||||
clearTimeout(this._timeoutId);
|
||||
this._timeoutId = null;
|
||||
@@ -125,6 +266,55 @@ class SplashState {
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络变化时更新 _networkType, 并按需重启 splash 兜底
|
||||
* @param {string} newType 新的网络类型
|
||||
* @param {Object} [meta] 附加信息 { isConnected, source: 'h5-online' | 'uni-change' | ... }
|
||||
* @returns {boolean} 是否真的更新了 (新旧不同时为 true)
|
||||
*/
|
||||
_updateNetworkType(newType, meta) {
|
||||
newType = newType || 'unknown'
|
||||
const oldType = this._networkType
|
||||
const changed = oldType !== newType
|
||||
// 关键: 即使 type 没变, _networkReady 也要设 true
|
||||
// 原因: 首次调用 _updateNetworkType 是从 _initNetworkDetect 进来
|
||||
// 此时 _networkType='unknown' (初始), 可能 newType 也是 'unknown' (uni 不可用)
|
||||
// 但 _networkReady 仍要从 false → true (让 _pickTimeoutByNetwork 工作)
|
||||
this._networkReady = true
|
||||
if (!changed) return false
|
||||
this._networkType = newType
|
||||
|
||||
// 网络变化时: 如果 splash 还 visible, 重启兜底 (用新档)
|
||||
// 业务场景:
|
||||
// - wifi → 2g: 启动期远端慢, 兜底延长, 避免过早强关
|
||||
// - 2g → wifi: 兜底缩短, 避免过晚强关
|
||||
// - any → none: 兜底缩短到 4s, 让 diy 错误信号更快触发
|
||||
// - none → any: 兜底恢复正常档, 给足时间
|
||||
const shouldReboot = this._visible && this._pendingHide === null
|
||||
// 关键: 仅当 splash 还没进入"软隐藏"阶段时重启兜底
|
||||
// _pendingHide 不为 null 表示业务方已调 hide(), 等 notifyPageReady/DiyLoadError 即可
|
||||
// 此时网络变化不影响兜底 (业务方已接管决策)
|
||||
if (shouldReboot) {
|
||||
const newTimeout = this._pickTimeoutByNetwork()
|
||||
this._armTimeout(newTimeout)
|
||||
}
|
||||
|
||||
// 发 splash:network-change 事件 (业务方监听)
|
||||
// payload: { from, to, isConnected, retryRecommended, rebootSplashTimeout }
|
||||
// retryRecommended: 从 none 恢复时建议重试 (业务方重调 diy 接口)
|
||||
// rebootSplashTimeout: 是否重启了 splash 兜底 (true/false)
|
||||
const eventPayload = {
|
||||
from: oldType,
|
||||
to: newType,
|
||||
isConnected: meta && typeof meta.isConnected === 'boolean' ? meta.isConnected : newType !== 'none',
|
||||
retryRecommended: oldType === 'none' && newType !== 'none', // 离线→在线, 建议重试
|
||||
rebootSplashTimeout: shouldReboot,
|
||||
source: (meta && meta.source) || 'unknown',
|
||||
}
|
||||
this._emit('splash:network-change', eventPayload)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏 splash
|
||||
* @param {Object} opts
|
||||
@@ -204,7 +394,11 @@ class SplashState {
|
||||
clearTimeout(this._mountGraceId);
|
||||
this._mountGraceId = null;
|
||||
}
|
||||
const _opts = this._pendingHide || {}
|
||||
// 已被 notifyDiyLoadError 拿走 _pendingHide 并自己 _doHide: noop
|
||||
// 原因: notifyMounted 50ms 后会调 _flushPendingHide, 但 _pendingHide 已被 notifyDiyLoadError 消费
|
||||
// 不应再调 _doHide (避免重复发 splash:hide 或时序错乱)
|
||||
if (!this._pendingHide) return;
|
||||
const _opts = this._pendingHide
|
||||
const useStrict = _opts && (_opts.mode === 'strict' || _opts._strict === true)
|
||||
|
||||
// 如果页面还未就绪, 等待页面就绪 (保留 _pendingHide, 等页面就绪后再处理)
|
||||
@@ -232,6 +426,11 @@ class SplashState {
|
||||
|
||||
/**
|
||||
* 实际隐藏 (硬路径)
|
||||
* @param {Object} opts
|
||||
* @param {string} [opts.reason='manual'] 隐藏原因
|
||||
* @param {string} [opts.mode='default'] 动画模式
|
||||
* @param {boolean} [opts._silent=false] true=不发 splash:hide 事件
|
||||
* 适用于 notifyDiyLoadError: 已经发了 splash:diy-error, 不能再发 splash:hide
|
||||
*/
|
||||
_doHide(opts) {
|
||||
if (!this._visible) return; // 防重入
|
||||
@@ -253,6 +452,8 @@ class SplashState {
|
||||
if (useNativeLoading()) {
|
||||
try { uni.hideLoading(); } catch (e) {}
|
||||
}
|
||||
// _silent=true: 业务方已经从 notifyDiyLoadError 收了 splash:diy-error, 这里不发 splash:hide
|
||||
if (opts._silent) return;
|
||||
this._emit('splash:hide', {
|
||||
reason: opts.reason || 'manual',
|
||||
mode: opts.mode || 'default',
|
||||
@@ -292,6 +493,60 @@ class SplashState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* diy 加载失败时通知 (网络异常/业务失败/无响应)
|
||||
* - 任何模式: 真关 splash (不能无限等, 错误已显示在页面横幅上)
|
||||
* - 触发独立事件 'splash:diy-error' (不是 'splash:hide')
|
||||
* 业务方可监听此事件做: 错误上报 / 自定义错误展示 / 区分错误类型
|
||||
* 调用时机: diy 错误横幅出现后 (diy.js._showDiyLoadError)
|
||||
*
|
||||
* @param {Object} [opts]
|
||||
* @param {string} [opts.reason='diy-load-error'] 隐藏原因
|
||||
* 用于 'splash:diy-error' 事件的 payload, 业务方可区分:
|
||||
* - 'diy-network-error': 网络异常 (uni.request catch)
|
||||
* - 'diy-no-response': 无响应 (res 为空)
|
||||
* - 'diy-business-error': 业务失败 (code!=0)
|
||||
* @param {string} [opts.errorType] 错误类型: 'network' | 'business' | 'no-response'
|
||||
* @param {Object|string} [opts.err] 原始错误对象
|
||||
* @param {string} [opts.url] 出错的请求地址
|
||||
*/
|
||||
notifyDiyLoadError(opts = {}) {
|
||||
// 已 _pageReady=true (notifyPageReady 之后): noop
|
||||
// 原因: 业务方先调 notifyPageReady (diy 渲染好) 之后又调 notifyDiyLoadError (重复调用或时序错乱)
|
||||
// 不应再触发 splash:diy-error (已经发过 splash:hide)
|
||||
if (this._pageReady) return;
|
||||
// 复用 notifyPageReady 的关闭逻辑 (页面就绪 = 错误状态也算"就绪")
|
||||
// 原因: 错误状态页 (diyLoadError=true) 已经有 UI 反馈, splash 真关不卡视觉
|
||||
this._pageReady = true
|
||||
// 清除所有兜底, 真关 (strict 模式严格依赖 notifyPageReady/notifyDiyLoadError)
|
||||
if (this._pageReadyGraceId) {
|
||||
clearTimeout(this._pageReadyGraceId)
|
||||
this._pageReadyGraceId = null
|
||||
}
|
||||
if (this._mountGraceId) {
|
||||
clearTimeout(this._mountGraceId)
|
||||
this._mountGraceId = null
|
||||
}
|
||||
// 触发独立事件 (不发 splash:hide, 让业务方区分正常 hide 和 diy 错误)
|
||||
this._emit('splash:diy-error', {
|
||||
reason: opts.reason || 'diy-load-error',
|
||||
errorType: opts.errorType,
|
||||
err: opts.err,
|
||||
url: opts.url,
|
||||
})
|
||||
// 关键: 直接 _doHide 关闭 splash, **不**走 _flushPendingHide
|
||||
// 原因: _flushPendingHide 内部会 _doHide → 触发 splash:hide 事件
|
||||
// notifyDiyLoadError 路径**只**应发 splash:diy-error, 不应发 splash:hide
|
||||
// (业务方根据事件类型区分, 不能混淆)
|
||||
if (this._pendingHide) {
|
||||
// 把 _pendingHide 取出, 直接 _doHide 关闭
|
||||
// _silent=true 让 _doHide 不发 splash:hide (因为已经发了 splash:diy-error)
|
||||
const _pendingOpts = this._pendingHide
|
||||
this._pendingHide = null
|
||||
setTimeout(() => this._doHide({ ..._pendingOpts, _silent: true }), 50)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件 unmount 时通知 (用于 _hasMounted 状态管理)
|
||||
* 目前保留 _hasMounted=true, 因为一般只有 tab 页有 splash 组件, 不会动态增减
|
||||
|
||||
Reference in New Issue
Block a user