node.js ExpressJS 如何构建应用程序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5778245/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 14:05:28  来源:igfitidea点击:

ExpressJS How to structure an application?

node.jsexpress

提问by Sandro Munda

I'm using the ExpressJS web framework for NodeJS.

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

People using ExpressJS put their environments (development, production, test...), their routes etc on the app.js. I think that it's not a beautiful way because when you have a big application, app.js is too big!

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

I would like to have this directory structure:

我想要这个目录结构:

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

Here's my code:

这是我的代码:

app.js

应用程序.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

配置/环境.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

配置/routes.js

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

My code works well and I think that the structure of the directories is beautiful. However, the code had to be adapted and I'm not sure that it's good/beautiful.

我的代码运行良好,我认为目录的结构很漂亮。但是,代码必须进行调整,我不确定它是否好/漂亮。

Is it better to use my structure of directories and adapt the code or simply use one file (app.js)?

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

Thanks for your advices!

感谢您的建议!

采纳答案by Peter Lyons

OK, it's been a while and this is a popular question, so I've gone ahead and created a scaffolding github repository with JavaScript code and a long README about how I like to structure a medium-sized express.js application.

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

focusaurus/express_code_structureis the repo with the latest code for this. Pull requests welcome.

focusaurus/express_code_structure是带有最新代码的仓库。欢迎拉取请求。

Here's a snapshot of the README since stackoverflow doesn't like just-a-link answers. I'll make some updates as this is a new project that I'll continue updating, but ultimately the github repo will be the up-to-date place for this information.

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



Express Code Structure

快速代码结构

This project is an example of how to organize a medium-sized express.js web application.

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

Current to at least express v4.14 December 2016

当前到至少快递 v4.14 2016 年 12 月

Build Status

构建状态

js-standard-style

js-标准风格

How big is your application?

你的应用程序有多大?

Web applications are not all the same, and there's not, in my opinion, a single code structure that should be applied to all express.js applications.

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

If your application is small, you don't need such a deep directory structure as exemplified here. Just keep it simple and stick a handful of .jsfiles in the root of your repository and you're done. Voilà.

如果您的应用程序很小,则不需要像这里举例说明的如此深的目录结构。只需保持简单并.js在存储库的根目录中粘贴一些文件即可。瞧。

If your application is huge, at some point you need to break it up into distinct npm packages. In general the node.js approach seems to favor many small packages, at least for libraries, and you should build your application up by using several npm packages as that starts to make sense and justify the overhead. So as your application grows and some portion of the code becomes clearly reusable outside of your application or is a clear subsystem, move it to it's own git repository and make it into a standalone npm package.

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

Sothe focus of this project is to illustrate a workable structure for a medium-sized application.

所以这个项目的重点是说明一个适用于中型应用程序的可行结构。

What is your overall architecture

你的整体架构是什么

There are many approaches to building a web application, such as

有许多方法可以构建 Web 应用程序,例如

  • Server Side MVC a la Ruby on Rails
  • Single Page Application style a la MongoDB/Express/Angular/Node (MEAN)
  • Basic web site with some forms
  • Models/Operations/Views/Events style a la MVC is dead, it's time to MOVE on
  • and many others both current and historical
  • 服务器端 MVC 和 Ruby on Rails
  • 单页应用程序风格,如 MongoDB/Express/Angular/Node (MEAN)
  • 带有一些表格的基本网站
  • Models/Operations/Views/Events 风格的MVC 已经死了,是时候继续前进了
  • 以及许多其他当前和历史

Each of these fits nicely into a different directory structure. For the purposes of this example, it's just scaffolding and not a fully working app, but I'm assuming the following key architecture points:

这些中的每一个都非常适合不同的目录结构。就本示例而言,它只是一个脚手架,而不是一个完整的应用程序,但我假设以下关键架构点:

  • The site has some traditional static pages/templates
  • The "application" portion of the site is developed as a Single Page Application style
  • The application exposes a REST/JSON style API to the browser
  • The app models a simple business domain, in this case, it's a car dealership application
  • 该站点有一些传统的静态页面/模板
  • 站点的“应用程序”部分被开发为单页应用程序样式
  • 该应用程序向浏览器公开了一个 REST/JSON 风格的 API
  • 该应用程序模拟了一个简单的业务领域,在本例中,它是一个汽车经销商应用程序

And what about Ruby on Rails?

那么 Ruby on Rails 呢?

