refactor(privacy&lang): optimize privacy check and language bundle loading

1. add cache for privacy check result to reduce repeated calls
2. refactor lang bundle boot process with integrity checks
3. add timeout recovery for late fetch responses
4. improve error handling and fallback logic
5. add comments for better maintainability
This commit is contained in:
2026-07-02 16:03:04 +08:00
parent 2014d27834
commit c86e688e47
5 changed files with 241 additions and 38 deletions

View File

@@ -65,6 +65,7 @@ export default {
}
},
mounted() {
// 调试: 记录本实例的 ID 和当前总数
this.listenPrivacySettingChange()
this.curPageShow()
@@ -119,7 +120,9 @@ export default {
*/
handlePrivacyShow() {
// 互斥: 只有一个实例能消费成功
if (!PrivacyManager.consumePendingShow()) return
if (!PrivacyManager.consumePendingShow()) {
return;
}
this.showPrivacyPopup()
},
@@ -139,7 +142,10 @@ export default {
showPrivacyPopup() {
// 先通知其他实例关闭
try { uni.$emit('privacy:forceClose'); } catch (e) {}
// 再弹出本实例
// 直接弹窗: boot 阶段已经保证 bundle 完整
// 完整性校验在 lang.js _bootImpl() 中完成(检查 defaultAppNameMp 等关键 key)
// 不完整时 force 重试, 仍不完整则报错不静默回退
// 所以这里无需再检测/自愈, 信任 boot 已就绪
this.popUp()
},
@@ -335,21 +341,29 @@ export default {
* mixin i18n-lazy 要求的方法
* 返回一个对象, key 与 data().i18n 字段一一对应
* 调用时机在 popUp() 中(此时 boot 已完成, $lang() 返回真实翻译)
*
* 这里**不做任何兜底**:
* - 无 hardcode fallback
* - 不依赖本地 lang
* - 直接信任 boot 阶段的完整性校验结果
* - 若 boot 阶段校验失败, $lang() 会返回空串, UI 显示空文案(按钮可点)
*/
i18nPrepareFn() {
let appName, privacyContractName, privacyServiceName;
// 关键: appName 必须先解析为字符串, 才能作为 descPrefix/descSuffix 的参数
// #ifdef H5 || MP-HARMONY
const appName = this.$lang('components.privacyPopup.defaultAppNameH5');
const privacyContractName = this.platformContractName || this.$lang('components.privacyPopup.defaultContractNameH5');
const privacyServiceName = this.$lang('components.privacyPopup.defaultServiceNameH5');
appName = this.$lang('components.privacyPopup.defaultAppNameH5');
privacyContractName = this.platformContractName || this.$lang('components.privacyPopup.defaultContractNameH5');
privacyServiceName = this.$lang('components.privacyPopup.defaultServiceNameH5');
// #endif
// #ifdef MP-WEIXIN
const appName = this.$lang('components.privacyPopup.defaultAppNameMp');
const privacyContractName = this.platformContractName || this.$lang('components.privacyPopup.defaultContractNameMp');
const privacyServiceName = '';
appName = this.$lang('components.privacyPopup.defaultAppNameMp');
privacyContractName = this.platformContractName || this.$lang('components.privacyPopup.defaultContractNameMp');
privacyServiceName = '';
// #endif
return {
const result = {
title: this.$lang('components.privacyPopup.title'),
descPrefix: this.$lang('components.privacyPopup.descPrefix', { appName }),
descSuffix: this.$lang('components.privacyPopup.descSuffix', { appName }),
@@ -359,12 +373,13 @@ export default {
agreeBtn: this.$lang('components.privacyPopup.agreeBtn'),
disagreeTip: this.$lang('components.privacyPopup.disagreeTip'),
};
return result;
},
popUp() {
// 弹窗前**一次性**计算所有 i18n 文案
// 此时 App.vue 已 await boot() + await PrivacyManager.show() 完成,
// Lang.boot() 必然已完成, $lang() 返回真实翻译(效果与 H5 一致)
// boot 阶段已用 isBundleComplete() 保证 bundle 完整
// $lang() 返回真实翻译(效果与 H5 一致)
this.prepareI18n();
this.showPop = true;
},