【问题标题】:How to initialize a shared javascript module default export如何初始化共享的 javascript 模块默认导出
【发布时间】:2021-04-08 23:33:23
【问题描述】:

我想在多个模块之间共享一个 api 实例,并能够使用外部配置对其进行初始化。我的代码使用 Webpack 和 Babel 将那些漂亮的 ES6 模块转换为浏览器可用的东西。我正在努力实现这一目标:

// api.js
let api = null;
export default api;
export function initApi(config) {
  // use config to configure the shared api instance (e.g. with api base url)
  api = ...
}


// ======================
// entry.js
import { initApi } from './api';
import App from './App';

// Initialize the single shared instance before anyone has the chance to use it
const apiConfig = ...
initApi(apiConfig);

// Create the app and run it


// ======================
// App.js
// RootComponent has an import dependency chain that eventually imports DeeplyNestedComponent.js
import RootComponent from './RootComponent';

// Actual App code not important


// ======================
// DeeplyNestedComponent.js
// PROBLEM! This "assignment" to the api var happens before initApi is run!
import api from '../../../../api';

api.getUser(123); // Fails because "api" stays null forever even after the initApi() call

出现“问题”是因为 ES6 模块是静态导入的,并且导入语句被提升。换句话说,简单地将import App from './App' 行移到initApi(apiConfig) 下方并不会在调用initApi 之后进行导入。

解决此问题的一种方法是从api.js(如果我有多个具有相同模式的此类共享对象,则在另一个globals.js文件中)导出一个对象,而不是像这样的单个变量:

// api.js
const api = {
  api: null,
};
export default api;
export function initApi(config) {
  // use config to configure the shared api instance (e.g. with api base url)
  api.api = ... // <-- Notice the "api." notation
}


// ======================
// DeeplyNestedComponent.js
// api is now the object with an empty "api" property that will be created when initApi() is called
import api from '../../../../api';

api.api.getUser(123); // <-- Ugh :(

有没有办法在使用 ES6 模块时优雅地实现共享服务实例的初始化?

在我的情况下,DeeplyNestedComponent.js 必须仍然 import 以某种方式使用 api 实例。换句话说,不幸的是,没有任何上下文对象从App 一直传递到DeeplyNestedComponent.js,可以访问 api 实例。

【问题讨论】:

    标签: javascript ecmascript-6 module


    【解决方案1】:

    你的代码的问题是

    let api = null;
    export default api;
    

    在默认导出的隐式生成绑定中导出 null。但是,您也可以使用语法导出名为 default 的任意绑定

    let api = null;
    export { api as default };
    

    这将按预期工作。但是在调用 initApi 之前,您仍然需要确保没有模块访问此导出。

    【讨论】:

    • 感谢您的回答。我尝试了替代语法以防万一,它给出了相同的结果。由于导入是静态的和提升的,我如何才能延迟访问此导出直到调用initApi 之后?这是我问题的核心。
    • 不要在模块的顶级代码中访问它,而只能在函数内部访问,然后在调用initApi之前不要调用这些函数。
    • 唯一的选择是以正确的顺序加载你的模块,即首先调用initApi()的模块(不是entry.js)然后是使用@的模块987654329@
    • 我在最初测试您的建议时一定做错了什么。我刚刚重试了它,它现在似乎可以工作了。郑重声明,在调用initApi 之前,我不会在任何地方访问api。我会花更多的时间在这方面,因为我没想到你的建议真的会有所作为!显然有一些我不明白的地方。
    • "export default «expression»; 等同于 const __default__ = «expression»; export { __default__ as default };" - 是的,完全正确。当你的 «表达式» (api) 被评估时,它仍然有 null 的值,所以 __default__ 被初始化为 null 并且稍后重新分配 api 不会改变导出的常量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-27
    • 2020-05-21
    • 2012-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-29
    相关资源
    最近更新 更多