ExpressJS Come strutturare un'applicazione?


528

Sto usando il framework Web ExpressJS per NodeJS.

Le persone che usano ExpressJS mettono i loro ambienti (sviluppo, produzione, test ...), i loro percorsi ecc. Sul app.js. Penso che non sia un bel modo perché quando hai una grande applicazione, app.js è troppo grande!

Vorrei avere questa struttura di directory:

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

Ecco il mio codice:

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

Il mio codice funziona bene e penso che la struttura delle directory sia meravigliosa. Tuttavia, il codice ha dovuto essere adattato e non sono sicuro che sia buono / bello.

È meglio usare la mia struttura di directory e adattare il codice o semplicemente usare un file (app.js)?

Grazie per i tuoi consigli!


I problemi prestazionali di farlo in questo modo sono ancora in agguato? Ricordo di aver letto da qualche parte (forse il gruppo espresso) che quando si separa tutto in questo modo si perde una tonnellata di prestazioni. Qualcosa come i tuoi reqs / sec scenderà di una quantità evidente, quasi come se fosse un bug.
AntelopeSalad,

2
Era del gruppo Express di Google. Ecco il link: groups.google.com/group/express-js/browse_thread/thread/…
AntelopeSalad

52
no, questo è molto falso
tjholowaychuk,

Risposte:


306

OK, è passato un po 'di tempo e questa è una domanda popolare, quindi sono andato avanti e ho creato un repository github per impalcature con codice JavaScript e un lungo README su come mi piace strutturare un'applicazione express.js di medie dimensioni.

focusaurus / express_code_structure è il repository con l'ultimo codice per questo. Pull richieste di benvenuto.

Ecco un'istantanea del file README poiché StackOverflow non ama le risposte "just-a-link". Farò alcuni aggiornamenti poiché si tratta di un nuovo progetto che continuerò ad aggiornare, ma alla fine il repository github sarà il luogo aggiornato per queste informazioni.


Struttura del codice espresso

Questo progetto è un esempio di come organizzare un'applicazione web express.js di medie dimensioni.

Corrente almeno per esprimere v4.14 dicembre 2016

Stato build

js-standard di stile

Quanto è grande la tua applicazione?

Le applicazioni Web non sono tutte uguali e, secondo me, non esiste un'unica struttura di codice che dovrebbe essere applicata a tutte le applicazioni express.js.

Se la tua applicazione è piccola, non hai bisogno di una struttura di directory così profonda come esemplificata qui. Mantienilo semplice e metti una manciata di .jsfile nella radice del tuo repository e il gioco è fatto. Ecco.

Se l'applicazione è enorme, a un certo punto è necessario suddividerla in pacchetti npm distinti. In generale, l'approccio node.js sembra favorire molti piccoli pacchetti, almeno per le librerie, e dovresti costruire la tua applicazione usando diversi pacchetti npm poiché questo inizia a dare un senso e giustificare il sovraccarico. Quindi man mano che la tua applicazione cresce e una parte del codice diventa chiaramente riutilizzabile al di fuori della tua applicazione o è un sottosistema chiaro, spostalo nel suo repository git e trasformalo in un pacchetto npm autonomo.

Così l'obiettivo di questo progetto è quello di illustrare una struttura praticabile per un'applicazione di medie dimensioni.

Qual è la tua architettura complessiva

Esistono molti approcci alla creazione di un'applicazione Web, ad esempio

  • Server Side MVC alla Ruby on Rails
  • Applicazione a pagina singola in stile MongoDB / Express / Angolare / Nodo (MEAN)
  • Sito Web di base con alcuni moduli
  • Lo stile dei modelli / operazioni / viste / eventi a la MVC è morto, è ora di spostarsi
  • e molti altri sia attuali che storici

Ognuno di questi si adatta perfettamente a una diversa struttura di directory. Ai fini di questo esempio, è solo un'impalcatura e non un'app completamente funzionante, ma presumo i seguenti punti chiave dell'architettura:

  • Il sito ha alcune pagine / modelli statici tradizionali
  • La parte "applicazione" del sito è sviluppata come uno stile di applicazione a pagina singola
  • L'applicazione espone un'API di stile REST / JSON al browser
  • L'app modella un semplice dominio aziendale, in questo caso è un'applicazione di un concessionario di automobili

