
本文旨在帮助开发者在使用ES模块(`type: "module"`)的项目中,解决 `app.use()` 中动态引入路由模块时,如何使用 `import` 替代 `require` 的问题。我们将提供一种简洁有效的解决方案,并解释其背后的原理,确保你的Express服务器能够正确加载路由。
在使用ES模块(type: "module")的Node.js项目中,require 函数不再可用,我们需要使用 import 来引入模块。然而,在 app.use() 中动态引入路由模块时,直接替换 require 可能会遇到一些问题。本文将提供一种解决方案,并解释其背后的原理。
问题分析
原始代码使用 fs.readdirSync 同步读取路由目录,然后使用 require 动态引入每个路由文件。在使用 ES 模块后,我们需要将 require 替换为 import。 直接使用 import 在 app.use 的回调函数中会导致服务器挂起,因为 import 是一个异步操作。
解决方案
可以使用顶层 await 结合循环来解决这个问题。 顶层 await 允许我们在模块的顶层使用 await 关键字,而无需将其包裹在 async 函数中。
代码示例
import express from "express";
import fs from "fs";
const app = express();
const port = 8000; // 建议明确定义端口号
// 使用 for...of 循环和顶层 await
for (const r of fs.readdirSync("./routes")) {
const routeModule = await import(`./routes/${r}`);
app.use("/api", routeModule.default);
}
app.listen(port, () => console.log(`Server running on port ${port}`));代码解释
- fs.readdirSync("./routes"): 同步读取 ./routes 目录下的所有文件。
- for (const r of ...): 使用 for...of 循环遍历读取到的文件名。
- const routeModule = await import(\./routes/\${r}`)`: 使用 import() 动态导入路由模块。await 关键字确保在导入模块完成后再执行后续代码。
- app.use("/api", routeModule.default): 将导入的路由模块的默认导出(default)作为中间件添加到Express应用中。 路由模块通常会导出一个Express Router实例作为默认导出。
注意事项
- 确保你的 package.json 文件中设置了 "type": "module",以启用 ES 模块支持。
- 路由文件应该导出 Express Router 实例作为默认导出 (export default router;)。
- 使用顶层 await 需要 Node.js 14.8+ 或更高版本。
- 如果你的路由文件依赖于异步操作,请确保在路由处理函数中使用 async 和 await。
示例路由文件 (routes/user.js)
import express from "express";
import { getUser, createUser } from "../controllers/userController.js";
const router = express.Router();
router.get("/:id", getUser);
router.post("/", createUser);
export default router;总结
通过使用顶层 await 和 import() 函数,我们可以轻松地在 ES 模块项目中动态加载路由模块,并将其添加到 Express 应用中。 这种方法简洁有效,避免了回调地狱和服务器挂起的问题。 确保你的代码符合 ES 模块规范,并注意 Node.js 版本要求,以获得最佳的开发体验。










