Начало работы с модулями Node.js: require, exports, imports и не только

Модули являются важной концепцией для понимания проектов Node.js. В этом посте мы рассмотрим модули Node: requireexports и будущее import .

Модули Node.js позволяют писать повторно используемый код. Вы можете вкладывать их друг в друга. Используя Node Package Manager (NPM) вы можете опубликовать свои модули и сделать их доступными для сообщества. Кроме того, NPM позволяет повторно использовать модули, созданные другими разработчиками.

Мы используем Node 12.x для примеров и синтаксис ES6 +. Тем не менее, концепции действительны для любой версии.

В этом разделе мы рассмотрим, как создавать модули Node и каждый из его компонентов:

  • Require
  • Exports
  • Module (module.exports vs. export)
  • Import

require — подключение модулей в Node.js

require используются для подключения модулей. Это позволяет вам включать модули в ваши программы. Вы можете добавить встроенные базовые модули Node.js, модули, написанные сообществом разработчиков из папки node_modules и локальные модули.

Допустим, мы хотим прочитать файл из файловой системы. Node.js имеет для этого встроенный модуль под названием «fs»:

const fs = require('fs');

fs.readFile('./file.txt', 'utf-8', (err, data) => {
  if(err) { throw err; }
  console.log('data: ', data);
});

Как видите, мы импортировали модуль «fs» в наш код. Это позволяет нам использовать любую прикрепленную к нему функцию, например «readFile» и многие другие.

Функция require будет искать файлы в следующем порядке:

  1. Built-in. Встроенные в Node.js модули (такие как fs)
  2. NPM Modules. Устанавливаемые модули из репозитория npm, их можно посмотреть в папке node_modules.
  3. Local Modules. Локальные модули, которых надо подгружать из вашего локального места через обращение к пути .// or ../. Модули соответствуют расширениям: *.js*.json*.mjs*.cjs*.wasm и *.node.

Давайте теперь объясним каждый чуть подробнее

1. Встроенные модули Node.js

Когда вы устанавливаете Node.js, он поставляется со многими встроенными модулями. Node.js поставляется с батарейками в комплекте :).

Некоторые из наиболее используемых основных модулей:

  • fs: позволяет манипулировать (создавать / читать / писать) файлами и каталогами .
  • path: утилиты для работы с файлами и каталогами путей.
  • http: создавать HTTP-серверы и клиенты для веб-разработки .
  • url: утилиты для разбора URL и извлечения из него элементов .

Эти модули вам не нужно устанавливать, вы можете импортировать их и использовать в своих программах из коробки.

2. Модули из репозитория NPM

Модули NPM — это сторонние модули, которые вы можете использовать после их установки. Назвать несколько:

  • lodash: коллекция служебных функций для манипулирования массивами, объектами и строками.
  • request: HTTP-клиент проще в использовании, чем встроенный модуль  http.
  • express: HTTP сервер для создания сайтов и API. Опять же, проще в использовании, чем встроенный модуль http.

Это вы должны сначала установить их, вот так:

$ npm install express

и затем вы можете ссылаться на них, как на встроенные модули, но на этот раз они будут обслуживаться из папки node_modules, которая содержит все сторонние библиотеки.

const express = require('express');
3. Собственные модули

Если вы не можете найти встроенную или стороннюю библиотеку, которая делает то, что вы хотите, вам придется разрабатывать ее самостоятельно. В следующих разделах вы узнаете, как это сделать с помощью экспорта exports.

exports — экспорт данных из модуля Node.js

Ключевое слово export дает вам возможность «экспортировать» ваши объекты и методы. Давайте сделаем пример:

const PI = 3.14159265359;

exports.area = radius => (radius ** 2) * PI;
exports.circumference = radius => 2 * radius * PI;

В приведенном ниже коде мы экспортируем функции area и circumference. Мы определили константу PI, но она доступна только внутри модуля. Только элементы, связанные с exports, доступны за пределами модуля.

Итак, мы можем использовать его, используя require в другом файле, как показано ниже:

const circle = require('./circle');

const r = 3;
console.log(`Circle with radius ${r} has
  area: ${circle.area(r)};
  circumference: ${circle.circumference(r)}`);

Заметил, что на этот раз мы добавляем имя модуля к ./. Это означает, что модуль является локальным файлом.

Обертка модуля в Node.js

Вы можете рассматривать каждый модуль Node.js как отдельную функцию, подобную следующей:

