feat(i18n): 实现remote-only打包模式,优化首屏加载和产物体积
1. 新增remote-only模式下的空语言包stub 2. 新增缓存有效期配置并对齐后端Cache::set 600s 3. 重构语言包加载逻辑,适配remote-only模式 4. 添加webpack替换插件,排除本地lang目录减少产物体积 5. 优化Vue挂载时机,等待语言包就绪后再渲染首屏 6. 改进缓存策略,新增内存缓存TTL校验和语种缓存淘汰 7. 修复语言包缓存key生成逻辑,支持uniacid和appType隔离
This commit is contained in:
@@ -575,8 +575,10 @@ function getOverrideStorageKey(lang) {
|
||||
* 内存 cache key: <uniacid>_<appType>_<lang>
|
||||
* 必须与 storage key 三维一致 — 切租户时内存不会"巧合"命中旧值
|
||||
*/
|
||||
function getOverrideMemKey(lang) {
|
||||
return `${getCurrentUniacid()}_${getCurrentAppType()}_${lang}`;
|
||||
function getOverrideMemKey(lang, uniacid, appType) {
|
||||
const u = uniacid !== undefined ? uniacid : getCurrentUniacid();
|
||||
const a = appType !== undefined ? appType : getCurrentAppType();
|
||||
return `${u}_${a}_${lang}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -833,8 +835,19 @@ export default {
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
const { tree, version, source } = await loadBundleTree({ lang });
|
||||
remoteTreeCache[getOverrideMemKey(lang)] = { version, tree, ts: Date.now(), source };
|
||||
const uniacid = getCurrentUniacid();
|
||||
const appType = getCurrentAppType();
|
||||
let result = await loadBundleTree({ lang, appType, uniacid });
|
||||
// 只要不是从远端获取的(memory/cache),都强制刷新一次,避免旧缓存结构导致 missing key
|
||||
if (result.source !== 'remote') {
|
||||
try {
|
||||
result = await loadBundleTree({ lang, appType, uniacid, force: true });
|
||||
} catch (e) {
|
||||
// 网络刷新失败仍用缓存兜底
|
||||
}
|
||||
}
|
||||
const { tree, version, source } = result;
|
||||
remoteTreeCache[getOverrideMemKey(lang, uniacid, appType)] = { version, tree, ts: Date.now(), source };
|
||||
setRemoteStatus('ok', { lang });
|
||||
// bundle 就绪后清空字段缓存并触发全局刷新,避免首屏组件在 boot 前渲染的 raw key 被缓存
|
||||
emitUpdated(lang);
|
||||
@@ -929,7 +942,7 @@ export default {
|
||||
getRemoteStatus() { return remoteStatus; },
|
||||
|
||||
/**
|
||||
* 当前 source 配置(规范化后):'local' | 'remote' | 'hybrid'
|
||||
* 当前 source 配置(规范化后):'local' | 'remote' | 'hybrid' | 'remote-only'
|
||||
*/
|
||||
getSource() { return SOURCE; },
|
||||
|
||||
@@ -1116,30 +1129,46 @@ export default {
|
||||
invalidateStorageCache('lang');
|
||||
const locale = getCurrentLocale();
|
||||
|
||||
// 切语言后清除语言包合并缓存 + 字段解析缓存
|
||||
// 切语言后清除语言包合并缓存 + 字段解析缓存(这些是翻译结果缓存,必须清)
|
||||
invalidateLangCaches();
|
||||
|
||||
// 清空已加载的"非新语种"语言包缓存
|
||||
// 用 Object.keys 防止被原型链污染(for...in 会遍历继承属性)
|
||||
// 策略优化: 保留旧语种的 bundle/override 缓存,只在内存中限制最大语种数
|
||||
// 这样切回旧语种时能直接命中缓存,不重复请求
|
||||
// 仅清空 loadedLangPacks 的合并结果缓存(非远端原始数据)
|
||||
Object.keys(loadedLangPacks).forEach(key => {
|
||||
if (!key.startsWith(locale + '_')) delete loadedLangPacks[key];
|
||||
});
|
||||
// 清空内存里的"非新语种"远端覆写 — 防止反复切语种导致内存泄漏
|
||||
Object.keys(overrideCache).forEach(mk => {
|
||||
if (!mk.endsWith('_' + locale)) delete overrideCache[mk];
|
||||
});
|
||||
// remote-only 模式:清除旧语种 bundle 缓存
|
||||
Object.keys(remoteTreeCache).forEach(mk => {
|
||||
if (!mk.endsWith('_' + locale)) delete remoteTreeCache[mk];
|
||||
});
|
||||
|
||||
// 限制 remoteTreeCache 最大保留 3 个语种,超出时淘汰最旧的
|
||||
const maxLocaleCache = 3;
|
||||
const cachedLocales = Object.keys(remoteTreeCache);
|
||||
if (cachedLocales.length >= maxLocaleCache) {
|
||||
// 按时间戳排序,淘汰最旧的非当前语种
|
||||
const sorted = cachedLocales
|
||||
.filter(mk => !mk.endsWith('_' + locale))
|
||||
.sort((a, b) => (remoteTreeCache[a].ts || 0) - (remoteTreeCache[b].ts || 0));
|
||||
while (sorted.length >= maxLocaleCache) {
|
||||
const evict = sorted.shift();
|
||||
delete remoteTreeCache[evict];
|
||||
}
|
||||
}
|
||||
|
||||
// 切换语种后,先 await 远端覆写(有 storage 缓存的同语种秒开,无缓存的会触发一次网络)
|
||||
// 远端失败/超时也返回 — await 拿到的就是 { ok:false, source:'local' },不会阻塞后续 refresh/reLaunch
|
||||
// 注意:loadOverrides 内部已 emitUpdated(若成功),refresh() 内部不再重复 emit
|
||||
if (IS_REMOTE_ONLY) {
|
||||
try {
|
||||
const { tree, version, source } = await loadBundleTree({ lang: value });
|
||||
remoteTreeCache[getOverrideMemKey(value)] = { version, tree, ts: Date.now(), source };
|
||||
const uniacid = getCurrentUniacid();
|
||||
const appType = getCurrentAppType();
|
||||
const { tree, version, source } = await loadBundleTree({ lang: value, uniacid, appType });
|
||||
remoteTreeCache[getOverrideMemKey(value, uniacid, appType)] = { version, tree, ts: Date.now(), source };
|
||||
setRemoteStatus('ok', { lang: value });
|
||||
// 新语种 bundle 就绪后,同步 App 全局 promise 并触发刷新
|
||||
try {
|
||||
const app = getApp();
|
||||
if (app && app.$langReady) app.$langReady = Promise.resolve({ ok: true, source, status: 'ok', version, lang: value });
|
||||
} catch (e) {}
|
||||
emitUpdated(value);
|
||||
} catch (e) { /* 静默 */ }
|
||||
} else if (SOURCE !== 'local') {
|
||||
try { await loadOverrides(value); } catch (e) { /* 静默 */ }
|
||||
|
||||
Reference in New Issue
Block a user