Node.js中基于类(使用import语法)与基于函数(使用require语法)编程的最佳实践及import与require选型疑问
Great question—this is something a lot of Node.js developers grapple with as the ecosystem has shifted toward modern JavaScript standards while still supporting legacy patterns. Let’s break this down into two clear parts: module system choice (import vs require) and programming paradigm (class-based vs function-based).
import vs require First, let’s clarify the basics:
require/module.exportsbelongs to CommonJS (CJS), Node.js's original module system.import/exportbelongs to ES Modules (ESM), the official standard JavaScript module system used across browsers and modern Node.js.
Best Practice for Module System
Prioritize ESM (import/export) for new projects—here’s why:
- It’s universal across all JavaScript environments (browser + Node.js), making your code more portable and consistent.
- Supports modern features like tree-shaking (reduces bundle size by removing unused code), top-level
await, and dynamicimport(). - It aligns with future JavaScript developments, so you won’t be tied to a legacy system long-term.
When to stick with CommonJS (require):
- If you’re maintaining an older codebase that relies heavily on CJS-only packages or tools that haven’t fully migrated to ESM.
- For niche libraries that still only support CJS (though this is becoming increasingly rare).
To switch to ESM:
- Add
"type": "module"to yourpackage.json. - Use
.mjs/.cjsfile extensions if you need to mix ESM and CJS in the same project.
There’s no absolute "best" choice here—it depends on your use case, team conventions, and the problem you’re solving.
When to Use Class-Based Programming
- Stateful objects: Use classes when you need to encapsulate state and related behavior (e.g., a database service holding a connection pool, or an auth manager tracking session data). Example:
import { Pool } from 'pg'; class DatabaseService { constructor() { this.pool = new Pool({ connectionString: process.env.DATABASE_URL }); } async getUserById(id) { const result = await this.pool.query('SELECT * FROM users WHERE id = $1', [id]); return result.rows[0]; } } export default DatabaseService; - OOP patterns: If your project benefits from inheritance, polymorphism, or clear encapsulation (common in larger codebases where organizing related logic into classes boosts maintainability).
When to Use Function-Based Programming
- Stateless logic: For pure functions that don’t rely on or modify external state (e.g., utility functions, data transformers, Express middleware). Example:
import { validationResult } from 'express-validator'; const validateRequest = (req, res, next) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } next(); }; export { validateRequest }; - Functional patterns: If you prefer immutability, function composition, or pipeline-style data processing. Function-based code is often more concise and easier to test when it’s pure.
Pro Tip
Many modern Node.js/Express projects mix both paradigms! For example, you might use a class to manage a stateful service, but write function-based middleware and utility helpers to support it. The key is to keep your code consistent within your team and choose the pattern that makes the most sense for the task at hand.
- Module system: Go with ESM (
import/export) for new projects—it’s the future of JavaScript. Use CommonJS only if you have to support legacy code. - Programming paradigm: Choose classes for stateful, encapsulated logic; choose functions for stateless, reusable logic. Mix them as needed, but prioritize consistency across your codebase.
内容的提问来源于stack exchange,提问作者Suraj Narule