(function (exports, require, module, __filename, __dirname) {
  module.exports = exports = {};

  // Your module code ...

});

Мы уже описывали exports и require. Обратите внимание на связь между module.exports и exports. Они указывают на одну и ту же ссылку. Но если вы назначите что-то напрямую для exports, вы прервете его ссылку на module.exports — подробнее об этом в следующем разделе.

Для нашего удобства определены __filename и __dirname. Они предоставляют полный путь к текущему файлу и каталогу. Последний исключает имя файла и печатает путь к каталогу.

Например, для нашего модуля ./circle.js это будет примерно так:

  • __filename : /User/websofter/code/circle.js
  • __dirname : /User/websofter/code

Хорошо, мы рассмотрели exports, require, __filename и __dirname. Единственное, что мы не рассмотрели, это module. Давай сделаем это!

Что использовать module.exports vs. exports ?

Модуль module не является глобальным и является локальным для каждого модуля. Он содержит метаданные о модуле, такие как id, exports, parent, children и так далее.

exports — это псевдоним module.exports. Следовательно, все, что вы назначаете для exports, также доступно в module.exports. Однако, если вы назначите что-то напрямую для экспорта, вы потеряете ярлык для module.exports. Например.

class Cat {
  makeSound() {
    return `${this.constructor.name}: Meowww`;
  }
}

// exports = Cat; // It will not work with `new Cat();`
// exports.Cat = Cat; // It will require `new Cat.Cat();` to work (yuck!)
module.exports = Cat;

Попробуйте следующий случай с exports, а затем с module.exports.

const Cat = require('./cat');

const cat = new Cat();
console.log(cat.makeSound());

Подводя итог, когда использовать module.exports против exports:

Используйте exports для:

  • Экспорт именованной функции. например exports.area, exports.circumference.

Используйте module.exports для:

  • если вы хотите экспортировать объект, класс, функцию на корневом уровне (например, module.exports = Cat)
  • Если вы предпочитаете возвращать один объект, который предоставляет несколько назначений. например module.exports = {area, circumference};

imports — новые возможности импорта модулей

Начиная с версии 8.5.0+, Node.js нативно поддерживает модули ES с флагом функции и новым расширением файла * .mjs.

Например, наш предыдущий circle.js можно переписать как circle.mjs следующим образом:

const PI = 3.14159265359;

export function area(radius) {
  return (radius ** 2) * PI;
}

export function circumference(radius) {
  return 2 * radius * PI;
}

Затем мы можем использовать импорт:

import { area, circumference } from './circle.mjs';

const r = 3;

console.log(`Circle with radius ${r} has
  area: ${area(r)};
  circunference: ${circumference(r)}`);

И, наконец, вы можете запустить его, используя флаг функции экспериментального модуля:

$ node --experimental-modules main.mjs

Если вам не нравятся экспериментальные модули, другой альтернативой является использование транспилятора. Это преобразует современный JavaScript в более старые версии для вас. Хорошими вариантами являются TypeScriptBabel и Rollup.

Устранение неполадок при использовании imports и require

Экспериментальный Флаг

Если вы не используете экспериментальный флаговый узел --experimental-modules и пытаетесь использовать import, вы получите ошибку, подобную этой:

internal/modules/cjs/loader.js:819
  throw new ERR_REQUIRE_ESM(filename);
  ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: bla bla blah
Расширения файлов .mjs vs .js (или .cjs)

Если у вас есть код модуля написан в файле с расширением *.mjs, то вы не можете использовать require, он выдаст ошибку (ReferenceError: require не определен). .mjs предназначен для импорта через imports в соответствии ECMAScript, а .js — для регулярного использования require.

Однако с помощью *.mjs вы можете загружать оба вида модулей!

import { area, circumference } from './circle.mjs';
import Cat from './cat.js';

const r = 3;
console.log(`Circle with radius ${r} has
  area: ${area(r)};
  circumference: ${circumference(r)}`);

const cat = new Cat();
console.log(cat.makeSound());

Обратите внимание, что cat.js использует модули commonJS.

Резюме

Мы узнали о том, как создавать модули Node.js, и использовали этот опыт в нашем коде. Модули позволяют легко повторно использовать код. Они обеспечивают функциональность, которая изолирована от других модулей. Функция require используется для загрузки модулей. exports и module.exports позволяют нам определить, какие части нашего кода мы хотим представить. Мы также исследовали разницу между module.exports и exports. Наконец, мы быстро выбрали, что будет с модулями, использующими imports.