It will be a theme throughout this project that many of the ideas embodied in Ruby on Rails and the "Convention over Configuration" decisions they have adopted, though widely accepted and used, are not actually very helpful and sometimes are the opposite of what this repository recommends.

Ruby on Rails 中包含的许多想法和他们采用的“约定优于配置”决定,虽然被广泛接受和使用,但实际上并不是很有帮助,有时与这个存储库的内容相反,这将是整个项目的一个主题推荐。

My main point here is that there are underlying principles to organizing code, and based on those principles, the Ruby on Rails conventions make sense (mostly) for the Ruby on Rails community. However, just thoughtlessly aping those conventions misses the point. Once you grok the basic principles, ALL of your projects will be well-organized and clear: shell scripts, games, mobile apps, enterprise projects, even your home directory.

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

For the Rails community, they want to be able to have a single Rails developer switch from app to app to app and be familiar and comfortable with it each time. This makes great sense if you are 37 signals or Pivotal Labs, and has benefits. In the server-side JavaScript world, the overall ethos is just way more wild west anything goes and we don't really have a problem with that. That's how we roll. We're used to it. Even within express.js, it's a close kin of Sinatra, not Rails, and taking conventions from Rails is usually not helping anything. I'd even say Principles over Convention over Configuration.

对于 Rails 社区,他们希望能够让单个 Rails 开发人员从应用程序切换到应用程序,并且每次都熟悉并适应它。如果您是 37 信号或 Pivotal Labs,这很有意义,并且有好处。在服务器端 JavaScript 世界中,整体精神只是更加狂野的西部任何事情,我们对此并没有真正的问题。我们就是这样滚的。我们已经习惯了。即使在 express.js 中,它也是 Sinatra 的近亲,而不是 Rails,并且从 Rails 中获取约定通常无济于事。我什至会说原则优于约定而不是配置

Underlying Principles and Motivations