E che dire di Ruby on Rails?

Durante questo progetto sarà un tema che molte delle idee incorporate in Ruby on Rails e le decisioni "Convenzione sulla configurazione" che hanno adottato, sebbene ampiamente accettate e utilizzate, non sono in realtà molto utili e talvolta sono l'opposto di ciò che questo repository raccomanda.

Il mio punto principale qui è che ci sono principi di base nell'organizzazione del codice e, sulla base di tali principi, le convenzioni di Ruby on Rails hanno senso (principalmente) per la comunità di Ruby on Rails. Tuttavia, solo sconsiderare quelle convenzioni non ha senso. Una volta individuati i principi di base, TUTTI i tuoi progetti saranno ben organizzati e chiari: script di shell, giochi, app mobili, progetti aziendali, persino la tua home directory.

Per la comunità di Rails, vogliono essere in grado di avere un singolo sviluppatore di Rails da un'app all'altra dell'app e avere familiarità con essa ogni volta. Questo ha molto senso se hai 37 segnali o Pivotal Labs e ha dei vantaggi. Nel mondo JavaScript lato server, l'ethos generale è semplicemente molto più selvaggio ovest e non abbiamo davvero problemi con questo. È così che andiamo. Ci siamo abituati. Anche all'interno di express.js, è un parente stretto di Sinatra, non di Rails, e prendere convenzioni da Rails di solito non aiuta nulla. Direi anche Principi sulla Convenzione sulla Configurazione .

