我正在为NodeJS使用ExpressJS web框架.

使用ExpressJS的人会将他们的环境(开发、生产、测试…),他们的路由等都在app.js号线上.我认为这不是一个很好的方式,因为当你有一个大的应用程序,应用程序.js太大了!

我想要这个目录 struct :

| 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 !');
    });
};

我的代码运行良好,我认为目录的 struct 很漂亮.然而,代码必须进行修改,我不确定它是否好/漂亮.

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

谢谢你的建议!

推荐答案

好吧,已经有一段时间了,这是一个很受欢迎的问题,所以我继续创建了一个脚手架github存储库,其中包含JavaScript代码和关于如何构建中型express的长篇自述.js应用程序.

focusaurus/express_code_structure是带有最新代码的回购协议.欢迎拉请求.

这里是自述文件的一个快照,因为stackoverflow不喜欢简单的链接答案.我会做一些更新,因为这是一个新项目,我会继续更新,但最终github repo将是这些信息的最新位置.


Express Code Structure

这个项目是如何组织中型express 的一个例子.js web应用程序.

Current to at least express v4.14 December 2016

Build Status

js-standard-style

你的申请有多大?

Web应用程序并不完全相同,在我看来,没有一种代码 struct 可以应用于所有express.js应用程序.

如果你的应用程序很小,你不需要像这里举例说明的那样有这么深的目录 struct .只需保持简单,在存储库的根目录中插入少量.js个文件,就完成了.瞧.

如果你的应用程序很大,在某个时候你需要把它分解成不同的npm包.一般来说, node .js方法似乎倾向于使用许多小软件包,至少对于库来说是这样,您应该通过使用几个npm软件包来构建应用程序,因为这开始有意义,并证明了开销的合理性.因此,随着应用程序的增长,部分代码在应用程序之外明显可重用,或者是一个清晰的子系统,请将其移动到自己的git存储库中,并将其变成一个独立的npm包.

So本项目的重点是为中型应用程序演示一个可行的 struct .

你的总体架构是什么

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

  • 服务器端MVC与Ruby on Rails
  • 单页应用程序样式a la MongoDB/Express/Angular/Node(平均值)
  • 带有一些表单的基本网站
  • 模型/操作/视图/事件样式a la MVC is dead, it's time to MOVE on
  • 还有许多其他的,包括现在的和历史的

每一个都很好地适应了不同的目录 struct .就本例而言,它只是一个脚手架,并不是一个完全工作的应用程序,但我假设以下关键架构点:

  • 该网站有一些传统的静态页面/模板
  • 网站的"应用程序"部分开发为单页应用程序风格
  • 应用程序向浏览器公开REST/JSON样式的API
  • 该应用程序模拟了一个简单的商业领域,在本例中,它是一个汽车经销商应用程序

那么Ruby on Rails呢?

整个项目的主题是,Ruby on Rails中体现的许多 idea 以及他们所采用的"约定优先于配置"决策,尽管被广泛接受和使用,但实际上并不是很有帮助,有时与该存储库建议的相反.

这里我的主要观点是,组织代码有一些基本原则,基于这些原则,Ruby on Rails约定对Ruby on Rails社区来说(主要)是有意义的.然而,只是轻率地模仿这些惯例就没有抓住重点.一旦你掌握了基本原则,你所有的项目都会井然有序,清晰明了:shell脚本、游戏、移动应用程序、企业项目,甚至你的主目录.

对于Rails社区来说,他们希望能够让一个Rails开发人员在不同的应用之间切换,并且每次都能熟悉和熟悉它.如果你是37个信号或关键实验室,这很有意义,而且有好处.在服务器端JavaScript的世界里,总体的精神状态是更疯狂的,我们对此没有什么问题.我们就是这样滚的.我们已经习惯了.甚至在快车内.js,它是Sinatra的近亲,而不是Rails,从Rails中获取惯例通常没有任何帮助.我甚至会说Principles over Convention over 配置.

