предисловие
В данной работе для ситуации, когда интернационализированных данных перевода много и они хранятся на сервере, рассматривается оптимизация и пишется плагин.GitHub.com/Feather Letter16449196…, для справки или использования. Если есть что-то, что нужно улучшить, я надеюсь, вы меня поправите.
фон спроса
В нашем одностраничном приложении есть две точки оптимизации функции интернационализации:
-
Интернационализированные данные перевода хранятся в базе данных. Каждый раз, когда вы переключаете языки или перезагружаете страницу, вам нужно один раз запросить интерфейс, и интерфейс, вероятно, будет задерживаться на 1 секунду, что сильно влияет на пользовательский опыт, особенно на загрузку первого экрана.
-
Поскольку все данные хранятся в базе данных, при загрузке первого экрана данные перевода недоступны перед интерфейсом запроса.
Оптимизация
Для первой проблемы используйте кеш
По второму вопросу я разделил данные перевода на две части:
статические данные перевода: Он написан на js и используется для некоторых основных данных перевода.Даже если сеть отключена, пользователь все равно может видеть обычную страницу.
Динамически переводить данные: Полученная из интерфейса эта часть данных может быть изменена пользователем через соответствующую страницу конфигурации.
Когда заходит оптимизированная страница, ее можно разделить на следующие этапы:
ключевой код
// 创建实例并调用
const translateManager = new TranslateManager({/*传参*/})
translateManager.update('en', (res) => {
// 触发相应的更新视图的逻辑
// 例如在vue中,使用了vue-i18n,你可以
i18n.setLocaleMessage(res.data);
});
class TranslateManager {
STORAGE_KEY: string
expireTime: number
requestFn: Function | null
staticTranslateData: object
/**
* 构造函数
* @param params
*/
constructor(params: constructorParams) {
this.expireTime = params.expireTime || Infinity; // 前端缓存有效时间
this.STORAGE_KEY = params.storageKey || 'TranslateManager';
this.requestFn = params.requestFn;
this.staticTranslateData = params.staticTranslateData || {};
}
setRequestFn(fn: Function) {
this.requestFn = fn;
}
/**
* 设置缓存
* @param {String} language
*/
setCache(language: string, data: object) {
// 请求完成后缓存
const storageData = jsonParse(localStorage.getItem(this.STORAGE_KEY)) || {};
storageData[language] = data;
storageData['time'] = new Date().getTime();
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(storageData));
}
/**
* 从缓存中读取
* @param language 语种
*/
getCache(language: string) {
const storageData = jsonParse(localStorage.getItem(this.STORAGE_KEY)) || {};
if (storageData && storageData['time'] && storageData[language]) {
const time = storageData['time'];
const now = new Date().getTime();
// 缓存只有两小时
if (now - time <= this.expireTime) {
return storageData[language] || null;
}
}
return null;
}
/**
* 获取后端的静态国际化数据
*/
getDynamicTranslateData(language: string) {
if (!this.requestFn) {
console.error('请先执行setRequestFn!');
return Promise.reject();
}
return this.requestFn({language}).then((res: object) => {
this.setCache(language, res);
return res;
});
}
/**
* 获取前后端交集后的国际化数据
* @param {*} language
*/
getMergeTranslateData(language: string) {
// 没缓存就发起请求
return this.getDynamicTranslateData(language).then((res: any) => {
const data = this.staticTranslateData[language] || {};
// 部分国际化写在了前端,把前端的国际化文件合并到后端返回的数据中
merge(res, data)
if (isEmptyResult(res)) {
return Promise.reject(new Error('locale empty !!'))
}
return res;
})
}
/**
* 主方法
* @param locale 语种
* @param callback 回调函数,用于订制自己触发的渲染逻辑
*/
update(locale: string, callback: Function) {
let staticData = this.staticTranslateData[locale];
const cacheData = this.getCache(locale) || {};
// 使用静态数据和缓存数据的并集,触发第一次视图更新
merge(cacheData, staticData);
callback(cacheData, 'first');
// 返回静态和动态数据的合集
return this.getMergeTranslateData(locale).then((res: object) => {
// 获取数据是否和动态数据不一致
if (res && !isEqual(res, cacheData)) {
callback(res, 'second')
}
})
}
}
продлевать
Вышеупомянутый метод был использован, и ожидаемый эффект был хорошо достигнут. Как раз, когда я писал эту статью, я вдруг обнаружил, что есть еще направление для дальнейшей глубокой оптимизации: динамические данные можно разбивать на разные модули и загружать пошагово. По логике собственного дела, например, он может основываться на разрешениях пользователей. Когда мои полномочия пользователя низкие и я могу просматривать только домашнюю страницу, я могу загрузить только данные перевода модуля домашней страницы; когда мои полномочия пользователя высоки и я захожу на соответствующую страницу, я загружаю данные соответствующего модуля в это время.
Суммировать
Идея предложенного оптимизационного решения на этот раз не сложная, но требует некоторого времени для реализации.Я построил колесо по идее этой оптимизации, которое очень удобно в использовании.Включает в себя демо и комментарии Добро пожаловать в звезду:GitHub.com/Feather Letter16449196…. Если вы считаете, что эта статья была вам полезна, ставьте лайк, спасибо~