Principi e motivazioni sottostanti

  • Sii gestibile mentalmente
    • Il cervello può affrontare e pensare solo a un piccolo numero di cose correlate contemporaneamente. Ecco perché usiamo le directory. Ci aiuta a gestire la complessità concentrandoci su piccole porzioni.
  • Sii adeguato alle dimensioni
    • Non creare "Mansion Directories" dove c'è solo 1 file da solo 3 directory verso il basso. Puoi vederlo accadere nelle Best Practices Ansible che fanno vergognare piccoli progetti nella creazione di oltre 10 directory per contenere più di 10 file quando 1 directory con 3 file sarebbe molto più appropriata. Non guidi un bus per funzionare (a meno che tu non sia un autista di autobus, ma anche allora guidare un bus AT non funziona PER FUNZIONARE), quindi non creare strutture di filesystem che non sono giustificate dai file reali al loro interno .
  • Sii modulare ma pragmatico
    • La comunità dei nodi in generale favorisce i piccoli moduli. Tutto ciò che può essere completamente separato dalla tua app deve essere estratto in un modulo per uso interno o pubblicato pubblicamente su npm. Tuttavia, per le applicazioni di medie dimensioni che sono l'ambito di applicazione qui, il sovraccarico di questo può aggiungere tedio al flusso di lavoro senza un valore commisurato. Quindi, per il momento in cui si dispone di un codice che viene preso in considerazione ma non abbastanza per giustificare un modulo npm completamente separato, basta considerarlo un " proto-modulo " con l'aspettativa che quando varcherà una soglia di dimensioni, verrà estratto.
    • Alcune persone come @ hij1nx includono persino una app/node_modulesdirectory e hanno package.jsonfile nelle directory del proto-modulo per facilitare quella transizione e fungere da promemoria.
  • Cerca facilmente il codice
    • Data una funzionalità da creare o un bug da correggere, il nostro obiettivo è che uno sviluppatore non abbia difficoltà a individuare i file di origine coinvolti.
    • I nomi sono significativi e precisi
    • il codice crufty viene completamente rimosso, non lasciato in un file orfano o semplicemente commentato
  • Sii favorevole alla ricerca
    • tutto il codice sorgente di prima parte è nella appdirectory in modo da poter cdeseguire run / grep / xargs / ag / ack / etc e non essere distratto da corrispondenze di terze parti
  • Usa una denominazione semplice e ovvia
    • npm ora sembra richiedere nomi di pacchetti tutto in minuscolo. Lo trovo per lo più terribile, ma devo seguire la mandria, quindi i nomi dei file dovrebbero usare kebab-caseanche se il nome della variabile per quello in JavaScript deve essere camelCaseperché- è un segno meno in JavaScript.
    • il nome della variabile corrisponde al nome di base del percorso del modulo, ma con kebab-casetrasformato incamelCase
  • Raggruppa per accoppiamento, non per funzione
    • Questo è un grande partenza dalla convenzione di Ruby on Rails app/views, app/controllers, app/models, ecc
    • Le funzionalità vengono aggiunte a uno stack completo, quindi desidero concentrarmi su uno stack completo di file rilevanti per la mia funzionalità. Quando aggiungo un campo di numero di telefono al modello utente, non mi interessa nessun controller diverso dal controller utente e non mi interessa alcun modello diverso dal modello utente.
    • Quindi, invece di modificare 6 file che si trovano ciascuno nella propria directory e ignorare tonnellate di altri file in quelle directory, questo repository è organizzato in modo tale da raggruppare tutti i file necessari per creare una funzione
    • Per natura di MVC, la vista utente è accoppiata al controller utente che è accoppiato al modello utente. Quindi, quando cambio il modello utente, quei 3 file cambieranno spesso insieme, ma il controller delle offerte o il controller del cliente vengono disaccoppiati e quindi non coinvolti. Lo stesso vale anche per i progetti non MVC.
    • Il disaccoppiamento di stile MVC o MOVE in termini di codice in cui il modulo è ancora incoraggiato, ma la diffusione dei file MVC in directory di pari livello è solo fastidioso.
    • Pertanto, ciascuno dei miei file delle rotte ha la parte delle rotte che possiede. Un routes.rbfile in stile rotaia è utile se si desidera una panoramica di tutti i percorsi nell'app, ma quando si creano effettivamente funzionalità e si correggono i bug, ci si preoccupa solo dei percorsi rilevanti per il pezzo che si sta modificando.
  • Conservare i test accanto al codice
    • Questa è solo un'istanza di "gruppo per accoppiamento", ma volevo chiamarlo in modo specifico. Ho scritto molti progetti in cui i test vivono sotto un filesystem parallelo chiamato "test" e ora che ho iniziato a mettere i miei test nella stessa directory del loro codice corrispondente, non torno più indietro. Questo è più modulare e molto più facile da lavorare negli editor di testo e allevia molte assurdità del percorso "../../ ..". In caso di dubbi, provalo su alcuni progetti e decidi tu stesso. Non farò altro oltre a questo per convincerti che è meglio.
  • Ridurre l'accoppiamento trasversale con Eventi
    • È facile pensare "OK, ogni volta che viene creata una nuova offerta, voglio inviare un'e-mail a tutti i venditori", quindi inserire semplicemente il codice per inviare quelle e-mail nel percorso che crea le offerte.
    • Tuttavia, questo accoppiamento alla fine trasformerà la tua app in una gigantesca palla di fango.
    • Invece, il DealModel dovrebbe semplicemente generare un evento "create" ed essere completamente inconsapevole di cos'altro il sistema potrebbe fare in risposta a quello.
    • Quando si codifica in questo modo, diventa molto più possibile inserire tutto il codice relativo all'utente in app/usersquanto non esiste un nido di topi di logica aziendale accoppiata in tutto il luogo che inquina la purezza della base di codici utente.
  • È possibile seguire il flusso di codice
    • Non fare cose magiche. Non caricare automaticamente i file dalle directory magiche nel filesystem. Non essere rotaie. L'app inizia alle app/server.js:1e puoi vedere tutto ciò che carica ed esegue seguendo il codice.
    • Non creare DSL per i tuoi percorsi. Non fare stupide metaprogrammazioni quando non è richiesto.
    • Se la vostra applicazione è così grande che fare magicRESTRouter.route(somecontroller, {except: 'POST'})è una grande vittoria per voi sopra 3 di base app.get, app.put, app.del, chiamate, probabilmente stai costruendo un'applicazione monolitica che è troppo grande per lavorare in modo efficace su. Divertiti per le GRANDI vittorie, non per convertire 3 linee semplici in 1 linea complessa.
  • Usa i nomi dei file con lettere minuscole

    • Questo formato evita problemi di distinzione tra maiuscole e minuscole nel filesystem su tutte le piattaforme
    • npm proibisce le maiuscole nei nomi di nuovi pacchetti, e questo funziona bene con quello

      specifiche di express.js

  • Non usare app.configure. È quasi del tutto inutile e non ne hai bisogno. È in molte piastre a causa della copypasta insensata.

  • L'ORDINE DI MIDDLEWARE E ITINERARI IN QUESTIONI ESPRESSE !!!
    • Quasi tutti i problemi di routing che vedo su StackOverflow sono middleware espressi non funzionanti
    • In generale, vuoi che i tuoi percorsi siano disaccoppiati e non ti affidino così tanto all'ordine
    • Non utilizzarlo app.useper l'intera applicazione se hai davvero bisogno di quel middleware per 2 percorsi (ti sto guardando body-parser)
    • Assicurati che quando tutto è stato detto e fatto hai ESATTAMENTE questo ordine:
      1. Qualsiasi middleware per applicazioni estremamente importante
      2. Tutti i tuoi percorsi e programmi di percorso assortiti
      3. POI gestori di errori
  • Purtroppo, essendo di ispirazione sinatra, express.js presume principalmente che tutti i percorsi saranno presenti server.jse sarà chiaro come vengono ordinati. Per un'applicazione di medie dimensioni, suddividere le cose in moduli di percorsi separati è utile, ma introduce il pericolo di middleware fuori servizio

