Node.js 开发的最佳实践
Node.js 的普及度正在快速增长,越来越多的独角兽公司开始采用这项技术,遵循某些约定以确保代码易于其他开发人员阅读和修改变得至关重要。您编写的代码也与您最终构建的产品的可扩展性成正比。
最佳实践 #1
选择分层方法:分离关注点
像 Express.js 这样的热门 Node.js 框架允许你将路由定义为回调函数,这些函数会在收到客户端请求时执行。这很容易让你将所有业务逻辑都写在一个函数里,但这会在不知不觉中变成一堆难以理解的混乱代码。
因此,在实施过程中应牢记“关注点分离”的编程原则。根据这一原则,我们应该在应用程序中使用不同的模块来处理不同的关注点。服务器端方面大致分为:
- 控制器: API 路由和端点
- 服务层:用于业务逻辑
- 数据访问层:用于处理数据库
控制器层
在此模块中,您只需编写 API 路由。在路由处理函数中,您可以解构 HTTP 请求对象以获取参数、查询参数、有效负载等,并将它们传递给服务层进行处理。
服务层
这一层包含业务逻辑,包含所有承担单一职责且可重用的方法。
数据访问层
这一层的主要作用是与数据库交互——从数据库获取数据、更新数据以及写入数据。所有数据库连接、模型、ODM/ORM 都定义在这里。
最佳实践 #2
文件夹结构:正确组织你的代码文件
在上一节中,我们讨论了如何将项目逻辑地模块化为多个层。为了实现这种抽象架构,我们需要一个合适的文件夹结构
src
├── app.js app entry point
├── /api controller layer: api routes
├── /config config settings, env variables
├── /services service layer: business logic
├── /models data access layer: database models
├── /scripts miscellaneous NPM scripts
├── /subscribers async event handlers
└── /test test suites
这里我们说的三个层是/apis(控制层)、/services和/models (数据访问层)。
/config层可以存储您的常量和应用程序配置/设置,如环境变量等。/scripts目录可用于存储自动化脚本,如部署管道。/tests将容纳您为应用程序编写的测试用例。
最佳实践 #3
发布者订阅者模型
发布者/订阅者模型可用于代码中两个实体之间的通信。发布者(消息发送者)通过特定渠道发送消息,而无需知道接收实体是谁。同样,订阅者(接收实体)则监听一个或多个渠道,而无需知道发布实体是谁。
在您的项目中实现此模型来管理与单个操作对应的多个子操作是一种很好的做法。假设您的应用中,在用户注册时,您会执行许多操作,例如创建数据库条目、生成授权密钥、发送确认邮件。如果您在单个服务函数中处理这些操作,函数代码往往会变得冗长而混乱。
代码结构如下:
export default class AuthService() {
async function signup(user) {
//1. create DB entry
//2. generate auth key
//3. send confirmation email
}
}
我们可以用发布/订阅模型很好地简化这一点。
发布/订阅模型可以使用 Node.js 事件 API
var events = require('events');
var eventEmitter = new events.EventEmitter();
export default class UserService() {
async function signup(user) {
// emit 'signup' event
eventEmitter.emit('signup', user.data)
}
}
为了处理此类事件的发出,你可以设置多个订阅者,它们本质上是事件监听器,等待特定事件的发出。这些订阅者可以根据其用途组织成单独的文件,并存储在 /subscribers 目录中。
// email.js
// ...
eventEmitter.on('signup', async ({ data }) => { // event listener
// send email
})
// auth.js
// ...
eventEmitter.on('signup', async ({ data }) => { // event listener
// generate auth key
})
最佳实践 #4
干净的代码和易读性:使用代码检查器、格式化程序和注释
- 代码检查和格式化
这里的主要目标是提高代码质量和可读性。linter 会警告语法错误(有时甚至是语义错误),而代码格式化则以更易读的方式设置代码样式。一些流行的 JavaScript linter 工具包括 Jslint 和 Eslint。对于代码格式化,Prettier 是一款著名的代码格式化工具。大多数代码编辑器(例如 Atom 和 VS Code)都提供了这些 linter 和格式化工具的插件。
- 添加评论
编写代码时,开发人员必须编写合适的注释,以便项目团队受益。简短而恰当地解释代码功能,可以避免其他开发人员的困惑,并节省他们的时间,最终节省整个团队的时间。下面的屏幕截图展示了一个合适的注释示例:
- 函数、变量和常量的正确命名法
您定义的函数、变量和常量的名称应该能够表明该函数(/变量/常量)的用途或重要性。变量命名不当可能会在团队成员和同行开发人员之间造成混淆。正确的命名还有助于您在检查内存快照时识别函数。
这里我们不清楚 items 数组中定义了哪些类型的项,而且 items 是一个很可能会频繁使用的常用术语。这是一个糟糕的命名法的例子。
函数名也与方法的功能不符。因为该函数既可以标记商品可用,也可以标记商品不可用,所以将其命名为“makeAvailable”似乎不太合适。
函数中的参数 n 只是一个字母“n”,并没有说明它的含义。
更好的代码应该是:
最佳实践 #5
编写异步代码:使用 Promises、Async/Await
JavaScript 以回调函数而闻名。回调函数允许你在 JavaScript 中定义异步行为。但随着代码中回调函数的增多,代码变得越来越笨重,导致了所谓的回调地狱。JavaScript
在 2015 年引入了 Promise,这是一种更简洁的处理异步行为的方式。此外,2017 年引入了 async/await 语法,进一步简化了代码。
因此,建议放弃使用回调函数,转而选择 Promise 或 Async/await。
这些语法使代码更具可读性,并且更易于查看代码流和调试。
举个例子,以下是同一段代码在回调和 Async/await 中的对比:
使用 Async/Await: 具有 async/await 语法的代码执行与回调完全相同的计算,但更易于阅读和调试。
最佳实践 #6
测试、日志和错误处理
测试
新手常常会忽视编写代码测试用例的重要性。然而,编写测试用例与编写代码同样重要,因为它可以发现哪怕是最小的错误,从而检查代码的有效性和准确性。
单元测试是大多数测试设置的基础。单元测试会将各个单元/组件与其他代码隔离进行测试,以验证其正确性。这样一来,您的代码就可以在(逻辑上)较低的层面上进行验证,确保每个内部组件都能按预期准确运行。
日志记录
日志记录是开发过程中至关重要的一部分,因为它有助于在发生故障时追踪错误。它可以帮助您记录重要信息,并根据准确性和性能指标分析其各个方面。它可以帮助您更好地管理应用程序并辅助调试。您可以在此处阅读有关 Node.js 日志记录和推荐库的更多信息。
捕获错误
错误提供了代码中出错之处以及可能出错位置的相关信息。但是,与其让 Node.js 抛出错误、中断代码执行甚至失败,不如通过处理错误条件来掌控局面。我们可以通过 try/catch 代码块来实现这一点。这样,我们既可以保持代码的简洁、稳定和易于调试,又可以避免糟糕的用户体验。
try {
if(somethingHappened)throw "Error Message";
} catch (err) {
console.log(e);
} finally {
console.log("Finally Executed" );
}
最佳实践 #7
第三方解决方案:不要重复造轮子 | 也不要过度
Node.js 拥有庞大的全球开发者社区。就第三方支持而言,Node 的包管理器 NPM 功能丰富、维护良好、文档齐全,涵盖了各种用例的框架、库和工具。因此,开发者可以非常方便地将这些现有的解决方案集成到他们的代码中,并充分利用其 API。
虽然这些库和工具减轻了很多负担,但重要的是要明智且负责任地对待我们导入的每个包。我们应该了解我们导入的每个包的用途、优点和缺点,并确保我们不会过度依赖它们。
最初发布于amodshinde.com
文章来源:https://dev.to/amoled27/best-practices-for-nodejs-development-5ao5