Модули Node.js и ключевое слово require в JavaScript

Многие новички, иногда, приходят в конфуз, связанный с тем, что на серверной стороне в Node.js используются включение отдельных скриптов и модулей через ключевое слово require, хотя, если глянуть в стандарт клиентского языка JavaScript, то там он не используется.

Модульная система Node.js

Node.js обрабатывает каждый файл JavaScript как отдельный модуль. Но этот способ подключения не используется на клиентской стороне в целях безопасности

Например, если у вас есть файл, содержащий некоторый код, и этот файл называется xyz.js, то этот файл рассматривается как модуль в Node, и вы можете сказать, что создали модуль с именем xyz.

Node.js абстрагируется от конкретного расширения, потому что есть и другие языки, к примеру TypeScript, который бы потребовал другого именования

Давайте возьмем пример, чтобы понять это лучше.

У вас есть файл с именем circle.js, который состоит из логики для вычисления площади и окружности круга заданного радиуса, как показано ниже:

// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
const calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
const calculateCircumference = r => 2 * PI * r;

Вы можете назвать файл circle.js модулем с именем circle.

Вам может быть интересно, почему нужно иметь несколько модулей? Вы могли бы просто написать весь код в одном модуле. Что ж, очень важно написать модульный код. Под модульным я хочу сказать, что ваш код должен быть независимым и должен быть слабо связанным. Представьте, что есть большое приложение, и весь ваш код написан в одном месте, в одном файле. Слишком грязно, верно?

Как работает код, написанный внутри модуля?

Перед выполнением кода, написанного внутри модуля, Node берет весь код и заключает его в оболочку функции. Синтаксис этой функции-обёртки:

Весь код, который вы пишете в модуле, находится в оболочке функции!

Функциональная оболочка для модуля circle будет выглядеть так, как показано ниже:

(function(exports, require, module, __filename, __dirname) {
  // all the code written inside the 'circle' module will breathe inside this function wrapper
  
  // constant
  const PI = 3.14; 

  /**
   * Function to calculate area of a circle with given radius r
   * @param {number} r
   * @returns {number} area of circle 
   */
  const calculateArea = r => PI * r * r;

  /**
   * Function to calculate circumference of a circle with given radius r
   * @param {number} r
   * @returns {number} circumference of circle 
   */
  const calculateCircumference = r => 2 * PI * r;
});

Вы можете видеть, что на корневом уровне есть функция-обертка, охватывающая весь код, написанный внутри модуля circle.

Весь код, написанный внутри модуля, является частным для модуля, если явно не указано, как экспортированное через ключевое слово export.

Это самое значительное преимущество наличия модулей в Node.js. Даже если вы определяете глобальную переменную в модуле, используя ключевые слова var, let или const, переменные ограничиваются локальной областью модуля, а не глобально. Это происходит потому, что каждый модуль имеет свою собственную оболочку функции, а код, написанный внутри одной функции, является локальным для этой функции и недоступен вне этой функции.

Код, написанный внутри модуля, является частным для него!

Представьте, что есть два модуля — A и B. Код, написанный внутри модуля A, заключен в оболочку функции, соответствующую модулю A. Подобное происходит с кодом, написанным внутри модуля B. Потому что код, относящийся к обоим модулям заключен в различные функции, эти функции не смогут получить доступ к коду друг друга. (Помните, что каждая функция в JavaScript имеет свою локальную область видимости?) По этой причине модуль A не может получить доступ к коду, написанному внутри модуля B, и наоборот.

Пять параметров — export, require, module, __filename, __dirname доступны внутри каждого модуля в Node.

Хотя эти параметры являются глобальными для кода в модуле, они являются локальными для модуля (из-за функции-оболочки, как описано выше). Эти параметры предоставляют ценную информацию, связанную с модулем.

Давайте вернемся к модулю circle, который вы рассматривали ранее. В этом модуле определены три конструкции: константа-переменная PI, функция с именем CalculaArea и другая функция с именем CalculateCircumference. Важно помнить, что все эти конструкции по умолчанию являются частными для модуля circle. Это означает, что вы не можете использовать эти конструкции в любом другом модуле, если это не указано явно.

