提问人:Morgan 提问时间:11/11/2023 更新时间:11/12/2023 访问量:69
如何有条件地修改 ES6 Javascript 模块的导出
How to conditionally modify exports from an ES6 Javascript module
问:
我想知道是否有可能以某种方式仍然从模块内访问 ES6 模块的模块导出,就像您在 CommonJS 中使用 module.exports 所做的那样。
为了清楚起见,我有一个js模块(Config.js),我用它来导出我所有的配置变量。
export const DatabaseName = "myDbName";
export const DatabasePort = 3000;
export const DatabaseHosts = ["174.292.292.32"];
export const MaxWebRequest = 50;
export const MaxImageRequests = 50;
export const WebRequestTimeout = 30;
etc...
然后我有一个单独的Dev.Config.js文件,它只包含我的开发环境的覆盖。
export const DatabaseHosts = ["localhost"];
export const DatabasePort = 5500;
在我的主 Config.js 文件中,我的底部有这个逻辑。
try {
var environmentConfig = `./${process.env.NODE_ENV}.Config.js`;
var localConfig = require(environmentConfig)
module.exports = Object.assign(module.exports, localConfig)
} catch (error) {
console.log("Error overriding config with local values. " + error)
}
最后,在我的消费代码中,我可以像这样导入我的 config.js 文件
import * as Config from "./Config.js";
console.log(Config.DatabaseHosts) // Gives me the correct "overridden" value on my dev environment
目前,我一直在使用 babel 将我的代码全部转译回 CommonJS,我想这就是我能够混合和匹配导入/导出语法的方式,并且仍然像我上面所做的那样引用 module.exports。
我的问题是,我如何在纯 ES6 模块中复制此模式,而无需使用 babel 进行转译,因为我无法从模块本身中修改我的 module.exports?
答:
ESM 不支持条件导出模式。
为了使用来自另一个模块的动态导入来修改导出的值,该模块的说明符派生自环境变量(在尝试...catch
语句,以便失败的尝试不会在顶层抛出未捕获的异常),您可以修改导出的结构,以便将它们公开为对象上的属性。下面是一个可重现的例子来演示:
./package.json
:
{
"name": "so-77465699",
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "NODE_ENV=Dev node main.js",
"prod": "NODE_ENV=production node main.js"
},
"license": "MIT"
}
./Dev.Config.js
:
export const Config = {
DatabaseHosts: ["localhost"],
DatabasePort: 5500,
};
export default Config;
./Config.js
:
const Config = {
DatabaseName: "myDbName",
DatabasePort: 3000,
DatabaseHosts: ["174.292.292.32"],
MaxWebRequest: 50,
MaxImageRequests: 50,
WebRequestTimeout: 30,
};
try {
// Import a module specifier based on
// the value of the NODE_ENV environemnt variable.
// Destructure and rename the default export:
const { default: envConfig } = await import(
import.meta.resolve(`./${process.env.NODE_ENV}.Config.js`)
);
// Iterate the keys and values, updating the existing Config object:
for (const [key, value] of Object.entries(envConfig)) {
Config[key] = value;
}
} catch (cause) {
console.log(`Error overriding config with local values: ${cause}`);
}
export { Config, Config as default };
./main.js
:
// Import named export:
import { Config } from "./Config.js";
// Alternatively, since it's also the default export:
// import { default as Config } from "./Config.js";
// Or, using syntax sugar:
// import Config from "./Config.js";
console.log(Config.DatabaseHosts);
在终端中:
% node --version
v20.9.0
% npm run dev
> [email protected] dev
> NODE_ENV=Dev node main.js
[ 'localhost' ]
% npm run prod
> [email protected] prod
> NODE_ENV=production node main.js
Error overriding config with local values: Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/node/so-77465699/production.Config.js' imported from /Users/node/so-77465699/Config.js
[ '174.292.292.32' ]
评论
export default await setup()
在 ES6 模块中无法动态构建/覆盖导出对象。即使是有条件的导入也已经需要顶级或工具支持。await
但是,您可以做的是弄乱声明和:let
eval
export const DatabaseName = "myDbName"; // keep things as `const` to prevent overriding them
export let DatabasePort = 3000;
export let DatabaseHosts = ["174.292.292.32"];
export let MaxWebRequest = 50;
export let MaxImageRequests = 50;
export const WebRequestTimeout = 30;
… // etc
try {
var environmentConfig = `./${process.env.NODE_ENV}.Config.js`;
var localConfig = await import(environmentConfig)
for (const [name, value] of Object.entries(localConfig)) {
eval(`${name} = value;`);
}
} catch (error) {
console.log("Error overriding config with local values. " + error)
}
不过我不推荐这个。仅当无法更改 Config.js 模块的使用方式,或者导出的变量太多而替代方法不可行时,才使用此方法。
相反,我建议您创建一个单独的模块来合并配置,尽管这需要拼写两次配置名称:
// Default.Config.js
export const DatabaseName = "myDbName";
export const DatabasePort = 3000;
export const DatabaseHosts = ["174.292.292.32"];
export const MaxWebRequest = 50;
export const MaxImageRequests = 50;
export const WebRequestTimeout = 30;
// Dev.Config.js
export const DatabaseHosts = ["localhost"];
export const DatabasePort = 5500;
// Config.js
import * as defaultConfig from "./Default.Config.js";
const localConfig = await import(`./${process.env.NODE_ENV}.Config.js`).catch(error => {
console.log("Error overriding config with local values. " + error);
return {};
});
export const {
DatabaseName,
DatabasePort,
DatabaseHosts,
MaxWebRequest,
MaxImageRequests,
WebRequestTimeout,
… // etc
} = Object.assign({}, defaultConfig, localConfig);
或者将配置模块更改为默认导出对象,您可以任意操作这些对象。但是,您失去了使用命名导入和获得静态验证的能力。
评论
export const …
评论
dotenv