Il trucco del collegamento simbolico dell'app

Ci sono molti approcci descritti e discussi a lungo dalla comunità nella grande Gist Meglio locale richiede) i percorsi per Node.js ( . Potrei presto decidere di preferire "solo occuparmi di un sacco di ../../../ .." o utilizzare il mod request di Rom. Tuttavia, al momento, sto usando il trucco del link simbolico descritto di seguito.

Quindi un modo per evitare intra-progetto richiede con percorsi relativi fastidiosi come require("../../../config")è quello di usare il seguente trucco:

  • crea un link simbolico in node_modules per la tua app
    • cd node_modules && ln -nsf ../app
  • aggiungi solo il link simbolico node_modules / app stesso , non l'intera cartella node_modules, a git
    • git aggiungi -f node_modules / app
    • Sì, dovresti avere ancora "node_modules" nel tuo .gitignorefile
    • No, non dovresti inserire "node_modules" nel tuo repository git. Alcune persone ti consiglieranno di farlo. Non sono corretti
  • Ora puoi richiedere moduli intra-progetto usando questo prefisso
    • var config = require("app/config");
    • var DealModel = require("app/deals/deal-model");
  • Fondamentalmente, questo rende il progetto intra-progetto molto simile a quello richiesto per i moduli npm esterni.
  • Siamo spiacenti, utenti di Windows, è necessario attenersi ai percorsi relativi della directory principale.

Configurazione

Generalmente i moduli di codice e le classi prevedono solo un optionsoggetto JavaScript di base passato. Solo app/server.jsil app/config.jsmodulo deve essere caricato . Da lì può sintetizzare piccoli optionsoggetti per configurare i sottosistemi secondo necessità, ma accoppiare ogni sottosistema a un grande modulo di configurazione globale pieno di informazioni extra è un cattivo accoppiamento.

Provare a centralizzare la creazione di connessioni DB e passare quelle nei sottosistemi anziché passare i parametri di connessione e fare in modo che i sottosistemi effettuino connessioni in uscita.

NODE_ENV

Questa è un'altra idea allettante ma terribile riportata da Rails. Dovrebbe esserci esattamente 1 posto nella tua app, app/config.jsche esamina la NODE_ENVvariabile di ambiente. Tutto il resto dovrebbe prendere un'opzione esplicita come argomento del costruttore di classe o parametro di configurazione del modulo.

Se il modulo e-mail ha un'opzione su come recapitare le e-mail (SMTP, accedere a stdout, mettere in coda ecc.), Dovrebbe prendere un'opzione come {deliver: 'stdout'}ma non dovrebbe assolutamente controllare NODE_ENV.

test

Ora mantengo i miei file di test nella stessa directory del codice corrispondente e utilizzo le convenzioni di denominazione dell'estensione del nome file per distinguere i test dal codice di produzione.

  • foo.js ha il codice del modulo "pippo"
  • foo.tape.js ha i test basati sul nodo per foo e vive nella stessa directory
  • foo.btape.js può essere utilizzato per i test che devono essere eseguiti in un ambiente browser

Uso i globs del filesystem e il find . -name '*.tape.js'comando per accedere a tutti i miei test, se necessario.

Come organizzare il codice all'interno di ciascun .jsfile del modulo

Lo scopo di questo progetto riguarda principalmente dove vanno i file e le directory e non voglio aggiungere altro ambito, ma menzionerò solo che organizzo il mio codice in 3 sezioni distinte.

  1. Il blocco di apertura di CommonJS richiede chiamate per dichiarare dipendenze
  2. Blocco di codice principale di puro JavaScript. Nessun inquinamento da CommonJS qui. Non fare riferimento a esportazioni, moduli o richieste.
  3. Blocco di chiusura di CommonJS per impostare le esportazioni

1
Cosa devo usare al posto di bodyParser Se ho solo pochi percorsi che lo usano?
Ilan Frumer,

3
Ho trovato quello che cercavo qui: stackoverflow.com/questions/12418372/...
Ilan Frumer

1
@wlingke controlla gist.github.com/branneman/8048520 per una discussione approfondita degli approcci disponibili a quel problema.
Peter Lyons,

@peterLyons Grazie per averlo condiviso. Dopo aver letto, penso che scriverò uno script di avvio. Grazie!
Wlingke,

2
per quanto riguarda il trucco del link simbolico dell'app , c'è questo piccolo modulo che
risolve

157

AGGIORNAMENTO (29-10-2013) : Vedi anche la mia altra risposta che ha JavaScript invece di CoffeeScript a grande richiesta, nonché un repository github su piastra di caldaia e un ampio README che dettaglia i miei ultimi consigli su questo argomento.

config

Quello che stai facendo va bene. Mi piace avere il mio spazio dei nomi di configurazione impostato in un config.coffeefile di livello superiore con uno spazio dei nomi nidificato come questo.

#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}"

Questo è amichevole per l'editing di amministratore di sistema. Quindi quando ho bisogno di qualcosa, come le informazioni sulla connessione DB, è

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

Itinerari / Controller

Mi piace lasciare i miei percorsi con i miei controller e organizzarli in una app/controllerssottodirectory. Quindi posso caricarli e lasciarli aggiungere qualsiasi percorso di cui hanno bisogno.

Nel mio app/server.coffeefile coffeescript faccio:

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

Quindi ho file come:

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

E ad esempio nel mio controller di dominio, ho una setupfunzione come questa.

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

Visualizzazioni

Mettere in vista app/viewssta diventando il luogo abituale. Lo dispongo così.

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

File statici

Vai in una publicsottodirectory.

Github / Semver / NPM

Inserisci un file di markdown README.md nella tua radice repo git per github.

Inserisci un file package.json con un numero di versione semantico nella tua radice repo git per NPM.


1
Ehi Peter! Mi piace molto questo approccio che stai cercando. Sto lavorando alla costruzione di un progetto espresso e vorrei davvero fare le cose nel modo giusto piuttosto che semplicemente modificarlo e metterlo in giro. Sarebbe fantastico se avessi un repository di esempio su github e / o un post sul blog.
suVasH .....

4
Questo repository ha una serie di modelli che è possibile utilizzare come riferimento: github.com/focusaurus/peterlyons.com
Peter Lyons,

75
La sceneggiatura del caffè rende difficile la lettura: / C'è qualche possibilità di ottenere una modifica JS alla vaniglia? Grazie
toasted_flakes

1
Grazie per questa risposta Sto solo cercando di pensarci su. Come si accede agli altri controller all'interno di un altro (ad es. Nella funzione di configurazione come sopraapp.put route, api.needId
chmanie

@PeterLyons: hey amico, ho visto il tuo codice sorgente ma non ho idea di come fare la modalità build, ho già installato Goe incluso il binfile nella struttura. Come si esegue quel gofile bin?
user2002495

51

Quella che segue è la risposta letterale di Peter Lyons, trasferita su JS alla vaniglia da Coffeescript, come richiesto da molti altri. La risposta di Peter è molto abile, e chiunque vota sulla mia risposta dovrebbe votare anche sulla sua.


config

Quello che stai facendo va bene. Mi piace avere il mio spazio dei nomi di configurazione impostato in un config.jsfile di livello superiore con uno spazio dei nomi nidificato come questo.

// 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}"
};