Итак, возникает вопрос: как указать что-то в модуле, который может быть использован другим модулем? Это когда модуль и требуются параметры функции оболочки являются полезными. Давайте обсудим эти два параметра в этой статье.

Параметр module

Параметр module (скорее ключевое слово в модуле в Node) относится к объекту, представляющему текущий модуль. exports— это ключевое слово объекта модуля, соответствующее значение которого является объектом.

Значением по умолчанию для модуля module.exports является {} (пустой объект). Вы можете проверить это, зарегистрировав значение ключевого слова module внутри любого модуля. Давайте проверим, каково значение параметра module внутри модуля circle


// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
const calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
const calculateCircumference = r => 2 * PI * r;

// logging the contents of module object
console.log(module);

Обратите внимание, в коде есть строчка вывода в консоль console.log (module). Переде тем, как вывести модуль регистрирует объект module, у которого есть ключ с именем exports, и значение, соответствующее этому ключу, равно {} (пустой объект).

Теперь, что делает объект module.exports? Ну, это используется для определения вещей, которые могут быть экспортированы модулем.

Все, что экспортируется из модуля, может, в свою очередь, быть доступным для других модулей.

Экспортировать что-то довольно легко. Вам просто нужно добавить его в объект module.exports. Есть три способа добавить что-то к объекту module.exports для экспорта. Давайте обсудим эти методы один за другим.

Экспорт свойств из модуля

Для экспорта вы сначала определяете конструкции, а затем используете несколько операторов module.exports, где каждая инструкция используется для экспорта чего-либо из модуля.

Давайте посмотрим на этот способ в действии и посмотрим, как можно экспортировать две функции, определенные в модуле circle.

// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
const calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
const calculateCircumference = r => 2 * PI * r;

// exporting stuff by adding to module.exports object
module.exports.calculateArea = calculateArea; // exporting function named calculateArea
module.exports.calculateCircumference = calculateCircumference; // exporting function named calculateCircumference

Как я уже говорил ранее, module — это объект, имеющий ключ с именем exports, и этот ключ (module.exports), в свою очередь, состоит из другого объекта. Теперь, если вы заметили приведенный выше код, все, что вы делаете, это добавляете новые свойства (пары ключ-значение) в объект module.exports.

Первое свойство имеет ключ calculateArea (определен в строке 19), а значение, записанное в правой части оператора присваивания, является функцией, определенной с именем calculateArea (в строке 9).

Второе свойство (определено в строке 20) имеет ключ calculateCircumference, а значением является функция, определенная с именем calculateCircumference (в строке 16).

Таким образом, вы присвоили два свойства (пары ключ-значение) объекту module.exports.

Кроме того, давайте не будем забывать, что вы использовали здесь точечную запись. В качестве альтернативы вы можете использовать нотацию в квадратных скобках для назначения свойств объекту module.exports и добавить функции — возложить на calculateArea и calculateCircumference, указав ключи, следующие за нотацией в скобках. Таким образом, вы можете написать следующие две строки, чтобы добавить свойства к объекту module.exports, используя скобочные обозначения, заменив последние две строки в приведенном выше коде:

...
// exporting stuff by adding to module.exports object using the bracket notation
module.exports['calculateArea'] = calculateArea;
module.exports['calculateCircumference'] = calculateCircumference; 
...

Давайте теперь попробуем записать значение объекта module.exports после добавления свойств. Обратите внимание, что следующий оператор вывода значения объекта module добавляется в конец кода в приведенном ниже файле:

// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
const calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
const calculateCircumference = r => 2 * PI * r;

// exporting stuff by adding to module.exports object
module.exports.calculateArea = calculateArea; // exporting function named calculateArea
module.exports.calculateCircumference = calculateCircumference; // exporting function named calculateCircumference

// logging the contents of module.exports object after adding properties to it
console.log(module.exports);

Давайте проверим вывод этого кода и посмотрим, все ли работает нормально. Для этого сохраните свой код и выполните следующую команду в своем терминале:

$ node circle

Это выведет

{ 
   calculateArea: [Function: calculateArea],
   calculateCircumference: [Function: calculateCircumference] 
}

Конструкции — calculateArea и calculateCircumference, добавленные в module.exports, регистрируются в объекте module.exports. Таким образом, вы успешно добавили два свойства в объект module.exports, чтобы можно было экспортировать функции — calculateArea и calculateCircumference в другой модуль.

