ExpressJS Як структурувати додаток?


527

Я використовую веб-рамку ExpressJS для NodeJS.

Люди, які використовують 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 / route.js

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

Мій код працює добре, і я думаю, що структура каталогів прекрасна. Однак код потрібно було адаптувати, і я не впевнений, що це добре / красиво.

Чи краще використовувати мою структуру каталогів і адаптувати код або просто використовувати один файл (app.js)?

Дякуємо за поради!


Чи все ще ховаються проблеми ефективності виконання цього способу? Я пам’ятаю, як десь читав (можливо, експрес-групу), що коли ви розлучаєте все так, ви втрачаєте тону продуктивності. Щось на зразок ваших запитів / сек зменшиться на помітну кількість, майже як якщо б це була помилка.
AntelopeSalad

2
Це було з групи Express Google. Ось посилання: groups.google.com/group/express-js/browse_thread/thread/…
AntelopeSalad

52
nope це дуже неправда
tjholowaychuk

Відповіді:


306

Гаразд, минув час, і це популярне питання, тому я пішов вперед і створив сховище лісів github з кодом JavaScript та довгим README про те, як мені подобається структурувати середній розмір програми express.js.

focusaurus / express_code_structure є репо з останнім кодом для цього. Прохання просити ласкаво просимо.

Ось короткий знімок README, оскільки stackoverflow не любить відповіді просто на посилання. Я проведу кілька оновлень, оскільки це новий проект, який я продовжуватиму оновлювати, але, зрештою, github repo стане актуальним місцем для цієї інформації.


Структура експрес-коду

Цей проект є прикладом того, як організувати веб-додаток express.js середнього розміру.

Поточний принаймні експрес v4.14 грудня 2016 року

Статус складання

js-стандартний стиль

Наскільки велика ваша заявка?

Веб-додатки не однакові, і, на мою думку, немає єдиної структури коду, яка повинна застосовуватися до всіх програм express.js.

Якщо ваша програма невелика, вам не потрібна така глибока структура каталогу, як показано тут. Просто тримайте це просто і вставте кілька .jsкореневих файлів у корінь вашого сховища, і ви закінчите. Voilà.

Якщо ваша програма величезна, вам в якийсь момент вам потрібно розбити її на різні npm-пакети. Загалом підхід node.js, здається, надає перевагу багатьом невеликим пакетам, принаймні, для бібліотек, і ви повинні створити свою програму, використовуючи кілька пакетів npm, оскільки це починає мати сенс і виправдовувати накладні витрати. Оскільки ваш додаток зростає і деяка частина коду стає чітко використаною поза вашою програмою або є чіткою підсистемою, перемістіть її у власне сховище git та перетворіть її в окремий пакет npm.

Тому основна мета цього проекту - проілюструвати працездатну структуру для додатків середнього розміру.

Яка ваша загальна архітектура

Існує багато підходів до створення веб-додатків, таких як

  • Сторона сервера MVC a la Ruby on Rails
  • Стиль застосування на одній сторінці a la MongoDB / Express / Angular / Node (MEAN)
  • Основний веб-сайт з деякими формами
  • Моделі / Операції / Перегляди / Стилі подій a la MVC мертвий, настав час ПОВЕРНУТИСЯ
  • та багато інших як сучасних, так і історичних

Кожен з них добре вписується в іншу структуру каталогів. Для цього прикладу, це просто ліси і не повністю працює додаток, але я припускаю наступні ключові моменти архітектури:

  • На сайті є деякі традиційні статичні сторінки / шаблони
  • Частина сайту "додаток" розробляється як стиль застосування для однієї сторінки
  • Додаток відкриває веб-переглядачу API стилю REST / JSON
  • Додаток моделює простий бізнес-домен, в даному випадку це програма автосалону

А як щодо Ruby on Rails?

Темою цього проекту буде те, що багато ідей, втілених у рішеннях Ruby on Rails та «Конвенції про конфігурацію», які вони прийняли, хоча і широко приймаються та використовуються, насправді не є дуже корисними, а іноді є протилежними до того, що це сховище рекомендує.

