一尘不染

ExpressJS如何构建应用程序?

node.js

我正在为NodeJS使用ExpressJS Web框架。

使用ExpressJS的人将他们的环境(开发,生产,测试…),路线等放置在app.js。我认为这不是一个好方法,因为当您拥有大型应用程序时,app.js太大了!

我想要这个目录结构:

| my-application
| -- app.js
| -- config/
     | -- environment.js
     | -- routes.js

这是我的代码:

app.js

var express = require('express');
var app = module.exports = express.createServer();

require('./config/environment.js')(app, express);
require('./config/routes.js')(app);

app.listen(3000);

config / environment.js

module.exports = function(app, express){
    app.configure(function() {
    app.use(express.logger());
    });

    app.configure('development', function() {
    app.use(express.errorHandler({
        dumpExceptions: true,
        showStack: true
    }));
    });

    app.configure('production', function() {
    app.use(express.errorHandler());
    });
};

config / routes.js

module.exports = function(app) {
    app.get('/', function(req, res) {
    res.send('Hello world !');
    });
};

我的代码运行良好,我认为目录的结构很漂亮。但是,必须对代码进行修改,但我不确定代码是否良好/美观。

使用我的目录结构并修改代码还是仅使用一个文件(app.js)更好?

感谢您的建议!


阅读 206

收藏
2020-07-07

共1个答案

一尘不染

好的,已经有一段时间了,这是一个很普遍的问题,所以我继续前进,创建了一个带有JavaScript代码的脚手架github存储库,以及关于我喜欢如何构造一个中型express.js应用程序的自述文件。

focusaurus /
express_code_structure
是具有最新代码的存储库。拉请求欢迎。

这是自述文件的快照,因为stackoverflow不喜欢仅链接的答案。我将进行一些更新,因为这是我将继续更新的新项目,但是最终github存储库将是此信息的最新信息。


快速代码结构

该项目是如何组织中型express.js Web应用程序的示例。

当前至少要表达v4.14 2016年12月

建立状态

js-
标准样式

您的应用程序有多大?

Web应用程序并不完全相同,在我看来,没有一个单一的代码结构可以应用于所有express.js应用程序。

如果您的应用程序很小,则不需要此处示例的深层目录结构。只需保持简单,然后将少量.js文件粘贴到存储库的根目录即可。Voilà。

如果您的应用程序很大,则有时需要将其分解为不同的npm软件包。通常,node.js方法似乎倾向于使用许多小软件包,至少对于库而言是如此,并且您应该通过使用几个npm软件包来构建应用程序,因为这开始变得有意义并证明了开销。因此,随着应用程序的增长以及部分代码在应用程序外部或作为清晰的子系统可以清楚地重用,请将其移至其自己的git存储库中,并将其放入独立的npm包中。

因此 ,该项目的重点是说明中型应用程序的可行结构。

你的整体架构是什么

构建Web应用程序的方法有很多,例如

  • 服务器端MVC和Ruby on Rails
  • 单页应用程序样式为MongoDB / Express / Angular / Node(MEAN)
  • 具有某些形式的基本网站
  • MVC的模型/操作/视图/事件样式已死,现在该进行MOVE了
  • 和其他许多当前和历史的

所有这些都很好地适合于不同的目录结构。就本示例而言,它只是脚手架,而不是一个完全正常运行的应用程序,但我假设以下关键体系结构要点:

  • 该网站有一些传统的静态页面/模板
  • 该站点的“应用程序”部分被开发为单页应用程序样式
  • 该应用程序向浏览器公开REST / JSON样式的API
  • 该应用程序为一个简单的业务领域建模,在这种情况下,它是一个汽车经销商应用程序

Ruby on Rails呢?

在整个项目中,将成为一个主题,Ruby on Rails所体现的许多想法及其采纳的“ Convention over
Configuration”决策尽管被广泛接受和使用,但实际上并没有什么帮助,有时与该存储库相反推荐。

我在这里的主要观点是,组织代码具有一些基本原则,基于这些原则,Ruby on Rails约定(大多数情况下)对Ruby on
Rails社区有意义。但是,仅仅不加思索地修改这些约定就没有意义。一旦掌握了基本原理,您的所有项目都将井井有条,清晰:shell脚本,游戏,移动应用程序,企业项目,甚至您的主目录。

对于Rails社区,他们希望能够有一个Rails开发人员从一个应用程序切换到另一个应用程序,并且每次都熟悉并感到满意。如果您是37个信号机或Pivotal
Labs,这非常有意义,并且有好处。在服务器端JavaScript世界中,总体上讲,风风雨雨更像西方,我们对此并不存在任何问题。我们就是这样滚动的。我们已经习惯了。即使在express.js中,它也是Sinatra的近亲,而不是Rails,并且从Rails获得约定通常无济于事。我什至要说
原则胜于约定胜于配置