Questo è amichevole per l'editing di amministratore di sistema. Quindi quando ho bisogno di qualcosa, come le informazioni sulla connessione DB, è

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

Itinerari / Controller

Mi piace lasciare i miei percorsi con i miei controller e organizzarli in una app/controllerssottodirectory. Quindi posso caricarli e lasciarli aggiungere qualsiasi percorso di cui hanno bisogno.

Nel mio app/server.jsfile javascript faccio:

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

Quindi ho file come:

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

E ad esempio nel mio controller di dominio, ho una setupfunzione come questa.

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

Visualizzazioni

Mettere in vista app/viewssta diventando il luogo abituale. Lo dispongo così.

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

File statici

Vai in una publicsottodirectory.

Github / Semver / NPM

Inserisci un file di markdown README.md nella tua radice repo git per github.

Inserisci un file package.json con un numero di versione semantico nella tua radice repo git per NPM.


43

La mia domanda è stata presentata nell'aprile 2011, è piuttosto vecchia. Durante questo periodo, ho potuto migliorare la mia esperienza con Express.js e come progettare un'applicazione scritta usando questa libreria. Quindi, condivido qui la mia esperienza.

Ecco la mia struttura di directory:

├── 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

L'obiettivo del app.jsfile è di avviare l'applicazione expressjs. Carica il modulo di configurazione, il modulo logger, attende la connessione al database, ... ed esegue il server express.

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

