使用Top-level await新特性,按需加载国际化资源文件,提高首屏效率

我正在参加「掘金·启航计划」

前言

现在很多项目都需要支持国际化,多语言,即页面会默认显示浏览器的语言词条,也可以自行在页面上切换语言。

有很多封装好的第三方国际化库,这里举一个简单例子:通过读取各语言的json格式国际化资源文件,然后封装common get方法获取词条value。

// i18n/en/a.en.json
{
    "I18N_A_Key1": "value1",
    "I18N_A_Key2": "value2",
    "I18N_A_Key3": "value3"
}

// i18n/en/index.js
import a from './a.en.json';
import b from './b.en.json';

export default { ...a, ...b };

// i18n/index.js
import en from './en';
import zh from './zh';
const lang = localStorage.getItem('lang') || 'en';
let json = {};
switch (lang) {
    case 'zh':
        json = zh;
        break;
    case 'en':
    default:
        json = en;
        break;
}
export default {
    get: (key) => {
        return json[key];
    }
};

// page.js
import i18n from './i18n';
const text = i18n.get('I18N_A_Key1');

但是这样会有个问题,如果国际化词条过多,资源文件过大,会每次进页面时,都需要加载所有语言的资源,而页面上只需要一种语言,那其它语言的资源,都是unused resource,可能会很影响网站首屏加载速度。

所以需求是拆分国际化资源文件,并按需加载。

本文只研究多语言国际化资源文件的拆分及按需加载,是指国际化词条已经翻译之后,已经交付给开发后,开发这边需要做的工作。打包用的是webpack

import 动态加载

封装成aync方法,然后顶层调用。

let json = {};
async function load() {
    const lang = localStorage.getItem('lang') || 'en';
    switch (lang) {
        case 'zh':
            const zh = await import('./zh');
            json = zh.default;
            break;
        case 'en':
        default:
            const en = await import('./en');
            json = en.default;
            break;
    }
}
load();

export default {
    get: (key) => {
        return json[key];
    }
};

webpack-bundle-analyzer分析下,资源文件拆分了,满足需求。

使用Top-level await新特性,按需加载国际化资源文件,提高首屏效率

但是这种毕竟是异步,会有延迟,如果跟页面组件同时加载,那肯定会有get不到value的情况。 如果想做到执行先后顺序,那就得在async结束后,再加载页面组件,不仅会影响首屏速度,也增加了逻辑复杂度。

Top-level await

developer.mozilla.org/zh-CN/docs/…

是一个ECMAScript提案 Top-level await,允许开发者在 async 函数外使用 await 字段,目前已进入Stage 4阶段。

之前:

// utils.js
let result = '';
async function asyncGet() {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    result = 'result';
}
export { asyncGet, result };

// page.js
import { asyncGet, result } from './utils';
alert(result); // => ''
asyncGet().then(() => {
    alert(result); // => 'result'
});

现在:

// utils.js
...
await asyncGet();
export { asyncGet, result };

// page.js
import { result } from './utils';
alert(result); // => 'result'

之前需要执行下异步方法,然后callback回来后才能取到结果;现在用了Top-level await,支持顶层调用await,也就是说会等待await执行结束后,才会继续执行page.js下面的代码,这样就不用自己控制执行顺序了。

用在上例国际化里:

let json = {};
async function load() {
    ... // 同上例
}
await load();

export default {
    get: (key) => {
        return json[key];
    }
};

webpack-bundle-analyzer分析下,资源文件拆分了,满足需求。

使用Top-level await新特性,按需加载国际化资源文件,提高首屏效率

这样就可以保证在国际化资源文件加载时候,json都已经写在全局变量里了之后,才会加载页面组件,这样就能保证每次get都能取到值。

上例更简便写法,webpack打包时会按路径自动检索文件夹内的文件,然后进行拆分打包,用了魔法注释给拆分的文件起名。

const lang = localStorage.getItem('lang') || 'en';
const result = await import(/* webpackChunkName: "i18n" */ `./${lang}/index`);
const json = result.default;

export default {
    get: (key) => {
        return json[key];
    }
};

使用Top-level await新特性,按需加载国际化资源文件,提高首屏效率

这样就实现了资源文件的打包拆分,并且按需加载,比如当前语言是en刷新页面进来就只会加载英语资源文件,语言是zh就只加载中文。会有效提升首屏加载速度。

Top-level await 虽然是提案阶段,但是大部分主流浏览器都已经支持了:caniuse.com/mdn-javascr…

另外,webpack默认的不支持 Top-level await,如果想支持需要在config文件里打开设置:

// webpack.config.js
experiments: { topLevelAwait: true }

总结

本文只是探讨一种资源文件按需加载的两种方式,也不一定是最优解,比如官方对Top-level await的支持还不算完美,还存在很多问题及风险。如果有更好的解决方案,欢迎分享。

原文链接:https://juejin.cn/post/7231457691679211557 作者:Mark大熊

(0)
上一篇 2023年5月11日 上午10:16
下一篇 2023年5月11日 上午10:26

相关推荐

发表回复

登录后才能评论