В этом методе вы сначала определили все конструкции, а затем использовали несколько операторов module.exports, где каждый оператор используется для добавления свойства к объекту module.exports.

Можно было не использовать несколько выводов, а ограничиться одним выводом объектас нужными свойствами:

...
// using object literal notation to export stuff by adding all at once to module.exports object
module.exports = {
  calculateArea : calculateArea,
  calculateCircumference : calculateCircumference
}
...

Еще один способ вывода — это использовать экспортирование на этапе вычислений нужных свойств и сразу же применить

// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
// adding function definition as the value corresponding to the key calculateArea while defining the function
module.exports.calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
// adding function definition as the value corresponding to the key calculateCircumference while defining the function
module.exports.calculateCircumference = r => 2 * PI * r;

// no need to write any module.exports statement later

// logging the contents of module.exports object after adding properties to it
console.log(module.exports);

Функция require

Ключевое слово require относится к функции, которая используется для импорта всех конструкций, экспортированных с использованием объекта module.exports из другого модуля.

Если у вас есть модуль x, в который вы экспортируете некоторые конструкции, используя объект module.exports, и вы хотите импортировать эти экспортированные конструкции в модуль y, вам потребуется импортировать модуль x в модуле y с помощью функции require. Значение, возвращаемое функцией require в модуле y, равно объекту module.exports в модуле x.

функция require возвращает объект module.exports

Давайте разберемся в этом на примере, который мы обсуждали ранее. У вас уже есть модуль circle, из которого вы экспортируете функции calculateArea и calculateCircumference. Теперь давайте посмотрим, как вы можете использовать функцию require для импорта экспортированного материала в другой модуль.

Разработка на удаленном сервере для платформ React, Angular, Vue

В данном посте рассмотрим процесс удаленной разработки для современных фреймворков React, Angular, Vue. Статья предполагает, что проекты у вас установлены на удаленном VDS/VPS с ОС Ubuntu.

Настройка проекта React для удаленной разработки

Настройка проекта Angular для удаленной разработки

Настройка проекта Vue для удаленной разработки

Работа через менеджер версий Node.js (NVM)

Что такое NVM?

NVM(Node Version Manager) — это простой bash — скрипт, который позволяет упралять(устанавливать, обновлять, активировать, удалять) разные версии Node.js на одной машине так, что они выполняются в изолированных друг от друга средах. Таким образом, можно работать с разными версиями одного программного обеспечения не задевая или разрушая их модульные зависимости.

Почему NVM?

В любой момент вы захотите работать с различными проектами, используя различные версии Node.js и NVM позволяет устанавливать разные версии для различных проектов. NVM позволяет работать  с разными версиями посредством сессий, при котором каждая сессия соответствует версии Node.js.

Установка NVM

Установка на Linux/Ubuntu очень легко делается, сначала надо будет скачать через ссылку, указав версию(на момент написания версия была v0.33.8 и это можно проверить по ссылке на официальном репозитории).

Скачка через curl:

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash

или через wget:

wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash

Возможно, что после установки вам придется закрыть и еще раз открыть консоль, но в большинстве этого не требуется.

Скрипт клонирует репозиторий nvm в директорию  ~/.nvm и добавляет исходные строки в наш профиль в виде:   ~/.bash_profile~/.zshrc~/.profile или ~/.bashrc

Установка Node.js используя NVM

Давайте рассмотрим процесс установки Node.js через NVM и это легко. Перед тем, как установить Node.js, нам необходимо узнать какие версии на данный момент доступны для загрузки и установки и для этой проверки есть команда:

nvm ls-remote

Если в списке есть желаемая версия, то ее можно установить через команду с указанием этой версии:

nvm install 5.0

И потом можно менять оболочки(сессии) использования версии через команду:

nvm use 5.0

Использование NVM

Чтобы посмотреть список установленных версий Node.js можно набрать команду:

nvm ls

И если мы в большинстве используем определенную версию Node.js, то можем установить ее по умолчанию:

nvm alias default 5.0

И все это дело можно гибко настраивать, создавая файл .nvmrc в корневой директории проекта с указанием необходимых данных версии Node.js для запуска проекта.

 

Заключение

Управление версиями на одной машине — это ключевая задача, но утомительная. Задачи должны быть автоматизированы и для этого и создавался NVM, который помогает нам управлять различными версиями Node.js без труда. Дополнительно про возможности NVM можно почитать на официальной странице репозитория.

 