Моя головна думка тут полягає в тому, що є основні принципи організації коду, і виходячи з цих принципів, конвенції Ruby on Rails мають сенс (переважно) для спільноти Ruby on Rails. Однак просто бездумне застосування цих конвенцій пропускає суть. Після того, як ви перейдете на основні принципи, ВСІ ваші проекти будуть добре організовані та зрозумілі: сценарії оболонки, ігри, мобільні додатки, корпоративні проекти, навіть домашній каталог.

Для спільноти Rails вони хочуть мати єдиний перемикач розробників Rails з програми на додаток до додатка, і щоразу вони будуть знайомі та комфортні. Це має великий сенс, якщо у вас 37 сигналів або основні лабораторії, і ви маєте переваги. У сервері JavaScript на стороні сервера загальний етос є просто набагато більш диким заходом, і ми з цим насправді не маємо проблем. Ось так ми котимось. Ми звикли до цього. Навіть у express.js це близька рідність Сінатри, а не Рейлів, і прийняття конвенцій з Рейлів зазвичай нічого не допомагає. Я б навіть сказав Принципи щодо Конвенції про конфігурацію .

Основні принципи та мотивації

  • Бути розумовим керованим
    • Мозок може розібратися і думати лише про невелику кількість пов’язаних речей одночасно. Ось чому ми використовуємо каталоги. Це допомагає нам впоратися зі складністю, зосереджуючись на невеликих порціях.
  • Будьте відповідні розміру
    • Не створюйте "Каталоги особняків", де є лише один файл, а по одному лише 3 каталоги. Ви можете бачити це в « Кращих практиках Ansible», які заважають невеликим проектам створювати 10+ каталогів для вміщення 10+ файлів, коли 1 каталог із 3 файлами буде набагато доречнішим. Ви не приводите автобус на роботу (якщо ви не водій автобуса, але навіть тоді, коли ви керуєте автобусом, АБО не працює), тому не створюйте структури файлової системи, які не виправдані фактичними файлами всередині них .
  • Будьте модульними, але прагматичними
    • Спільнота вузлів загалом надає перевагу невеликим модулям. Все, що можна повністю відокремити від вашої програми, має бути вилучено в модуль для внутрішнього використання або публічно опубліковано в npm. Однак для застосувань середнього розміру, які є сферою дії, накладні витрати цього можуть додавати тидію до вашого робочого процесу без відповідного значення. Тож на час, коли у вас є який-небудь код, який розглядають, але недостатньо, щоб виправдати повністю окремий npm-модуль, просто вважайте його " протомодулем " з розрахунком, що коли він переступить якийсь поріг розміру, він буде вилучений.
    • Деякі люди , такі як @ hij1nx навіть включати в app/node_modulesкаталог і є package.jsonфайли в прото-модулях каталогів , щоб полегшити цей перехід і діяти в якості нагадування.
  • Легко знайти код
    • Враховуючи особливість збирання або помилку, яку потрібно виправити, наша мета полягає в тому, щоб у розробника не виникало труднощів за пошук вихідних файлів.
    • Назви значущі та точні
    • грубий код повністю видаляється, не залишається навколо сиротного файлу або просто коментується
  • Будьте зручними для пошуку
    • весь вихідний код стороннього виробника знаходиться в appкаталозі, тому ви можете cdзапускати find / grep / xargs / ag / ack / тощо і не відволікатися на сторонні матчі
  • Використовуйте просте і очевидне називання
    • Тепер, схоже, npm вимагає всіх малих імен пакетів. Я вважаю це в основному жахливим, але я повинен дотримуватися стада, тому імена файлів повинні використовуватись, kebab-caseхоча ім'я змінної для цього в JavaScript повинно бути, camelCaseтому що -це знак мінуса в JavaScript.
    • Ім'я змінної відповідає базовій імені шляху модуля, але з kebab-caseперетвореною вcamelCase
  • Згрупуйте за сполученням, а не за функцією
    • Це серйозний відхід від Ruby On Rails конвенції app/views, app/controllers, app/modelsі т.д.
    • Особливості додаються до повного стеку, тому я хочу зосередитись на повному стеку файлів, що відповідають моїй функції. Коли я додаю поле номер телефону до моделі користувача, я не переймаюся будь-яким контролером, окрім контролера користувача, і мене не хвилює будь-яка модель, окрім моделі користувача.
    • Таким чином, замість редагування 6 файлів, що знаходяться у власному каталозі, і ігнорування тонн інших файлів у цих каталогах, це сховище організоване таким чином, що всі файли, необхідні для створення функції, є кольоровими
    • За характером MVC перегляд користувача з'єднаний з контролером користувача, який з'єднаний з користувацькою моделлю. Отже, коли я змінюю модель користувача, ці 3 файли часто змінюються разом, але контролер угод або контролер клієнта роз'єднується і, таким чином, не бере участь. Те саме стосується і конструкцій, що не належать до MVC, як правило.
    • Розв’язка стилю MVC або MOVE, з точки зору коду, в якому модулі все ще рекомендується, але розповсюдження файлів MVC у каталоги братів просто дратує.
    • Таким чином, кожен з моїх файлів маршрутів має частину маршрутів, якими він володіє. routes.rbФайл у стилі рейки - це зручно, якщо ви хочете переглянути огляд усіх маршрутів у додатку, але, коли насправді вибудовуєте функції та виправляєте помилки, ви дбаєте лише про маршрути, що стосуються деталі, яку ви змінюєте.
  • Зберігайте тести поруч із кодом
    • Це лише екземпляр "групи за допомогою з'єднання", але я хотів конкретно назвати це. Я написав багато проектів, де тести живуть у паралельній файловій системі під назвою "тести", і тепер, коли я почав ставити свої тести в той самий каталог, що і їх відповідний код, я ніколи не повертаюся назад. Це більш модульно і набагато простіше працювати з текстовими редакторами та зменшує багато дурниць "../../ ..". Якщо ви сумніваєтесь, спробуйте це на кількох проектах та вирішіть самі. Я не збираюся робити нічого, крім цього, щоб переконати вас, що це краще.
  • Зменшіть наскрізну зв’язок із подіями
    • Неважко подумати "Гаразд, кожного разу, коли буде створено нову угоду, я хочу надіслати електронний лист усім продавцям", а потім просто покласти код, щоб відправити ці електронні листи в маршрут, який створює угоди.
    • Однак ця зв'язок врешті-решт перетворить ваш додаток у гігантський кулька грязі.
    • Натомість DealModel повинен просто запустити подію "створення" та бути абсолютно невідомим, що ще може зробити система у відповідь на це.
    • Коли ви кодуєте таким чином, стає набагато більш можливим розмістити весь код, пов'язаний з користувачем, app/usersоскільки всюди не існує гнізда зв'язаної бізнес-логіки щура, що забруднює чистоту бази коду користувача.
  • Потік коду відстежується
    • Не робіть чарівних речей. Не завантажуйте автоматично файли з чарівних каталогів у файлову систему. Не будьте Рейлами. Додаток запускається, 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, - це експрес-проміжне програмне забезпечення, яке не впорядковане
    • Взагалі, ви хочете, щоб ваші маршрути були роз'єднані і не дуже покладалися на замовлення
    • Не використовуйте app.useдля всієї програми, якщо вам потрібно лише це програмне забезпечення для двох маршрутів (я дивлюся на вас, body-parser)
    • Переконайтесь, що коли все сказано і зроблено, ви точно отримали це замовлення:
      1. Будь-яке надзвичайно важливе проміжне програмне забезпечення для широкого застосування
      2. Всі ваші маршрути та різноманітні середні маршрути
      3. ТОГО оброблювачі помилок
  • На жаль, будучи натхненним синатрами, express.js здебільшого передбачає, що всі ваші маршрути будуть наявними, server.jsі буде зрозуміло, як вони впорядковані. Для додатків середнього розміру добре розбивати речі на окремі модулі маршрутів, але це вводить небезпеку середнього програмного забезпечення, яке не вийшло з ладу

