我的用例

我在一个大型应用程序上工作,在该应用程序中,根据用户角色,我加载/导入不同的模块集。这是一个流星应用程序,前端装有Vue,vue-router和vue-i18n,但没有像vuex这样的商店。

每个模块都有自己的路由,翻译文件,API和UI。这就是为什么在显示主界面和导航之前,我需要检查每个模块及其翻译是否都已加载(否则,例如,与已卸载模块相关的导航项标签将不会被翻译,或者本地化的路线将返回404) 。

是否有一种尽可能简单的模式来确保所有内容都已加载?

我的代码和逻辑

我的用例比使用Promise.all afaik所能实现的更为复杂。

我尝试使用Promises.allthen()的组合来做出嵌套承诺。

总结起来,顺序是:


加载基本包
登录客户端
导入i18n语言文件(用于主捆绑包),然后导入每个模块
对于每个模块,在加载其语言文件并将其合并到相关的i18n消息中之后,我需要加载模块本身(本地化路由,UI ...)




主要装载部分

Accounts.onLogin(function (user) {
    let userRoles = Roles.getRolesForUser(Meteor.userId())
    let promises = []
    let lang = getDefaultLanguage()
    promises.push(loadLanguageAsync(lang))
    this.modulesReady = false
    for (let role of userRoles) {
        switch (role) {
        case "user":
            import { loadUserLanguageAsync } from "/imports/user/data/i18n"
            promises.push(loadUserLanguageAsync(lang).then(import("/imports/user/")))
            break
        case "admin":
            import { loadAdminLanguageAsync } from "/imports/admin/data/i18n"
            promises.push(loadAdminLanguageAsync(lang).then(import("/imports/admin/")))
            break
        default:
            break
        }
    }

    return Promise.all(promises).then(function (values) {
        this.modulesReady = true // my green flag, attached to the window object
    })
})


主要语言加载功能

const loadedLanguages = []

// Load i18n
Vue.use(VueI18n)
export const i18n = new VueI18n()


export const getDefaultLanguage = () => {
    let storedLanguage = window.localStorage.getItem(
        Meteor.settings.public.brand + "_lang"
    )
    return Meteor.user() && Meteor.user().settings && Meteor.user().settings.language
        ? Meteor.user().settings.language
        : // condition 2: if not, rely on a previously selected language
        storedLanguage
            ? storedLanguage
            : // or simply the browser default lang
            navigator.language.substring(0, 2)
}
export const loadLanguage = (lang, langFile) => {
    console.log("LOAD LANGUAGE " + lang)

    // we store agnostically the last selected language as default, if no user is logged in.
    window.localStorage.setItem(
        Meteor.settings.public.brand + "_lang",
        lang
    )
    loadedLanguages.push(lang)
    if (langFile) {
        i18n.setLocaleMessage(lang, Object.assign(langFile))
    }
    i18n.locale = lang


    return lang
}

export const loadLanguageModule = (lang, langFile) => {
    console.log("LOAD LANGUAGE MODULE" + lang)
    i18n.mergeLocaleMessage(lang, Object.assign(langFile))
    return lang
}

export function loadLanguageAsync(lang) {
    if (i18n.locale !== lang) {
        if (!loadedLanguages.includes(lang)) {
            switch (lang) {

            case "en":
                return import("./lang/en.json").then(langFile => loadLanguage("en", langFile))

            case "fr":
                return import("./lang/fr.json").then(langFile => loadLanguage("fr", langFile))

            default:
                return import("./lang/fr.json").then(langFile => loadLanguage("fr", langFile))
            }
        } else {
            console.log("Already loaded " + lang)

        }
        return Promise.resolve(!loadedLanguages.includes(lang) || loadLanguage(lang))
    }
    return Promise.resolve(lang)
}


用户模块语言加载

const userLoadedLanguages = []

export default function loadUserLanguageAsync(lang) {
    if (i18n.locale !== lang || !userLoadedLanguages.includes(lang)) {
        switch (lang) {
        case "en":
            return import("./lang/en.json").then(langFile => loadLanguageModule("en", langFile))
        case "fr":
            return import("./lang/fr.json").then(langFile => loadLanguageModule("fr", langFile))
        default:
            return import("./lang/fr.json").then(langFile => loadLanguageModule("fr", langFile))
        }
    }
    return Promise.resolve(i18n.messages[lang].user).then(console.log("USER LANG LOADED"))
}



加载完每个模块后,我将切换一个标志,该标志允许我的路由器导航卫士继续进行所需的路线(请参阅主要的加载部分)。


路由器保护并等待异步功能

router.beforeEach((to, from, next) => {
     isReady().then(
        console.log("NEXT"),
        next()
    )
})
async function isReady() {
    while (true) {
        if (this.modulesReady) { console.log("READY"); return }
        await null // prevents app from hanging
    }
}


我对异步逻辑还很陌生,因此我很难确定自己在做什么错。这里的代码使浏览器崩溃,因为我猜我的promise值不合适,并且出现在无限的isReady()循环中。

我非常欢迎关于更好/正确方法的建议或建议。此外,如果缺少某些内容,请随时索取更多详细信息。

谢谢!

最佳答案

如何将动态导入与流星链接在一起?


首先在Promise链上考虑以下答案:How do I access previous promise results in a .then() chain?

如果您更喜欢异步/等待样式,则可以在此处进行后续操作:可以在await中使用async function调用动态导入。这使您有机会包装代码同步样式并在最终的Promise中解决所有问题:

考虑相对项目路径/imports/lang.json上的一个简单JSON文件:

{
  "test": "value"
}


还有一些示例在路径/imports/testObj.js上导出常量:

export const testObj = {
  test: 'other value'
}


您可以使用如下异步函数动态导入这些(例如client/main.js中的示例):

async function imports () {
  const json = await import('../imports/lang.json')
  console.log('json loaded')
  const { testObj } = await import('../imports/test')
  console.log('testObj loaded')
  return { json: json.default, testObj }
}

Meteor.startup(() => {
  imports().then(({ json, testObj }) => {
    console.log('all loaded: ', json, testObj)
  })
})


这将按顺序打印

json loaded
testObj loaded
all loaded: Object { test: "value" } Object { test: "other value" }


由于您的代码示例非常复杂且几乎不可复制,因此我建议您考虑采用这种方案以更同步的方式重写例程,以避免使用带有许多.then分支的Promises语法。

关于javascript - 如何将动态导入与 meteor 链接在一起?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56441647/

10-16 14:04