我怎样才能用流星链接动态导入?
How can I chain dynamic imports with meteor?
我的用例
我在一个大型应用程序上工作,根据用户角色,我 load/import 设置了不同的模块。这是一个流星应用程序,前端有 Vue、vue-router 和 vue-i18n,但没有像 vuex 这样的商店。
每个模块都有自己的路由、翻译文件,api & UI。这就是为什么我需要在显示主要 UI 和导航之前检查每个模块及其翻译是否已加载(否则,例如,与卸载模块相关的导航项标签将不会被翻译,或者本地化的路线将return 一个 404) .
是否有尽可能简单的模式来确保所有内容都已加载?
我的代码和逻辑
我的用例比我用 Promise.all
afaik 可以实现的更复杂。
我尝试使用 Promises.all
和 then()
的组合进行嵌套承诺。
总结一下,顺序是:
- 加载基本包
- 登录客户端
- 为主包导入 i18n 语言文件,然后为每个模块导入 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
}
}
我对异步逻辑还很陌生,我很难确定我做错了什么。这里的代码使浏览器崩溃,因为我猜我的承诺值不正确并且它进入无限 isReady()
循环。
我非常欢迎就 better/correct 的发展方向提出建议或建议。另外,如有遗漏,请随时索取更多详细信息。
谢谢!
How can I chain dynamic imports with meteor?
首先考虑 Promise 链上的这个答案:
如果您更喜欢 async/wait 风格,您可以在此处跟进:可以在 async function
中使用 await
调用动态导入。这使您有机会结束代码 sync-style 并在最终 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" }
由于您的代码示例非常复杂且几乎无法重现,因此我建议您考虑使用此方案以更 sync-style 的方式重写您的例程,以避免使用具有数十个 .then
分支的 Promises 语法.
我的用例
我在一个大型应用程序上工作,根据用户角色,我 load/import 设置了不同的模块。这是一个流星应用程序,前端有 Vue、vue-router 和 vue-i18n,但没有像 vuex 这样的商店。
每个模块都有自己的路由、翻译文件,api & UI。这就是为什么我需要在显示主要 UI 和导航之前检查每个模块及其翻译是否已加载(否则,例如,与卸载模块相关的导航项标签将不会被翻译,或者本地化的路线将return 一个 404) .
是否有尽可能简单的模式来确保所有内容都已加载?
我的代码和逻辑
我的用例比我用 Promise.all
afaik 可以实现的更复杂。
我尝试使用 Promises.all
和 then()
的组合进行嵌套承诺。
总结一下,顺序是:
- 加载基本包
- 登录客户端
- 为主包导入 i18n 语言文件,然后为每个模块导入 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
}
}
我对异步逻辑还很陌生,我很难确定我做错了什么。这里的代码使浏览器崩溃,因为我猜我的承诺值不正确并且它进入无限 isReady()
循环。
我非常欢迎就 better/correct 的发展方向提出建议或建议。另外,如有遗漏,请随时索取更多详细信息。
谢谢!
How can I chain dynamic imports with meteor?
首先考虑 Promise 链上的这个答案:
如果您更喜欢 async/wait 风格,您可以在此处跟进:可以在 async function
中使用 await
调用动态导入。这使您有机会结束代码 sync-style 并在最终 Promise 中解决所有问题:
考虑相对项目路径 /imports/lang.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" }
由于您的代码示例非常复杂且几乎无法重现,因此我建议您考虑使用此方案以更 sync-style 的方式重写您的例程,以避免使用具有数十个 .then
分支的 Promises 语法.