基本原理和动机

  • 在精神上易于管理
    • 大脑只能一次处理并思考少量相关事物。这就是为什么我们使用目录。通过专注于小部分,它可以帮助我们应对复杂性。
  • 尺寸合适
    • 不要创建“ Mansion Directories”,其中只有1个文件,仅3个目录向下。您可以在Ansible最佳实践中看到这种情况,该做法会使小型项目蒙羞地创建10个以上的目录来容纳10个以上的文件,而1个目录和3个文件更合适。您不会开车去上班(除非您是公共汽车司机,但是即使您在工作时开车去上班也不能上班),所以也不要创建文件系统结构,这些文件系统结构不能由其中的实际文件来证明。
  • 模块化但务实
    • 节点社区总体上偏爱小型模块。任何可以完全从您的应用程序中完全分离出来的内容,都应提取到模块中以供内部使用或在npm上公开发布。但是,对于此处适用的中型应用程序,此操作的开销可能会使乏味的添加到您的工作流中而没有相应的价值。因此,当您有一些代码被分解但不足以证明一个完全独立的npm模块的时间时,只需将其视为“ 原型模块 ”,并期望当它超过某个大小阈值时,将其提取出来。
    • 诸如@ hij1nx之类的某些人甚至包括一个app/node_modules目录,并package.json原型模块 目录中包含文件,以促进这种过渡并起到提醒作用。
  • 易于查找代码
    • 给定要构建的功能或要修复的错误,我们的目标是使开发人员可以轻松地找到所涉及的源文件。
    • 名称有意义且准确
    • 不完整的代码已完全删除,没有留在孤立文件中或仅被注释掉了
  • 易于搜索
    • 所有第一方源代码都在app目录中,因此您可以cd运行find / grep / xargs / ag / ack / etc并不受第三方匹配的干扰
  • 使用简单明了的命名
    • npm现在似乎要求使用全小写的软件包名称。我觉得这主要是可怕的,但我必须随大流,这样的文件名应该使用kebab-case,即使必须是对JavaScript中的变量名camelCase,因为-在JavaScript中有一个减号。
    • 变量名称与模块路径的基本名称匹配,但已kebab-case转换为camelCase
  • 按耦合分组,而不按功能分组
    • 这是从的Ruby on Rails的惯例是一大飞跃app/viewsapp/controllersapp/models,等
    • 功能已添加到完整堆栈中,因此我想重点关注与功能相关的完整文件堆栈。当我在用户模型中添加电话号码字段时,除了用户控制器外,我不在乎任何控制器,除了用户模型外,我也不在乎任何模型。
    • 因此,与其编辑每个目录中的6个文件并忽略这些目录中的其他文件,不如将这个存储库组织成这样,以便将我需要构建功能的所有文件都放在同一位置
    • 通过MVC的性质,用户视图耦合到与用户模型耦合的用户控制器。因此,当我更改用户模型时,这3个文件通常会一起更改,但是交易控制器或客户控制器是分离的,因此不涉及。同样,通常也适用于非MVC设计。
    • MVC或MOVE样式的去耦在哪个代码进入哪个模块方面仍然受到鼓励,但是将MVC文件散布到同级目录只是令人讨厌。
    • 因此,我的每个路由文件都具有其拥有的部分路由。routes.rb如果您想了解应用程序中所有路径的概述,可以使用Rails样式的文件,但是在实际构建功能和修复错误时,您只关心与要更改的零件相关的路径。
  • 将测试存储在代码旁边
    • 这只是“通过耦合进行分组”的一个实例,但我想特别指出。我已经编写了许多项目,这些测试都存在于名为“ tests”的并行文件系统下,并且现在我开始将测试与它们相应的代码放在同一目录中,我再也不会回头。这更加模块化,并且在文本编辑器中更易于使用,并且减轻了许多“ ../../ ..”路径的胡扯。如果您有疑问,请尝试一些项目并自己决定。除此以外,我不会做任何其他事情来使您相信它会更好。
  • 减少与事件的跨领域耦合
    • 容易想到“确定,每当创建新交易时,我都希望向所有销售人员发送电子邮件”,然后只需将代码发送到创建交易的路径中即可。
    • 但是,这种耦合最终会将您的应用程序变成一个巨大的泥泞球。
    • 取而代之的是,DealModel应该只触发一个“ create”事件,并且完全不知道系统可能会对此做出什么反应。
    • 当您以这种方式进行编码时,将所有与用户相关的代码放入app/users其中的可能性变得更大,因为到处都没有耦合业务逻辑的棘手问题,这污染了用户代码库的纯度。
  • 代码流是可遵循的
    • 不要做魔术。不要从文件系统的魔术目录中自动加载文件。不要成为Rails。该应用程序始于,app/server.js:1您可以按照以下代码查看加载和执行的所有内容。
    • 不要为您的路由创建DSL。在不需要时不要做愚蠢的元编程。
    • 如果你的应用程序是如此之大,这样做magicRESTRouter.route(somecontroller, {except: 'POST'})是一个巨大的胜利为你在3个基本app.getapp.putapp.del,电话,你可能会建立一个整体的应用程序,是太大了有效工作的。赢得大笔大奖,而不是将3条简单的线转换为1条复杂的线。
  • 使用小写字母的文件名

    • 这种格式避免了跨平台的文件系统区分大小写的问题
    • npm禁止在新的程序包名称中使用大写字母,这很适合