Трюк додатка symlink

Є багато підходів , викладених і обговорені на довжину спільноти у великій суті краще місцевим вимагають () шляху для Node.js . Невдовзі я можу вирішити перевагу або "просто мати справу з безліччю ../../../ ..", або використовувати модуль requFrom. Однак на даний момент я використовую хитрість символьного посилання, детально описану нижче.

Отож, один із способів уникнути внутрішньопроектного проекту потребує таких дратівливих відносних шляхів, як require("../../../config"):

  • створити для своєї програми символьне посилання під node_modules
    • cd node_modules && ln -nsf ../app
  • додайте просто gode власне node_modules / додаток , а не всю папку node_modules до git
    • git add -f node_modules / додаток
    • Так, у вашому .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. Основний блок коду pure-JavaScript. Тут немає забруднення CommonJS. Не посилайтеся на експорт, модуль та не вимагайте.
  3. Закриваючий блок CommonJS для налаштування експорту

1
Що я повинен використовувати замість bodyParser Якщо у мене є лише кілька маршрутів, які використовують його?
Ілан Фрумер

3
Я знайшов те , що я шукав тут: stackoverflow.com/questions/12418372 / ...
Ілана Frumer

1
@wlingke ознайомтесь з gist.github.com/branneman/8048520 для ретельного обговорення доступних підходів до цієї проблеми.
Пітер Ліонс