Создание архитектуры масштабируемого приложения Node.js

Понятие «Архитектура приложения»

Любая программа, будь то десктопная или WEB, должна иметь определенные правила оформления кода: классов, файлов, методов, частей и т.д. Это делается, скорее всего, для того, чтобы увеличить производительность труда разработчиков и уменьшить риск возникновения непредвиденной работы программы, которую можно быстро идентифицировать и отладить. Существуют несколько подходов того, как должна строиться архитектура WEB — приложения, в частности, эти же правила можно перенести и на приложения Node.js — это MVC, HMVC, MOVE и другие. В данной же статье рассмотрим простейший пример реализации архитектуры приложения на основе модели MVC (Model-View-Controller). Не будем придумывать что такое MVC,  а приведем цитату первого абзаца из Википедии:

Model-view-controller (MVC, «модель-представление-поведение», «модель-представление-контроллер», «модель-вид-контроллер») — схема использования нескольких шаблонов проектирования, с помощью которых модель данных приложения, пользовательский интерфейс и взаимодействие с пользователем разделены на три отдельных компонента таким образом, чтобы модификация одного из компонентов оказывала минимальное воздействие на остальные. Данная схема проектирования часто используется для построения архитектурного каркаса, когда переходят от теории к реализации в конкретной предметной области

Модель MVC, как и любая другая, — это такая вещь, которая призвана упростить разделение труда разработчиков. Так, если одна группа разрабатывает интерфейс, другая — пишет код, третья — разрабатывает модель взаимодействия и хранения, то MVC с легкостью может все это соединить воедино, чтобы получить рабочее приложение и наоборот — может разделить так, что одна модель может иметь несколько представлений, к примеру, одни и те же данные из БД выводятся на одной странице сайта в виде круговой диаграммы, а в другой -в виде таблицы, но данные неизменны или другой пример, где один и тот же запрос пользователя может воздействовать на модель по разному в различных представления, в зависимости от настроек контроллера и вида. По способу взаимодействия MVC можно подразделить на MVC с пассивной моделью, где данные отправляет на вид контроллер и с активной моделью, где данные отправляет на вид сама модель

mvc-active-passive-model

Это не единственное отличие, которое может быть в MVC, их ровно столько, сколько может позволить себе любой разработчик, но концепция MVC едина для всех.

Модульность в Node.js

Прежде чем рассмотрим реализацию архитектуры приложения в Node.js, давайте обсудим про модульность в Node.js. Как известно Node.js — это модульная платформа, в котором все работает по принципу лего. Даже простой пример

var http = require('http');//Подключаем модуль приема и отправки по протоколу Http 
//Создаем объект нового сервера 
http.createServer(function (req, res) { //Передаем заголовок res.writeHead(200, {'Content-Type': 'text/plain'}); //Выводим запись 
res.end('Hello World\n'); }).listen(1337, '127.0.0.1');//Указываем адрес хоста 
//Уведомляем в консоли о том, что запущен наш сервер console.log('Server running at http://127.0.0.1:1337/');

подключает модуль http для приема и отправки запросов, который находиться в составе Node.js,  а другой модуль, к примеру, fs нужен Node.js для работы с файловой системой компьютера и он тоже входит в стандартный набор Node.js, но это не значит, что платформа Node.js содержит модули для решения всех задач, которые встречаются у разработчиков, к примеру, в стандартный состав Node.js не входит модуль для работы с сокетами и для этого нам надо его установить через консоль командой

npm install socket.io

Отсюда мы понимаем, что для того чтобы создать масштабируемое приложение нам будет необходимо разделить его на отдельные модули и подключать их к программе по мере необходимости и Node.js позволяет нам создавать собственные модули. Для примера разделим выше приведенный простой код на модули и получим идентичный результат. Чтобы разбить код на модули нам необходимо создавать файлы модулей, к примеру, мы создадим новый модуль под названием server в файле serevr.js и поместим в нее весь код работы с запросами к серверу, а в файле index.js мы запустим наш пользовательский модуль server.  Название модуля соответствует названию файла без расширения .js, код в файле server.js будет следующим

 

var http = require('http');//Подключаем модуль протокола http

function start()//Создаем метод запуска модуля
{
	function onRequest(req, res){
	  //Передаем заголовок
	  res.writeHead(200, {'Content-Type': 'text/plain'});
	  //Выводим запись
	  res.end('Hello World from Server!\n');
	}
	//Указываем адрес хоста
	http.createServer(onRequest).listen(1337, '127.0.0.1');
}
//Указываем, что данный метод доступен из вне
exports.start = start;