itinerari/

La directory dei percorsi ha un index.jsfile. Il suo obiettivo è introdurre una sorta di magia per caricare tutti gli altri file all'interno della routes/directory. Ecco l'implementazione:

/**
 * 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);
  });
};

Con quel modulo, creare una nuova definizione di percorso e implementazione è davvero semplice. Ad esempio hello.js:

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

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

Ogni modulo di route è autonomo .


Usi un generatore per creare questa struttura?
Ashish,


17

Penso che sia un ottimo modo per farlo. Non si è limitato ad esprimere, ma ho visto parecchi progetti node.js su github fare la stessa cosa. Eliminano i parametri di configurazione + i moduli più piccoli (in alcuni casi ogni URI) sono fattorizzati in file separati.

Consiglierei di andare su progetti specifici per Express su Github per avere un'idea. IMO il modo in cui stai facendo è corretto.


16

è ora la fine del 2015 e dopo aver sviluppato la mia struttura per 3 anni e in piccoli e grandi progetti. Conclusione?

Non eseguire un MVC di grandi dimensioni, ma separarlo in moduli

Così...

Perché?

  • Di solito si lavora su un modulo (ad es. Prodotti), che è possibile modificare in modo indipendente.

  • Sei in grado di riutilizzare i moduli

  • Puoi provarlo separatamente

  • Puoi sostituirlo separatamente

  • Hanno interfacce chiare (stabili)

    -In ultimo, se c'erano più sviluppatori che lavorano, la separazione dei moduli aiuta

Il progetto nodebootstrap ha un approccio simile alla mia struttura finale. ( github )

Come appare questa struttura?

  1. Piccoli moduli capsulati , ciascuno con MVC separato

  2. Ogni modulo ha un package.json

  3. Test come parte della struttura (in ciascun modulo)

  4. Configurazione globale , librerie e servizi

  5. Docker integrato, Cluster, per sempre

Folderoverview (vedi cartella lib per i moduli):

struttura nodebootstrap


3
Sarebbe utile se anche tu potessi aggiornare l'immagine panoramica della cartella con i singoli moduli espansi, come esempio di come strutturerai anche quelli.
youngrrrr

8

Sto dando struttura delle cartelle in stile MVC, per favore trova qui sotto.

Abbiamo usato la struttura delle cartelle qui sotto per le nostre applicazioni web grandi e medie.

 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

Ho creato un modulo npm per lo strutturer di cartelle mvc express di generazione.

Si prega di trovare il seguente https://www.npmjs.com/package/express-mvc-generator

Solo semplici passaggi per generare e utilizzare questi moduli.

i) installare il modulo npm install express-mvc-generator -g

ii) selezionare le opzioni express -h

iii) Genera struttura espressa in mvc express myapp

iv) Installa dipendenze: npm install ::

v) Apri config / database.js, configura mongo db.

vi) Eseguire l'applicazione node apponodemon app

vii) Controlla l'URL http: // localhost: 8042 / iscrizione OPPURE http: // tuoip: 8042 / iscrizione


7

È passato un po 'di tempo dall'ultima risposta a questa domanda ed Express ha anche recentemente rilasciato la versione 4, che ha aggiunto alcune cose utili per l'organizzazione della struttura dell'app.

Di seguito è riportato un lungo post sul blog sulle migliori pratiche su come strutturare la tua app Express. http://www.terlici.com/2014/08/25/best-practices-express-structure.html

C'è anche un repository GitHub che applica i consigli nell'articolo. È sempre aggiornato con l'ultima versione di Express.
https://github.com/terlici/base-express


7

Non credo sia un buon approccio aggiungere percorsi per la configurazione. Una struttura migliore potrebbe essere qualcosa del genere:

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...

Quindi products.js e users.js conterranno tutte le rotte con tutta la logica all'interno.


6

Bene ho messo i miei percorsi come file json, che ho letto all'inizio, e in un ciclo for in app.js ho impostato i percorsi. Route.json include quale vista deve essere chiamata e la chiave per i valori che verranno inviati nella rotta.
Questo funziona per molti casi semplici, ma ho dovuto creare manualmente alcuni percorsi per casi speciali.


6

Ho scritto un post esattamente su questo argomento. Fondamentalmente si avvale di un routeRegistrarfile che scorre tra i file nella cartella /controllerschiamando la sua funzione init. La funzione initaccetta la appvariabile express come parametro in modo da poter registrare i percorsi nel modo desiderato.

var fs = require("fs");
var express = require("express");
var app = express();

var controllersFolderPath = __dirname + "/controllers/";
fs.readdirSync(controllersFolderPath).forEach(function(controllerName){
    if(controllerName.indexOf("Controller.js") !== -1){
        var controller = require(controllersFolderPath + controllerName);
        controller.init(app);
    }
});

app.listen(3000);


4

1) Il tuo filesystem di progetto Express potrebbe piacere:

/ ...
/lib
/node_modules
/public
/views
      app.js
      config.json
      package.json

app.js: il tuo contenitore di app globale

2) File principale del modulo (lib / mymodule / index.js):

var express = require('express');    
var app = module.exports = express();
// and load module dependencies ...  

// this place to set module settings
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');

// then do module staff    
app.get('/mymodule/route/',function(req,res){ res.send('module works!') });

3) Modulo di connessione in app.js principale

...
var mymodule = require('mymodule');
app.use(mymodule);

4) Logica di esempio

lib/login
lib/db
lib/config
lib/users
lib/verify
lib/
   /api/ 
   ...
lib/
   /admin/
      /users/
      /settings/
      /groups/
...
  • Ideale per i test
  • Ideale per la scala
  • Separare dipende dal modulo
  • Raggruppamento del percorso per funzionalità (o moduli)

tj dice / mostra su Vimeo un'idea interessante di come modulare l'applicazione express - Applicazioni web modulari con Node.js ed Express . Potente e semplice.


4

http://locomotivejs.org/ fornisce un modo per strutturare un'app creata con Node.js ed Express.

Dal sito Web:

"Locomotive è un framework web per Node.js. Locomotive supporta modelli MVC, percorsi RESTful e convenzione sulla configurazione, integrandosi perfettamente con qualsiasi database e motore di template. La locomotiva si basa su Express, preservando la potenza e la semplicità che ci si aspetta dal nodo ".


3

Di recente ho abbracciato i moduli come mini-app indipendenti.

|-- src
  |--module1
  |--module2
     |--www
       |--img
       |--js
       |--css
     |--#.js
     |--index.ejs
  |--module3
  |--www
     |--bower_components
     |--img
     |--js
     |--css
  |--#.js
  |--header.ejs
  |--index.ejs
  |--footer.ejs

Ora per qualsiasi instradamento dei moduli (# .js), le viste (* .ejs), js, css e le risorse sono una accanto all'altra. il routing del sottomodulo è impostato nel genitore # .js con due righe aggiuntive

router.use('/module2', opt_middleware_check, require('./module2/#'));
router.use(express.static(path.join(__dirname, 'www')));

In questo modo sono possibili anche moduli secondari.

Non dimenticare di impostare la vista sulla directory src

app.set('views', path.join(__dirname, 'src'));

qualsiasi link a github con tale struttura interessata a vedere come vengono caricati percorsi, viste e modelli
Muhammad Umer,

Penso che tutto sia spiegato. I percorsi sono solo percorsi espressi classici. Le viste devono essere caricate con il prefisso con i nomi dei moduli, i modelli devono essere caricati facendo riferimento al percorso relativo.
zevero,

Nell'ultima riga, ho impostato la vista sulla directory src. Da qui in poi, tutte le viste sono accessibili relativamente alla directory src. Nulla di bello.
zevero,

1

Ecco come appare la maggior parte della mia struttura di directory di progetto espresso.

Di solito faccio express dirnameun'inizializzazione del progetto, perdono la mia pigrizia, ma è molto flessibile ed estensibile. PS: devi prenderlo express-generator(per chi lo sta cercando sudo npm install -g express-generator, sudo perché lo stai installando a livello globale)

|-- bin
    |-- www //what we start with "forever"
|-- bower_components
|-- models
    |-- database.js
    |-- model1.js //not this exact name ofcourse.
    |-- .
|-- node_modules
|-- public
    |-- images
    |-- javascripts
        |-- controllers
        |-- directives
        |-- services
        |-- app.js
        |-- init.js //contains config and used for initializing everything, I work with angular a lot.
    |-- stylesheets
|-- routes
    |-- some
    |-- hierarchy
    .
    .
|-- views
    |-- partials
    |-- content
|-- .env
|-- .env.template
|-- app.js
|-- README.md

Ti starai chiedendo perché i file .env? Perché funzionano! Uso il dotenvmodulo nei miei progetti (molto di recente) e funziona! Inserisci queste 2 affermazioni in app.jsowww

var dotenv = require('dotenv');
dotenv.config({path: path.join(__dirname + "/.env")});

E un'altra linea da impostare rapidamente /bower_componentsper servire contenuto statico sotto la risorsa/ext

app.use('/ext', express.static(path.join(__dirname, 'bower_components')));

Probabilmente può essere adatto per le persone che stanno cercando di usare Express e Angular insieme, o semplicemente esprimere senza quella javascriptsgerarchia ovviamente.


1

La mia struttura express 4. https://github.com/odirleiborgert/borgert-express-boilerplate

Pacchi

View engine: twig
Security: helmet
Flash: express-flash
Session: express-session
Encrypt: bcryptjs
Modules: express-load
Database: MongoDB
    ORM: Mongoose
    Mongoose Paginate
    Mongoose Validator
Logs: winston + winston-daily-rotate-file
Nodemon
CSS: stylus
Eslint + Husky

Struttura

|-- app
    |-- controllers
    |-- helpers
    |-- middlewares
    |-- models
    |-- routes
    |-- services
|-- bin
|-- logs
|-- node_modules
|-- public
    |-- components
    |-- images
    |-- javascripts
    |-- stylesheets
|-- views
|-- .env
|-- .env-example
|-- app.js
|-- README.md

0

Un modo semplice per strutturare la tua app express:

  • In index.js principale dovrebbe essere mantenuto il seguente ordine.

    tutto app.set dovrebbe essere il primo.

    tutto app.use dovrebbe essere il secondo.

    seguito da altre API con le loro funzioni o route-continue in altri file

    exapmle

    app.use ("/ password", passwordApi);

    app.use ("/ user", userApi);

    app.post ("/ token", passport.createToken);

    app.post ("/ logout", passport.logout)


0

Il modo migliore per la struttura MVC per il progetto ExpressJs con manubrio e Passportjs

- app
      -config 
        -passport-setup.js
      -controllers
      -middleware
      -models
      -routes
      -service
    -bin
      -www
      -configuration.js
      -passport.js
    -node_modules
    -views
     -handlebars page
    -env
    -.gitignore
    -package.json
    -package-lock.json

@ sandro-munda, controlla
Manishkumar Bhavnani,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.