@peterLyons Дякую за те, що поділився цим. Після прочитання я думаю, що напишу сценарій запуску. Дякую!
wlingke

2
що стосується трюку програмного посилання на цей додаток , є цей маленький модуль, який усуває всі проблеми
Hayko Koryun

157

ОНОВЛЕННЯ (2013-10-29) : Будь ласка, дивіться мою іншу відповідь, яка має JavaScript замість CoffeeScript за популярним попитом, а також реконструкцію github repo на котлі та обширний README, в якому детально описуються мої останні рекомендації з цієї теми.

Налаштування

Те, що ти робиш, це добре. Мені подобається створити власний простір імен конфігурацій у 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}"

Це зручно для редагування системи. Тоді, коли мені щось потрібно, як інформація про з'єднання БД, це `s

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

Маршрути / контролери

Мені подобається залишати свої маршрути зі своїми контролерами та організовувати їх у app/controllersпідкаталозі. Тоді я можу завантажити їх і дозволити їм додавати потрібні маршрути.

У своєму app/server.coffeeфайлі coffeescript я роблю:

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

Отже, у мене є такі файли, як:

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

Наприклад, у моєму контролері доменів у мене є така 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

Перегляди

Розміщення поглядів app/viewsстає звичним місцем. Я викладаю так.

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

Статичні файли

Перейдіть у publicпідкаталог.

Github / Semver / NPM

Помістіть файл розмітки README.md у свій корінь git repo для github.

Помістіть файл package.json з семантичним номером версії у свій корінь git repo для NPM.


1
Гей, Пітер! Мені дуже подобається такий підхід, до якого ти йдеш. Я працюю над тим, щоб будувати експрес-проект, і мені дуже хотілося б робити речі правильним способом, а не просто зламати його та розміщувати. Було б чудово, якби у вас був зразок репо на github та / або допис у блозі.
suVasH .....

4
У цьому репо є купа шаблонів, які ви можете використовувати в якості посилання: github.com/focusaurus/peterlyons.com
Пітер Ліон

75
Сценарій кави робить це важким для читання: / Будь-який шанс отримати ванільний JS редагувати? Спасибі
toasted_flakes

1
Дякую за цю відповідь. Я просто намагаюся обернути свою думку навколо цього. Як ви отримуєте доступ до інших контролерів всередині іншого (наприклад, у app.put route, api.needId
такій

@PeterLyons: еге, я бачив ваш вихідний код, але поняття не маю, як виконати режим збирання, я вже встановив Goі включив binфайл у структуру. Як запустити цей goфайл bin?
користувач2002495

51

Далі йде відповідь дослівно у відповідь Пітера Лайона, перенесеного на ванільний JS з Coffeescript, як цього вимагали кілька інших. Відповідь Петра дуже здатна, і кожен, хто голосує за мою відповідь, повинен також проголосувати і за його.


Налаштування

Те, що ти робиш, це добре. Мені подобається створити власний простір імен конфігурацій у 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}"
};

Це зручно для редагування системи. Тоді, коли мені щось потрібно, як інформація про з'єднання БД, це `s

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

Маршрути / контролери

Мені подобається залишати свої маршрути зі своїми контролерами та організовувати їх у app/controllersпідкаталозі. Тоді я можу завантажити їх і дозволити їм додавати потрібні маршрути.

У своєму app/server.jsJavaScript-файлі я роблю:

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

Отже, у мене є такі файли, як:

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

Наприклад, у моєму контролері доменів у мене є така 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);
  });
}

Перегляди

Розміщення поглядів app/viewsстає звичним місцем. Я викладаю так.

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

Статичні файли

Перейдіть у publicпідкаталог.