А в файле index.js запишем код подключения модуля server и ее запуска

var server = require('./server');//Подключаем наш модуль server
server.start();//Запускаем наш модуль сервер
//Уведомляем в консоли о том, что запущен наш сервер
console.log('Server running at http://127.0.0.1:1337/');

Запускаем наш пример командой

node [ваш путь]/server.js

если все запустилось удачно, то будет следующее сообщение в консоли

start-node-js-hello-world-result

 

После этого идем в браузер и набираем адрес нашего запущенного хоста

http://127.0.0.1:1337/

node-js-h-w-module-from-server

На этом пример создания простого модуля закончено.

Роутер и обработка запросов

В предыдущем пункте мы использовали такую функцию, как onRequest(req, res), где req(request) — объект запроса пользователя, а res(response) — это ответ пользователю. Не имеет значения как называется данная функция, она передается как параметр, для нас важны параметры, которые передаются в данную функцию. Данная функция еще называется функцией обратного вызова(callback), которая работает ассинхронно. Чтобы соответствовать уровню MVC нам необходимо определять получаемые запросы из параметра req  и передавать в параметр  res ответ в виде вида страницы. А вид страницы — это сверстанный HTML файл, и таких файлов столько, сколько необходимо для нашего приложения и пользователь видит только их. Обычно, данные файлы расположены в отдельной папке. Чтобы узнать какой запрос с POST и GET данными отправил пользователь, нам необходим роутер, который будет проверять и выдавать ту информацию, которую мы позволили нашему приложению. Продолжение следует…

Введение в Node.js и запуск «Hello World!»

Node.js — это интерпретатор для языка JavaScript, который используется в виде серверного языка программирования ровно также, как и PHP, Ruby, Python и т.д. Так, зачем же надо было создавать еще один интерпретатор, если подобных уже немало? А затем, что это совсем необычная технология взаимодействия клиент-сервер. Основное отличие данного интерпретатора в том, что она позволяется быстро исполнять код JavaScript на стороне сервера и мгновенно ассинхронно передавать ответ на запросы удаленных пользователей. По сути дела, Node.js  — движок V8, на котором работает браузер Chrome от Google, который переделали в сервер, добавив к нему необходимые модули работы с файловой системой, с HTTP — запросами, c сокетами и т.д.

Установка Node.js

Скачать и установить Node.js — это проще простого. Для этого заходим на официальный сайт проекта и жмем на зеленную кнопку INSTALL. После скачивания установка происходит в стандартном режиме, если у вас Windows x64, то установщик по умолчанию установит в Node.js  в папке C:\Program Files\nodejs\, соглашаемся с установщиком

node-js-default-path

После установки мы сможем передавать команды управления для Node.js из командной строку, через ключевое слово node.

Запуск рабочего примера «Hello World!»

Давайте запустим пример работы в среде Node.js. Для этого нам нужно место, где будут храниться наши файлы сервера. Представьте это как папку хранения хостов Denvera или OpenServer, хотя для Node.js нет принципиальной разницы где будут храниться ваши исполняемые файлы, но мы условимся хранить наши файлы на диске C:/NodeServer/hostname.loc, а в папку hostname.loc добавим файл index.js со следующим сожержимым

var http = require('http');//Подключаем подуль приема и отправки по протоколу Http
//Создаем объект нового сервера
http.createServer(function (req, res) {
  //Передаем заголовок
  res.writeHead(200, {'Content-Type': 'text/plain'});
  //Выводим запись
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');//Указываем адрес хоста
//Уведомляем в консоли о том, что запущен наш сервер
console.log('Server running at http://127.0.0.1:1337/');

После записи данного кода сохраняем данный файл и закрываем, переходим в консоль и набираем команду исполнения данного файла с указанием пути

node C:/NodeServer/hostname.loc/index.js

start-node-js-hello-worldЕсли все удачно установилось и запустилось, то увидите следующее собщение

start-node-js-hello-world-result

Теперь отрываем браузер и вводим адрес http://127.0.0.1:1337/ и убеждаемся, что у нас вышла долгожданная строка

node-js-result-in-browser-hw

 

На этом все, Node.js установлен и готов для дальнейшей работы.