基本原则和动机

  • Be mentally manageable
    • The brain can only deal with and think about a small number of related things at once. That's why we use directories. It helps us deal with complexity by focusing on small portions.
  • Be size-appropriate
    • Don't create "Mansion Directories" where there's just 1 file all alone 3 directories down. You can see this happening in the Ansible Best Practicesthat shames small projects into creating 10+ directories to hold 10+ files when 1 directory with 3 files would be much more appropriate. You don't drive a bus to work (unless you're a bus driver, but even then your driving a bus AT work not TO work), so don't create filesystem structures that aren't justified by the actual files inside them.
  • Be modular but pragmatic
    • The node community overall favors small modules. Anything that can cleanly be separated out from your app entirely should be extracted into a module either for internal use or publicly published on npm. However, for the medium-sized applications that are the scope here, the overhead of this can add tedium to your workflow without commensurate value. So for the time when you have some code that is factored out but not enough to justify a completely separate npm module, just consider it a "proto-module" with the expectation that when it crosses some size threshold, it would be extracted out.
    • Some folks such as @hij1nxeven include an app/node_modulesdirectory and have package.jsonfiles in the proto-moduledirectories to facilitate that transition and act as a reminder.
  • Be easy to locate code
    • Given a feature to build or a bug to fix, our goal is that a developer has no struggle locating the source files involved.
    • Names are meaningful and accurate
    • crufty code is fully removed, not left around in an orphan file or just commented out
  • Be search-friendly
    • all first-party source code is in the appdirectory so you can cdthere are run find/grep/xargs/ag/ack/etc and not be distracted by third party matches
  • Use simple and obvious naming
    • npm now seems to require all-lowercase package names. I find this mostly terrible but I must follow the herd, thus filenames should use kebab-caseeven though the variable name for that in JavaScript must be camelCasebecause -is a minus sign in JavaScript.
    • variable name matches the basename of the module path, but with kebab-casetransformed to camelCase
  • Group by Coupling, Not by Function
    • This is a major departure from the Ruby on Rails convention of app/views, app/controllers, app/models, etc
    • Features get added to a full stack, so I want to focus on a full stack of files that are relevant to my feature. When I'm adding a telephone number field to the user model, I don't care about any controller other than the user controller, and I don't care about any model other than the user model.
    • So instead of editing 6 files that are each in their own directory and ignoring tons of other files in those directories, this repository is organized such that all the files I need to build a feature are colocated
    • By the nature of MVC, the user view is coupled to the user controller which is coupled to the user model. So when I change the user model, those 3 files will often change together, but the deals controller or customer controller are decoupled and thus not involved. Same applies to non-MVC designs usually as well.
    • MVC or MOVE style decoupling in terms of which code goes in which module is still encouraged, but spreading the MVC files out into sibling directories is just annoying.
    • Thus each of my routes files has the portion of the routes it owns. A rails-style routes.rbfile is handy if you want an overview of all routes in the app, but when actually building features and fixing bugs, you only care about the routes relevant to the piece you are changing.
  • Store tests next to the code
    • This is just an instance of "group by coupling", but I wanted to call it out specifically. I've written many projects where the tests live under a parallel filesystem called "tests" and now that I've started putting my tests in the same directory as their corresponding code, I'm never going back. This is more modular and much easier to work with in text editors and alleviates a lot of the "../../.." path nonsense. If you are in doubt, try it on a few projects and decide for yourself. I'm not going to do anything beyond this to convince you that it's better.
  • Reduce cross-cutting coupling with Events
    • It's easy to think "OK, whenever a new Deal is created, I want to send an email to all the Salespeople", and then just put the code to send those emails in the route that creates deals.
    • However, this coupling will eventually turn your app into a giant ball of mud.
    • Instead, the DealModel should just fire a "create" event and be entirely unaware of what else the system might do in response to that.
    • When you code this way, it becomes much more possible to put all the user related code into app/usersbecause there's not a rat's nest of coupled business logic all over the place polluting the purity of the user code base.
  • Code flow is followable
    • Don't do magic things. Don't autoload files from magic directories in the filesystem. Don't be Rails. The app starts at app/server.js:1and you can see everything it loads and executes by following the code.
    • Don't make DSLs for your routes. Don't do silly metaprogramming when it is not called for.
    • If your app is so big that doing magicRESTRouter.route(somecontroller, {except: 'POST'})is a big win for you over 3 basic app.get, app.put, app.del, calls, you're probably building a monolithic app that is too big to effectively work on. Get fancy for BIG wins, not for converting 3 simple lines to 1 complex line.
  • Use lower-kebab-case filenames

    • This format avoids filesystem case sensitivity issues across platforms
    • npm forbids uppercase in new package names, and this works well with that

      express.js specifics

  • Don't use app.configure. It's almost entirely useless and you just don't need it. It is in lots of boilerplate due to mindless copypasta.

  • THE ORDER OF MIDDLEWARE AND ROUTES IN EXPRESS MATTERS!!!
    • Almost every routing problem I see on stackoverflow is out-of-order express middleware
    • In general, you want your routes decoupled and not relying on order that much
    • Don't use app.usefor your entire application if you really only need that middleware for 2 routes (I'm looking at you, body-parser)
    • Make sure when all is said and done you have EXACTLY this order:
      1. Any super-important application-wide middleware
      2. All your routes and assorted route middlewares
      3. THEN error handlers
  • Sadly, being sinatra-inspired, express.js mostly assumes all your routes will be in server.jsand it will be clear how they are ordered. For a medium-sized application, breaking things out into separate routes modules is nice, but it does introduce peril of out-of-order middleware
  • 精神上易于管理
    • 大脑一次只能处理和思考少数相关的事情。这就是我们使用目录的原因。它通过专注于小部分来帮助我们处理复杂性。
  • 尺码合适
    • 不要创建“大厦目录”,那里只有 1 个文件,只有 3 个目录。您可以在Ansible 最佳实践中看到这种情况,当 3 个文件的 1 个目录更合适时,小项目羞于创建 10 个以上的目录来保存 10 个以上的文件。你不开车上班(除非你是公交车司机,但即便如此,你驾驶的公交车也不工作),所以不要创建不符合其中实际文件的文件系统结构.
  • 模块化但务实
    • 节点社区总体上偏爱小模块。任何可以完全从你的应用程序中完全分离出来的东西都应该被提取到一个模块中供内部使用或在 npm 上公开发布。但是,对于此处涉及的中型应用程序,这种开销可能会增加您的工作流程的乏味,而没有相应的价值。因此,当您有一些代码被分解但不足以证明完全独立的 npm 模块是合理的时,只需将其视为“原型模块”,并期望当它超过某个大小阈值时,它将被提取出来。
    • 一些人,例如@hij1nx,甚至包括一个app/node_modules目录,并package.jsonproto-module目录中有文件,以促进这种转换并作为提醒。
  • 易于定位代码
    • 给定要构建的功能或要修复的错误,我们的目标是开发人员可以毫不费力地找到所涉及的源文件。
    • 名称有意义且准确
    • 粗糙的代码被完全删除,不会留在孤儿文件中或只是被注释掉
  • 便于搜索
    • 所有第一方源代码都在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 应该只是触发一个“创建”事件,并且完全不知道系统可能会做些什么来响应它。
    • 当您以这种方式编码时,将所有与用户相关的代码放入其中变得更有可能,app/users因为不会到处都是耦合业务逻辑的老鼠窝,从而污染用户代码库的纯度。
  • 代码流程是可跟踪的
    • 不要做魔法的事情。不要从文件系统中的魔法目录自动加载文件。不要成为Rails。该应用程序开始于app/server.js:1,您可以按照代码查看它加载和执行的所有内容。
    • 不要为你的路由制作 DSL。不要在不需要时进行愚蠢的元编程。
    • 如果您的应用程序太大以至于magicRESTRouter.route(somecontroller, {except: 'POST'})超过 3 个基本的app.get, app.put, app.del, 调用对您来说是一个巨大的胜利,那么您可能正在构建一个太大而无法有效工作的整体应用程序。看中大赢,而不是将 3 条简单的线转换为 1 条复杂的线。
  • 使用小写的烤肉串文件名

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

      express.js 细节

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

  • 中间件的顺序和快递事项!!!
    • 我在 stackoverflow 上看到的几乎所有路由问题都是乱序的 express 中间件
    • 一般来说,你希望你的路线解耦,而不是太依赖订单
    • app.use如果您真的只需要 2 个路由的中间件,请不要用于整个应用程序(我在看着您,body-parser
    • 确保当所有事情都说完后,您确实有这个命令:
      1. 任何超级重要的应用程序范围的中间件
      2. 您所有的路线和各种路线中间件
      3. THEN 错误处理程序
  • 可悲的是,受 sinatra 启发,express.js 主要假设您的所有路由都将包含在内,server.js并且很清楚它们是如何排序的。对于中型应用程序,将事情分解成单独的路由模块很好,但它确实引入了乱序中间件的危险

The app symlink trick

应用符号链接技巧

There are many approaches outlined and discussed at length by the community in the great gist Better local require() paths for Node.js. I may soon decide to prefer either "just deal with lots of ../../../.." or use the requireFrommodlue. However, at the moment, I've been using the symlink trick detailed below.

社区在Node.js 的 Better local require() 路径中概述和详细讨论了许多方法。我可能很快就会决定更喜欢“只处理大量 ../../../..”或使用requireFrom模块。但是,目前,我一直在使用下面详述的符号链接技巧。

So one way to avoid intra-project requires with annoying relative paths like require("../../../config")is to use the following trick:

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

  • create a symlink under node_modules for your app
    • cd node_modules && ln -nsf ../app
  • add just the node_modules/app symlink itself, not the entire node_modules folder, to git
    • git add -f node_modules/app
    • Yes, you should still have "node_modules" in your .gitignorefile
    • No, you should not put "node_modules" into your git repository. Some people will recommend you do this. They are incorrect.
  • Now you can require intra-project modules using this prefix
    • var config = require("app/config");
    • var DealModel = require("app/deals/deal-model");
  • Basically, this makes intra-project requires work very similarly to requires for external npm modules.
  • Sorry, Windows users, you need to stick with parent directory relative paths.
  • 在 node_modules 下为您的应用创建符号链接
    • cd node_modules && ln -nsf ../app
  • 添加node_modules/app 符​​号链接本身,而不是整个 node_modules 文件夹,到 git
    • 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 用户,您需要坚持使用父目录相对路径。

Configuration

配置

Generally code modules and classes to expect only a basic JavaScript optionsobject passed in. Only app/server.jsshould load the app/config.jsmodule. From there it can synthesize small optionsobjects to configure subsystems as needed, but coupling every subsystem to a big global config module full of extra information is bad coupling.

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

Try to centralize creation of DB connections and pass those into subsystems as opposed to passing connection parameters and having subsystems make outgoing connections themselves.

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

NODE_ENV

NODE_ENV

This is another enticing but terrible idea carried over from Rails. There should be exactly 1 place in your app, app/config.jsthat looks at the NODE_ENVenvironment variable. Everything else should take an explicit option as a class constructor argument or module configuration parameter.

这是从 Rails 继承而来的另一个诱人但可怕的想法。您的应用程序中应该正好有 1 个位置,app/config.js用于查看NODE_ENV环境变量。其他一切都应该采用显式选项作为类构造函数参数或模块配置参数。

If the email module has an option as to how to deliver emails (SMTP, log to stdout, put in queue etc), it should take an option like {deliver: 'stdout'}but it should absolutely not check NODE_ENV.

如果电子邮件模块可以选择如何发送电子邮件(SMTP、登录到标准输出、放入队列等),它应该采用类似的选项,{deliver: 'stdout'}但绝对不应该检查NODE_ENV.

Tests

测试

I now keep my test files in the same directory as their corresponding code and use filename extension naming conventions to distinguish tests from production code.

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

  • foo.jshas the module "foo"'s code
  • foo.tape.jshas the node-based tests for foo and lives in the same dir
  • foo.btape.jscan be used for tests that need to execute in a browser environment
  • foo.js有模块“foo”的代码
  • foo.tape.js有基于节点的 foo 测试并且位于同一个目录中
  • foo.btape.js可用于需要在浏览器环境中执行的测试

I use filesystem globs and the find . -name '*.tape.js'command to get access to all my tests as necessary.

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

How to organize code within each .jsmodule file

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

This project's scope is mostly about where files and directories go, and I don't want to add much other scope, but I'll just mention that I organize my code into 3 distinct sections.

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

  1. Opening block of CommonJS require calls to state dependencies
  2. Main code block of pure-JavaScript. No CommonJS pollution in here. Don't reference exports, module, or require.
  3. Closing block of CommonJS to set up exports
  1. CommonJS 的打开块需要调用状态依赖项
  2. 纯 JavaScript 的主要代码块。这里没有 CommonJS 污染。不要引用导出、模块或要求。
  3. 关闭 CommonJS 块以设置导出

回答by Peter Lyons

UPDATE (2013-10-29): Please see my other answer as well which has JavaScript instead of CoffeeScript by popular demand as well as a boilerplate github repo and an extensive README detailing my latest recommendations on this topic.

更新(2013 年 10 月 29 日):请参阅我的其他答案,其中包含流行需求的 JavaScript 而不是 CoffeeScript,以及样板 github 存储库和详细介绍我对这个主题的最新建议的广泛 README。

Config

配置

What you are doing is fine. I like to have my own config namespace set up in a top-level config.coffeefile with a nested namespace like this.

你在做什么很好。我喜欢在具有config.coffee嵌套命名空间的顶级文件中设置自己的配置命名空间。

#Set the current environment to true in the env object
currentEnv = process.env.NODE_ENV or 'development'
exports.appName = "MyApp"
exports.env =
  production: false
  staging: false
  test: false
  development: false
exports.env[currentEnv] = true
exports.log =
  path: __dirname + "/var/log/app_#{currentEnv}.log"
exports.server =
  port: 9600
  #In staging and production, listen loopback. nginx listens on the network.
  ip: '127.0.0.1'
if currentEnv not in ['production', 'staging']
  exports.enableTests = true
  #Listen on all IPs in dev/test (for testing from other machines)
  exports.server.ip = '0.0.0.0'
exports.db =
  URL: "mongodb://localhost:27017/#{exports.appName.toLowerCase()}_#{currentEnv}"

This is friendly for sysadmin editing. Then when I need something, like the DB connection info, it`s

这对系统管理员编辑很友好。然后当我需要一些东西时,比如数据库连接信息,它是

require('./config').db.URL

Routes/Controllers

路由/控制器

I like to leave my routes with my controllers and organize them in an app/controllerssubdirectory. Then I can load them up and let them add whatever routes they need.

我喜欢将我的路线留给我的控制器,并将它们组织在一个app/controllers子目录中。然后我可以加载它们并让他们添加他们需要的任何路线。

In my app/server.coffeecoffeescript file I do:

在我的app/server.coffeecoffeescript 文件中,我这样做:

[
  'api'
  'authorization'
  'authentication'
  'domains'
  'users'
  'stylesheets'
  'javascripts'
  'tests'
  'sales'
].map (controllerName) ->
  controller = require './controllers/' + controllerName
  controller.setup app

So I have files like:

所以我有这样的文件:

app/controllers/api.coffee
app/controllers/authorization.coffee
app/controllers/authentication.coffee
app/controllers/domains.coffee

And for example in my domains controller, I have a setupfunction like this.

例如在我的域控制器中,我有一个setup这样的功能。

exports.setup = (app) ->
  controller = new exports.DomainController
  route = '/domains'
  app.post route, controller.create
  app.put route, api.needId
  app.delete route, api.needId
  route = '/domains/:id'
  app.put route, controller.loadDomain, controller.update
  app.del route, controller.loadDomain, exports.delete
  app.get route, controller.loadDomain, (req, res) ->
    res.sendJSON req.domain, status.OK

Views

观看次数

Putting views in app/viewsis becoming the customary place. I lay it out like this.

将视图放入app/views正在成为习惯的地方。我是这样摆放的。

app/views/layout.jade
app/views/about.jade
app/views/user/EditUser.jade
app/views/domain/EditDomain.jade

Static Files

静态文件

Go in a publicsubdirectory.

进入一个public子目录。

Github/Semver/NPM

Github/Semver/NPM

Put a README.md markdown file at your git repo root for github.

将 README.md 降价文件放在 github 的 git 存储库根目录中。

Put a package.json file with a semantic versionnumber in your git repo root for NPM.

将带有语义版本号的 package.json 文件放在NPM 的 git 存储库根目录中。

回答by dthree

The following is Peter Lyons' answer verbatim, ported over to vanilla JS from Coffeescript, as requested by several others. Peter's answer is very able, and anyone voting on my answer should vote on his as well.

以下是 Peter Lyons 的逐字回答,根据其他几个人的要求,从 Coffeescript 移植到 vanilla JS。彼得的回答非常有能力,任何对我的回答投票的人也应该对他的回答投票。



Config

配置

What you are doing is fine. I like to have my own config namespace set up in a top-level config.jsfile with a nested namespace like this.

你在做什么很好。我喜欢在具有config.js嵌套命名空间的顶级文件中设置自己的配置命名空间。

// Set the current environment to true in the env object
var currentEnv = process.env.NODE_ENV || 'development';
exports.appName = "MyApp";
exports.env = {
  production: false,
  staging: false,
  test: false,
  development: false
};  
exports.env[currentEnv] = true;
exports.log = {
  path: __dirname + "/var/log/app_#{currentEnv}.log"
};  
exports.server = {
  port: 9600,
  // In staging and production, listen loopback. nginx listens on the network.
  ip: '127.0.0.1'
};  
if (currentEnv != 'production' && currentEnv != 'staging') {
  exports.enableTests = true;
  // Listen on all IPs in dev/test (for testing from other machines)
  exports.server.ip = '0.0.0.0';
};
exports.db {
  URL: "mongodb://localhost:27017/#{exports.appName.toLowerCase()}_#{currentEnv}"
};

This is friendly for sysadmin editing. Then when I need something, like the DB connection info, it`s

这对系统管理员编辑很友好。然后当我需要一些东西时,比如数据库连接信息,它是

require('./config').db.URL

Routes/Controllers

路由/控制器

I like to leave my routes with my controllers and organize them in an app/controllerssubdirectory. Then I can load them up and let them add whatever routes they need.

我喜欢将我的路线留给我的控制器,并将它们组织在一个app/controllers子目录中。然后我可以加载它们并让他们添加他们需要的任何路线。

In my app/server.jsjavascript file I do:

在我的app/server.jsjavascript 文件中,我这样做:

[
  'api',
  'authorization',
  'authentication',
  'domains',
  'users',
  'stylesheets',
  'javascripts',
  'tests',
  'sales'
].map(function(controllerName){
  var controller = require('./controllers/' + controllerName);
  controller.setup(app);
});

So I have files like:

所以我有这样的文件:

app/controllers/api.js
app/controllers/authorization.js
app/controllers/authentication.js
app/controllers/domains.js

And for example in my domains controller, I have a setupfunction like this.

例如在我的域控制器中,我有一个setup这样的功能。

exports.setup = function(app) {
  var controller = new exports.DomainController();
  var route = '/domains';
  app.post(route, controller.create);
  app.put(route, api.needId);
  app.delete(route, api.needId);
  route = '/domains/:id';
  app.put(route, controller.loadDomain, controller.update);
  app.del(route, controller.loadDomain, function(req, res){
    res.sendJSON(req.domain, status.OK);
  });
}

Views

观看次数

Putting views in app/viewsis becoming the customary place. I lay it out like this.

将视图放入app/views正在成为习惯的地方。我是这样摆放的。

app/views/layout.jade
app/views/about.jade
app/views/user/EditUser.jade
app/views/domain/EditDomain.jade

Static Files

静态文件

Go in a publicsubdirectory.

进入一个public子目录。

Github/Semver/NPM

Github/Semver/NPM

Put a README.md markdown file at your git repo root for github.

将 README.md 降价文件放在 github 的 git 存储库根目录中。

Put a package.json file with a semantic versionnumber in your git repo root for NPM.

将带有语义版本号的 package.json 文件放在NPM 的 git 存储库根目录中。

回答by Sandro Munda

My question was introduced in April 2011, it's quiet old. During this time, I could improve my experience with Express.js and how to architecture an application written using this library. So, I share here my experience.

我的问题是在 2011 年 4 月提出的,它很旧。在此期间,我可以改进我对 Express.js 的体验以及如何构建使用该库编写的应用程序。所以,我在这里分享我的经验。

Here's my directory structure:

这是我的目录结构:

├── app.js   // main entry
├── config   // The configuration of my applications (logger, global config, ...)
├── models   // The model data (e.g. Mongoose model)
├── public   // The public directory (client-side code)
├── routes   // The route definitions and implementations
├── services // The standalone services (Database service, Email service, ...)
└── views    // The view rendered by the server to the client (e.g. Jade, EJS, ...)

App.js

应用程序.js

The goal of the app.jsfile is to bootstrap the expressjs application. It loads the configuration module, the logger module, wait for database connection, ..., and run the express server.

app.js文件的目标是引导 expressjs 应用程序。它加载配置模块,记录器模块,等待数据库连接,...,并运行快速服务器。

'use strict';
require('./config');
var database = require('./services/database');
var express = require('express');
var app = express();
module.exports = app;

function main() {
  var http = require('http');

  // Configure the application.
  app.configure(function () {
    // ... ... ...
  });
  app.configure('production', function () {
    // ... ... ...
  });
  app.configure('development', function () {
    // ... ... ...
  });

  var server = http.createServer(app);

  // Load all routes.
  require('./routes')(app);

  // Listen on http port.
  server.listen(3000);
}

database.connect(function (err) {
  if (err) { 
    // ...
  }
  main();
});

routes/

路线/

The routes directory has a index.jsfile. Its goal is to introduce a kind of magic to load all other files inside the routes/directory. Here's the implementation:

路由目录有一个index.js文件。它的目标是引入一种魔法来加载routes/目录中的所有其他文件。这是实现:

/**
 * This module loads dynamically all routes modules located in the routes/
 * directory.
 */
'use strict';
var fs = require('fs');
var path = require('path');

module.exports = function (app) {
  fs.readdirSync('./routes').forEach(function (file) {
    // Avoid to read this current file.
    if (file === path.basename(__filename)) { return; }

    // Load the route file.
    require('./' + file)(app);
  });
};

With that module, creating a new route definition and implementation is really easy. For examples, hello.js:

使用该模块,创建新的路由定义和实现非常容易。例如,hello.js

function hello(req, res) {
  res.send('Hello world');
}

module.exports = function (app) {
  app.get('/api/hello_world', hello);
};

Each route module is standalone.

每个路由模块都是独立的

回答by tjholowaychuk

I like to use a global "app", rather than exporting a function etc

我喜欢使用全局“应用程序”,而不是导出函数等

回答by neebz

I think it's a great way to do it. Not limited to express but I've seen quite a number of node.js projects on github doing the same thing. They take out the configuration parameters + smaller modules (in some cases every URI) are factored in separate files.

我认为这是一个很好的方法。不仅限于 express,但我在 github 上看到很多 node.js 项目都在做同样的事情。他们取出配置参数 + 较小的模块(在某些情况下每个 URI)都被放在单独的文件中。

I would recommend going through express-specific projects on github to get an idea. IMO the way you are doing is correct.

我建议在 github 上浏览特定于 express 的项目以获得一个想法。海事组织你正在做的方式是正确的。

回答by Simon Fakir

it is now End of 2015and after developing my structure for 3 years and in small and large projects. Conclusion?

现在是 2015 年底,在我开发了 3 年的结构和小型和大型项目之后。结论?

Do not do one large MVC, but separate it in modules

不要做一个大的MVC,而是把它分成模块

So...

所以...

Why?

为什么?

  • Usually one works on one module (e.g. Products), which you can change independently.

  • You are able to reuse modules

  • You are able to test it separatly

  • You are able to replace it separatly

  • They have clear (stable) interfaces

    -At latest, if there were multiple developers working, module separation helps

  • 通常一个工作在一个模块(例如产品)上,您可以独立更改。

  • 您可以重用模块

  • 你可以单独测试

  • 您可以单独更换

  • 他们有清晰(稳定)的界面

    - 最迟,如果有多个开发人员在工作,模块分离会有所帮助

The nodebootstrapproject has a similar approach to my final structure. (github)

nodebootstrap项目也有类似的做法,以我的最终结构。( github)

How does this structure look like?

这个结构看起来如何?

  1. Small, capsulated modules, each with separate MVC

  2. Each modulehas a package.json

  3. Testingas a part of the structure (in each module)

  4. Global configuration, libraries and Services

  5. Integrated Docker, Cluster, forever

  1. 小型封装模块,每个模块都有单独的 MVC

  2. 每个模块都有一个 package.json

  3. 测试作为结构的一部分(在每个模块中)

  4. 全局配置、库和服务

  5. 集成 Docker,集群,永远

Folderoverview (see lib folder for modules):

Folderoverview(请参阅模块的 lib 文件夹):

nodebootstrap structure

节点引导结构

回答by Raja Rama Mohan Thavalam

I am giving MVC style folder structure please find bellow .

我给了 MVC 风格的文件夹结构,请在下面找到。

We used bellow folder structure for our big and medium web applications .

我们为我们的大中型 Web 应用程序使用了波纹管文件夹结构。

 myapp   
|
|
|____app
|      |____controllers
|      |    |____home.js
|      |
|      |____models
|      |     |___home.js
|      |
|      |____views
|           |___404.ejs
|           |___error.ejs
|           |___index.ejs
|           |___login.ejs
|           |___signup.ejs
|   
|
|_____config
|     |___auth.js
|     |___constants.js
|     |___database.js
|     |___passport.js
|     |___routes.js
|
|
|____lib
|    |___email.js
|
|____node_modules
|
|
|____public.js
|    |____css
|    |    |__style.css
|    |    
|    |____js
|    |    |__script.js
|    |
|    |____img
|    |    |__img.jpg
|    |
|    |
|    |____uploads
|         |__img.jpg
|      
|   
|
|_____app.js
|
|
|
|_____package.json

I have created one npm module for generation express mvc folder structurer.

我已经创建了一个 npm 模块来生成 express mvc 文件夹结构器。

Please find the bellow https://www.npmjs.com/package/express-mvc-generator

请找到下面的 https://www.npmjs.com/package/express-mvc-generator

Just simple steps to generate and use this modules .

只需简单的步骤即可生成和使用此模块。

i) install module npm install express-mvc-generator -g