Github / Semver / NPM

Помістіть файл розмітки README.md у свій корінь git repo для github.

Помістіть файл package.json з семантичним номером версії у свій корінь git repo для NPM.


43

Моє запитання було представлено у квітні 2011 року, воно спокійне. За цей час я міг покращити свій досвід роботи з Express.js та як архітектуру програми, написаної за допомогою цієї бібліотеки. Отже, я ділюсь тут своїм досвідом.

Ось моя структура каталогу:

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

Мета 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();
});

маршрути /

У каталозі маршрутів є 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);
  });
};

За допомогою цього модуля створити нове визначення маршруту та реалізувати його дуже просто. Наприклад hello.js:

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

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

Кожен модуль маршруту є окремим .


Чи використовуєте ви генератор для створення цієї структури?
Ашиш

18

Мені подобається використовувати глобальний "додаток", а не експортувати функції тощо


Я вирішую брати поради від творців :) До речі, ви можете надати нам якийсь код?
втілився

це вірно. в цих додатках, які ви бачите - github.com/visionmedia/screenshot-app
diproart

17

Я думаю, що це чудовий спосіб зробити це. Не обмежуючись висловлюванням, але я бачив досить багато проектів node.js на github, що роблять те саме. Вони виймають параметри конфігурації + менші модулі (в деяких випадках кожен URI) враховуються в окремих файлах.

Я рекомендую пройти експрес-конкретні проекти на Github, щоб отримати уявлення. IMO так, як ви робите, є правильним.


16

це зараз Кінець 2015 року і після розробки моєї структури протягом 3 років та для малих та великих проектів. Висновок?

Не робіть одного великого MVC, а розділіть його по модулях

Тому...

Чому?

  • Зазвичай один працює на одному модулі (наприклад, Продукти), який ви можете самостійно змінити.

  • Ви можете повторно використовувати модулі

  • Ви можете це перевірити окремо

  • Ви можете його замінити окремо

  • Вони мають чіткі (стабільні) інтерфейси

    -Нарешті, якщо працювало кілька розробників, розділення модулів допомагає

Проект nodebootstrap має подібний підхід до моєї остаточної структури. ( github )

Як виглядає ця структура?

  1. Невеликі, капсульовані модулі , кожен з яких має окремий MVC

  2. У кожному модулі є пакет.json

  3. Тестування як частина структури (у кожному модулі)

  4. Глобальна конфігурація , бібліотеки та послуги

  5. Інтегрований Докер, Кластер, назавжди

Folderoverview (див. Папку lib для модулів):

структура nodebootstrap


3
Було б корисно, якби ви могли оновити зображення огляду папок із розширеними окремими модулями, як приклад того, як ви також структуруєте їх.
youngrrrr

8

Я надаю структуру папок у стилі MVC, будь ласка, знайдіть нижче.

Ми використовували структуру папок нижче для наших великих та середніх веб-додатків.

 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

Я створив один модуль npm для покоління Express Express mvc папок.

Нижче наведено https://www.npmjs.com/package/express-mvc-generator

Просто прості кроки для створення та використання цих модулів.

i) встановити модуль npm install express-mvc-generator -g

ii) перевірити варіанти express -h

iii) Створення експрес-структури mvc express myapp

iv) Встановити залежності npm install:

v) Відкрийте config / database.js, налаштуйте mongo db.

vi) Запустіть програму node appабоnodemon app

vii) Перевірте URL-адресу http: // localhost: 8042 / реєстрація АБО http: // yourip: 8042 / реєстрація


7

Минуло чимало часу з часу останньої відповіді на це питання, і Express також нещодавно випустила версію 4, яка додала кілька корисних речей для організації вашої структури додатків.

Нижче наведено довгий останній пост у блозі про найкращі практики щодо структури програми Express. http://www.terlici.com/2014/08/25/best-practices-express-structure.html

У статті також є сховище GitHub, яке застосовує поради. Це завжди в курсі останньої версії Express.
https://github.com/terlici/base-express


7

Я не думаю, що це вдалий підхід для додавання маршрутів до конфігурації. Краща структура може бути приблизно такою:

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

Тож products.js та users.js будуть містити всі ваші маршрути, і все це буде логічно.


6