express.js的细节

  • 不要使用app.configure。它几乎完全没有用,您只是不需要它。由于盲目复制,它在很多样板中。

  • 紧急情况下中途和路线的顺序!!!

    • 我在stackoverflow上看到的几乎每个路由问题都是乱序的快速中间件
    • 通常,您希望您的路线解耦,而不是太依赖顺序
    • app.use如果您真的只需要2条路由的中间件,就不要使用整个应用程序(我在看着您body-parser
    • 确保一切都说完之后,您已经完全符合以下顺序:
    • 任何超重要的应用程序范围的中间件
    • 您的所有路线和各种路线中间件
    • 然后错误处理程序
  • 可悲的是,受到sinatra的启发,express.js大多假设您的所有路线都将进入server.js,并且将清楚地说明它们的顺序。对于中型应用程序,将内容分解为单独的路由模块是不错的选择,但这确实会带来无序中间件的危险

应用程序符号链接技巧

社区在针对Node.js的更好的本地更好的require()路径中概述并讨论了许多方法。我可能很快会决定选择“只处理大量的../../../
..”还是使用requireFrom模块。但是,此刻,我一直在使用下面详述的symlink技巧。

因此,避免require("../../../config")使用类似恼人的相对路径来避免项目内需求的一种方法是使用以下技巧:

  • 在您的应用程序的node_modules下创建符号链接
    • cd node_modules && ln -nsf ../app
  • 加上 刚刚node_modules /应用程序符号链接本身 ,而不是整个文件夹node_modules,到混帐
    • git add -f node_modules / app
    • 是的,您的.gitignore文件中仍应包含“ node_modules”
    • 不,您不应将“ node_modules”放入您的git存储库。有人会建议您这样做。他们是不正确的。
  • 现在您可以要求使用此前缀的项目内模块
    • var config = require("app/config");
    • var DealModel = require("app/deals/deal-model");
  • 基本上,这使得项目内需求与外部npm模块的需求非常相似。
  • 抱歉,Windows用户需要坚持父目录的相对路径。

组态

通常,代码模块和类仅希望options传入基本JavaScript
对象。仅app/server.js应加载app/config.js模块。从那里可以options根据需要合成小对象以配置子系统,但是将每个子系统耦合到一个充满额外信息的大型全局配置模块上,则是不好的结合。

尝试集中创建数据库连接并将其传递到子系统中,而不是传递连接参数并让子系统自己进行传出连接。

NODE_ENV

这是Rails提出的另一个诱人但可怕的想法。您的应用中应该恰好有1个位置,app/config.js它可以查看NODE_ENV环境变量。其他所有内容都应采用显式选项作为类构造函数参数或模块配置参数。

如果电子邮件模块具有关于如何传递电子邮件的选项(SMTP,登录到stdout,放入队列等),则应采用类似的选项,{deliver: 'stdout'}但绝对不要选中NODE_ENV

测验

现在,我将测试文件与其对应的代码保存在同一目录中,并使用文件扩展名命名约定将测试与生产代码区分开。

  • foo.js 具有模块“ foo”的代码
  • foo.tape.js 具有针对foo的基于节点的测试,并位于同一目录中
  • foo.btape.js 可以用于需要在浏览器环境中执行的测试

我使用文件系统全局文件和find . -name '*.tape.js'命令来根据需要访问所有测试。

如何在每个.js模块文件中组织代码

这个项目的范围主要是关于文件和目录的位置,我不想添加其他范围,但是我只想提到我将代码分为3个不同的部分。

  1. CommonJS的打开块要求调用状态依赖项
  2. 纯JavaScript的主要代码块。这里没有CommonJS污染。不要引用导出,模块或要求。
  3. 关闭CommonJS以设置导出
2020-07-07