i) 安装模块 npm install express-mvc-generator -g

ii) check options express -h

ii) 检查选项 express -h

iii) Generate express mvc structure express myapp

iii) 生成express mvc 结构 express myapp

iv) Install dependencies: npm install:

iv) 安装依赖项npm install

v)Open your config/database.js , Please configure your mongo db.

v) 打开你的 config/database.js ,请配置你的 mongo db。

vi)Run the application node appor nodemon app

vi) 运行应用程序node appnodemon app

vii)Check URL http://localhost:8042/signupOR http://yourip:8042/signup

vii) 检查 URL http://localhost:8042/signuphttp://yourip:8042/signup

回答by Stefan

It's been quite a while since the last answer to this question and Express has also recently released version 4, which added a few useful things for organising your app structure.

自从上次回答这个问题以来已经有一段时间了,Express 最近也发布了第 4 版,它添加了一些有用的东西来组织您的应用程序结构。

Below is a long up to date blog post about best practices on how to structure your Express app. http://www.terlici.com/2014/08/25/best-practices-express-structure.html

下面是一篇关于如何构建 Express 应用程序的最佳实践的最新博客文章。 http://www.terlici.com/2014/08/25/best-practices-express-structure.html

There is also a GitHub repository applying the advice in the article. It is always up to date with the latest Express version.
https://github.com/terlici/base-express

还有一个 GitHub 存储库应用了文章中的建议。它始终与最新的 Express 版本保持同步。
https://github.com/terlici/base-express

回答by ecdeveloper

I don't think it's a good approach to add routes to config. A better structure could be something like this:

我认为将路由添加到 config 不是一个好方法。更好的结构可能是这样的:

application/
| - app.js
| - config.js
| - public/ (assets - js, css, images)
| - views/ (all your views files)
| - libraries/ (you can also call it modules/ or routes/)
    | - users.js
    | - products.js
    | - etc...

So products.js and users.js will contain all your routes will all logic within.

因此 products.js 和 users.js 将包含您的所有路由以及其中的所有逻辑。