Ну я розміщую свої маршрути як файл json, який я читав на початку, і в циклі for-loop в app.js встановлював маршрути. Route.json включає перегляд, який слід викликати, і ключ для значень, які будуть надіслані в маршрут.
Це працює для багатьох простих випадків, але мені довелося вручну створити деякі маршрути для особливих випадків.


6

Я написав пост саме з цього приводу. В основному використовується використання routeRegistrarітератора, який повторюється через файли в папці, /controllersвикликаючи його функцію init. Функція initприймає appзмінну експреса як параметр, щоб ви могли реєструвати маршрути так, як вам потрібно.

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

5

Це може зацікавити:

https://github.com/flatiron/nconf

Ієрархічна конфігурація node.js з файлами, змінними середовища, аргументами командного рядка та об'єднанням атомного об'єкта.


4

1) Ваша файлова система проекту Express, можливо:

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

app.js - ви глобальний контейнер додатків

2) Основний файл модуля (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) Підключіть модуль до головного app.js

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

4) Зразок логіки

lib/login
lib/db
lib/config
lib/users
lib/verify
lib/
   /api/ 
   ...
lib/
   /admin/
      /users/
      /settings/
      /groups/
...
  • Найкраще для тестування
  • Найкраще для масштабу
  • Окремо залежить від модуля
  • Групування маршруту за функціональністю (або модулями)

tj каже / показує на Vimeo цікаву ідею, як модулювати експрес-додаток - Модульні веб-додатки з Node.js та Express . Потужний і простий.


4

http://locomotivejs.org/ надає спосіб структурувати додаток, створене за допомогою Node.js та Express.

З веб-сайту:

"Локомотив - це веб-рамка для Node.js. Локомотив підтримує схеми MVC, маршрути RESTful та конвенцію щодо конфігурації, при цьому безперешкодно інтегруючись з будь-яким механізмом баз даних та шаблонів. Локомотив будує на Express, зберігаючи потужність та простоту, на яку ви очікували. від Вузла ".


3

Нещодавно я сприйняв модулі як незалежні міні-додатки.

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

Тепер для будь-якої маршрутизації модуля (# .js), представлення (* .ejs), js, css та активи знаходяться поруч. Маршрутизація підмодуля встановлюється в батьківському # .js з двома додатковими рядками

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

Таким чином можливі навіть субмодулі.

Не забудьте встановити подання до каталогу src

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

будь-яке посилання на github із такою структурою, зацікавлене у тому, як завантажуються маршрути, види та моделі
Muhammad Umer

Я думаю, що все пояснено. Маршрути - це просто класичні експрес-маршрути. Перегляди потрібно завантажувати з префіксами назви модулів, моделі потрібно завантажувати шляхом посилання на відносний шлях.
зеверо

У своєму останньому рядку я встановив подання до каталогу src. Тож звідси всі види доступні відносно каталогу src. Нічого фантазії.
zevero

1

Ось так виглядає більшість моєї експрес-структури каталогу проектів.

Я, як правило, express dirnameініціалізую проект, прощаю лінь, але це дуже гнучко і розширюється. PS - вам потрібно express-generatorза це (для тих, хто його шукає sudo npm install -g express-generator, судо, тому що ви встановлюєте це в усьому світі)

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

Вам, мабуть, цікаво, чому .env файли? Бо вони працюють! Я використовую dotenvмодуль у своїх проектах (дуже недавно), і він працює! Додайте ці 2 твердження в app.jsабоwww

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

І ще один рядок для швидкого налаштування /bower_componentsдля подачі статичного вмісту під ресурс/ext

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

Можливо, це може підійти людям, які хочуть використовувати Express і Angular разом або просто експресувати без цього javascripts ієрархії, звичайно.


1

Моя структура express 4. https://github.com/odirleiborgert/borgert-express-boilerplate

Пакети

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

Будова

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

Простий спосіб структурувати ур-експрес-додаток:

  • У основному index.js слід підтримувати наступний порядок.

    весь набір програм повинен бути першим.

    всі app.use повинні бути другими.

    слідом за іншими apis з їх функціями або продовження маршруту в інших файлах

    Exapmle

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

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

    app.post ("/ маркер", passport.createToken);

    app.post ("/ вихід", passport.logout)


0

Найкращий шлях до структури MVC для проекту ExpressJs з ручкою та 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 будь ласка перевірити
Manishkumar Bhavnani
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.