基本原则和动机

  • 精神上可控
  • 大小合适
  • 要模块化,但要务实
  • 很容易找到代码
  • 搜索友好
  • Use simple and obvious naming
    • npm现在似乎需要所有小写的包名.我觉得这很糟糕,但我必须随大流,因此文件名应该使用kebab-case,尽管JavaScript中的变量名必须是camelCase,因为-在JavaScript中是负号.
    • 变量名与模块路径的基名匹配,但将kebab-case转换为camelCase
  • 按耦合分组,而不是按功能分组
  • 将测试存储在代码旁边
  • 减少与事件的交叉耦合
  • 代码流是可遵循的
  • 使用小写的烤肉串文件名

    • 这种格式避免了跨平台的文件系统大小写敏感问题
    • npm禁止在新的包名中使用大写字母,这一点很好
  • 不要用app.configure.它几乎完全没用,你根本不需要它.由于无意识的模仿,它在很多样板中都有.

  • THE ORDER OF MIDDLEWARE AND ROUTES IN EXPRESS MATTERS!!!
    • 我在stackoverflow上看到的几乎所有路由问题都是无序的express中间件
    • 一般来说,你希望你的路由是解耦的,而不是太依赖订单
    • 如果你真的只需要两条路径的中间件,那么不要在整个应用程序中使用app.use(我看的是body-parser)
    • 确保当所有的话都说了,做了,你就有了这个顺序:
  • 可悲的是,受到西纳特拉的启发,表达.js主要假设你的所有路由都在server.js条之内,并且很清楚它们是如何订购的.对于一个中等规模的应用程序,将事情分解成单独的路由模块是不错的,但它确实会带来无序中间件的危险

应用程序符号链接技巧

在great gist Better local require() paths for Node.js中,社区详细介绍和讨论了许多方法.我可能很快就会决定 Select "只是处理大量的…./../...."或者使用requireFrom模式.然而,目前,我一直在使用下面详述的符号链接技巧.

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

  • 在应用程序的 node _模块下创建一个符号链接
  • just the node_modules/app symlink itself,而不是整个node_modules文件夹添加到git
  • Now you can require intra-project modules using this prefix
    • var config = require("app/config");
    • var DealModel = require("app/deals/deal-model");
  • 基本上,这使得项目内部所需的工作与外部npm模块的工作非常相似.
  • 抱歉,Windows用户,您需要坚持使用父目录相对路径.

配置

通常,对模块和类进行编码,使其只需要传入一个基本的JavaScript options对象.只有app/server.js应该加载app/config.js模块.从那里,它可以合成options个小对象来根据需要配置子系统,但将每个子系统耦合到一个充满额外信息的大型全局配置模块是不好的耦合.

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

NODE_ENV

这是Rails带来的另一个诱人但可怕的 idea .你的应用程序中应该只有一个位置,app/config.js,用于查看NODE_ENV环境变量.其他一切都应该使用显式选项作为类构造函数参数或模块配置参数.

如果邮箱模块有一个关于如何发送邮箱的选项(SMTP、登录到stdout、放入队列等),那么它应该 Select 像{deliver: 'stdout'}这样的选项,但绝对不应该 Select NODE_ENV.

测验

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

  • foo.js有模块"foo"的代码
  • foo.tape.js具有基于 node 的foo测试,并且位于同一目录中
  • foo.btape.js可用于需要在浏览器环境中执行的测试

我使用文件系统globs和find . -name '*.tape.js'命令来访问所有必要的测试.

How to organize code within each .js module file

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

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

Node.js相关问答推荐

Node.js promise 循环中的所有多个API调用

运行JEST测试时找不到模块&q;错误

Playwright - 无法在 img 标签中使用 file:// 访问本地文件

使用 axios 和 Cheerio (Node js) 抓取 google 搜索

Amplify 部署的应用程序出现TypeError: handler is not a function错误,但它在本地运行

Indexeddb 获取所有不同于特定值的记录

Typescript 条件语句不过滤值?

错误:无法检测到网络(event="noNetwork",code=NETWORK_ERROR,version=providers/5.6.8)

配额超出了每分钟的 Sheets API 写入请求数. node .js

如何在拦截器中发送不同的请求?

如何申请在NextJS上下载文件的许可?

file.slim.js 中的苗条是什么

如何在 MongoDB collection.find() 上获取回调

为什么我们要为 Angular 2.0 安装 Node.js?

代理(如提琴手)可以与 Node.js 的 ClientRequest 一起使用吗

按日期时间字段获取最新的 MongoDB 记录

nodejs v10.3.0 的 gulp 任务问题:src\node_contextify.cc:629: Assertion `args[1]->IsString()' failed

Bootstrap 中的 Grunt 依赖冲突

Express.js中的bodyParser.urlencoded({extended: true }))和bodyParser.json()是什么意思?

如何在express 中设置默认路径(路由前缀)?