Node.js中如何通过模块名/目录字符串自动导入导出模块?
自动导入并导出目录下的模块
我的文件夹结构如下:
modules module-and index.js module-not index.js module-or index.js module-xor index.js moduleBundler.js
我正在编写moduleBundler.js,希望自动从各个模块文件夹导入模块并统一导出,而非像下面这样硬编码:
import ModuleAnd from "./module-and"; import ModuleNot from "./module-not"; import ModuleOr from "./module-or"; import ModuleXor from "./module-xor"; export { ModuleAnd, ModuleNot, ModuleOr, ModuleXor };
我已经通过以下代码获取到所有模块的目录和对应导出名称:
const moduleDirectories = getDirectories(__dirname); const moduleNames = moduleDirectories.map(x => x.slice(0, 1).toUpperCase() + camelise(x).slice(1)); console.log(moduleDirectories); >>> [ 'module-and', 'module-not', 'module-or', 'module-xor' ] console.log(moduleNames); >>> [ 'ModuleAnd', 'ModuleNot', 'ModuleOr', 'ModuleXor' ]
但尝试动态导入时遇到了问题:
- 循环直接导入报错:
for (const i in moduleNames) { import moduleNames[i] from ("./" + moduleDirectories[i]); }
报错:ReferenceError: from is not defined
- 用
eval()测试也失败:
for (const [key, value] of Object.entries(moduleNames)) { const directory = "./" + moduleDirectories[parseInt(key)]; eval(`import ${value} from "${directory}"`); }
报错:SyntaxError: Cannot use import statement outside a module
我不想在package.json里加"type": "module",也希望完全避免用eval()。请问该如何实现动态导入并统一导出这些模块?
解决方案(CommonJS环境)
既然你不想启用ES模块模式,直接用CommonJS的require()就能实现动态导入,再构建导出对象即可:
const fs = require('fs'); const path = require('path'); // 实现getDirectories函数(如果还没写) function getDirectories(dir) { return fs.readdirSync(dir, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); } // 实现camelise函数(如果还没写) function camelise(str) { return str.replace(/-([a-z])/g, (match, char) => char.toUpperCase()); } const moduleDirectories = getDirectories(__dirname); const moduleNames = moduleDirectories.map(x => x.slice(0, 1).toUpperCase() + camelise(x).slice(1)); // 构建导出对象 const exportsObj = {}; moduleDirectories.forEach((dir, index) => { const modulePath = path.join(__dirname, dir); // 导入模块的默认导出 const moduleDefault = require(modulePath).default; exportsObj[moduleNames[index]] = moduleDefault; }); // 导出所有模块 module.exports = exportsObj;
之后其他文件就能通过const { ModuleAnd, ModuleNot } = require('./moduleBundler')来按需导入模块。
补充:如果后续考虑ES模块模式
如果之后愿意启用ES模块(在package.json中添加"type": "module"),可以用动态import()函数实现,完全不需要eval():
import fs from 'fs/promises'; import path from 'path'; // 实现camelise函数 function camelise(str) { return str.replace(/-([a-z])/g, (match, char) => char.toUpperCase()); } async function getModules() { const moduleDirectories = await fs.readdir(__dirname, { withFileTypes: true }) .then(dirents => dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name)); const moduleNames = moduleDirectories.map(x => x.slice(0, 1).toUpperCase() + camelise(x).slice(1)); const exportsObj = {}; for (let i = 0; i < moduleDirectories.length; i++) { const modulePath = `./${moduleDirectories[i]}`; const module = await import(modulePath); exportsObj[moduleNames[i]] = module.default; } return exportsObj; } // 导出包含所有模块的对象(需异步导入) export default await getModules();
这种方式下,导入该bundler的地方需要用import bundler from './moduleBundler',且因为是异步操作,可能需要在异步函数中使用。
内容的提问来源于stack exchange,提问作者JoshuaAGK
相关产品推荐
相关产品推荐

