Использование exports/require в CommonJS и export/import в EcmaScript 2015

Начиная с ECMAScript 2015 (ES6), JavaScript имеет концепцию модулей. Модули выполняются в своей собственной области, а не в глобальной области.

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

И наоборот, чтобы использовать переменную, функцию, класс, интерфейс и т. д., Экспортированные из другого модуля, его необходимо импортировать с помощью одной из форм импорта.

Модули декларативные, а отношения между модулями определяются в терминах импорта и экспорта на уровне файлов с кодом, которые именуются модулями.

Как это используют в CommonJS? Модули импортируют друг друга, используя загрузчик модулей. Во время выполнения загрузчик модуля отвечает за поиск и выполнение всех зависимостей модуля перед его выполнением. Хорошо известные загрузчики модулей, используемые в JavaScript — это загрузчик Node.js для модулей CommonJS и загрузчик RequireJS для модулей AMD в веб-приложениях.

Как это используют в ECMAScript 2015? Любой файл, содержащий импорт или экспорт верхнего уровня, считается модулем. И наоборот, файл без каких-либо объявлений импорта или экспорта верхнего уровня обрабатывается как сценарий, содержимое которого доступно в глобальной области.

Различие в использовании модулей CommonJS и ES6

Что предпочтительнее использовать?

Вопрос, относительно использовании модулей ES6 против CommonJS не однозначен. Первые универсальны, последние в основном используются в Node.js, но также поддерживаются сборщиками Webpack и другими компоновщиками, поэтому в приложениях на подобии React, Angular или Vue могут потенциально использоваться как import, так и require.

Начало работы с модулями 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 для импорта экспортированного материала в другой модуль.

Пулл соединений MySQL/Node.js

Ниже код, который организует это при помощи пулла соединений, который гораздо легче настраивать без непредвиденных ошибок:

var mysql = require('mysql');

var dbConfig = {
    host: "myhost",
    user: "user",
    password: "password",
    database: "dbname",
    port:3306
};

var user = require('./routes/user');
app.use('/user', user);
app.post('/user', function(req, res) {
    res.set({
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
        'Access-Control-Allow-Headers': 'X-Requested-With',
        'Access-Control-Allow-Credentials': 'true'
    });
    var data = req.body;
    var mysqlPool  = mysql.createPool(dbConfig);
    if(data.key=="ENTER_CUSTOMER")
    {
        mysqlPool.getConnection(function(err, connection) {
            if(err) throw err;
            connection.query("SELECT * FROM user where email LIKE '"+data.userName+"'", function(err, result, fields) {
                if(err) {
                    connection.release();
                    console.error(err);                   
                    return;
                }
                res.json({"key":data.key,"result":result});
                connection.release();
            });
        }); 
    }
});

 

Запуск любого процесса в демон/бакграунд при помощи pm2 и bash — скрипта

Допустим, есть такая задача, чтобы процесс не завершался после закрытия консоли и для этого есть утилита Node.js под названием pm2. Ссылка на проект в GitHub.

 

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

Установка

Делается это очень просто:

npm install pm2 -g

Запуск в демон

Запустим какое-нибудь приложение или процесс в демон:

pm2 start app.js

Вывод списка запущенных процессов, остановка, перезапуск и удаление процесса

Весь список процессов в делегировании:

pm2 list|ls

Остановка:

pm2 stop [options] <id|name|all|json|stdin...>

Перезапуск:

pm2 restart [options] <id|name|all|json|stdin...>

Удаление из списка делегирования:

pm2 delete <name|id|script|all|json|stdin...>

Это не полный перечень команд, но этого достаточно, чтобы быстро начать работу.

 

Реальный пример

Давайте теперь поставим задачу отправить в демон/бакграунд процесс запуска приложения Angular CLI на удаленном или локальном сервере. Для запуска приложения Angular в обычном режиме используется команда:

ng serve --host 0.0.0.0 --port 4200

Нам же нужно, сделать так, чтобы дать этому процессу название и запустить его в процесс делегирования pm2. Для этого создаем файл скрипта оболочки bash с расширением sh. Например, назовем его start.sh и запишем команду запуска процесса:

#!/bin/bash
ng serve --host 0.0.0.0 --port 4200

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

pm2 start start.sh --name serverapp

где serverapp — то название, которое мы дали процессу в списке задач делегирования pm2. Все, процесс запущен в демон/бакграунд режиме и чтобы посмотреть данный процесс в списке задач делегирования pm2 выполним команду:

pm2 list

После чего pm2 выведет в консоль список всех процессов, среди которых и наш serverapp:

Установка новой версии Node.js на CentOS

Данная статья является дополнением статьи по ссылке и отличается тем, что необходимо установить новую версию GCC 4.8.5 для компиляции исходников новой версии Node.js посредством чего решим ошибку установки:

WARNING: failed to autodetect C++ compiler version (CXX=g++)

В статье установим версию Node.js v8.0.0.

Примечание. Понятие «новая версия» относительна, поэтому, со временем версия версия Node.js 8.0.0 тоже устареет. Новая, относительно времени написания данной статьи.

 

Установка новой версии компилятора GCC на CentOS

Шаг 1. Устанавливаем GCC:

sudo yum install gcc-c++

Если после данной операции выводится то же предупреждение, что не установлена актуальная версия GCC, то идем к следующему шагу скачивания исходников и компиляции.

Шаг 2. Скачиваем исходники GCC для создания бинарников установки на CentOS:

Сначала переходим в папку /usr/src/:

cd /usr/src

и скачиваем исходники:

wget http://mirrors-usa.go-parts.com/gcc/releases/gcc-4.8.5/gcc-4.8.5.tar.gz

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

Шаг 3. Распаковываем исходники:

tar zxvf gcc-4.8.5.tar.gz

Шаг 4. Входим в распакованную папку и устанавливаем зависимости исходников:

cd gcc-4.8.5
./contrib/download_prerequisites

Шаг 5. Выходим из папки в корень /usr/src/ и создаем папку вывода компиляции:

cd ../
mkdir gcc-build-4.8.0

Было бы логично, что папка должна быть не с номерами версии 4.8.0, а с номерами 4.8.5, которую мы устанавливаем. Почему 4.8.0 я не знаю, это может быть клерикальная ошибка разработчиков, но последуем примеру.

Шаг 6. В созданном каталоге gcc-build-4.8.0 сгенерируем makefile:

cd gcc-build-4.8.0
../gcc-4.8.5/configure --enable-checking=release --enable-languages=c,c++ --disable-multilib

Вот каталог gcc-4.8.5, оригинал ошибочно принят за 4.8.0. Это ошибка разработчиков, которую я упомянул в шаге 5.

Шаг 7. Компиляция. В данном случае, ждал этот процесс около 3 часов, поэтому имейте в виду, что может идти долго:

make -j4

Выполняем эту команду в папке gcc-build-4.8.0.

Шаг 8. Устанавливаем скомпилированные бинарники:

sudo make install

Шаг 9. Удостоверимся, что GCC новой версии у нас установился:

gcc -v

Теперь, когда новый компилятор GCC для CentOS установлен продолжим установку новой версии Node.js

 

Установка Node.js v8.0.0 на CentOS

Сначала обновляем систему репозиторий:

yum update

Устанавливаем группу инструментов разработки Development Tools:

yum -y groupinstall "Development Tools"

Опция -y позволит избежать подтверждения действий на протяжении установки. После введите команду для установки ПО, необходимого для подключения к VPS и получения доступа к нескольким параллельным сессиям:

yum -y install screen

Переходим в папку /usr/src:

cd /usr/src

Загружаем архив с Node.js при помощи команды wget:

wget http://nodejs.org/dist/v8.0.0/node-v8.0.0.tar.gz

Все доступные архивы кода NodeJS можно посмотреть по ссылке http://nodejs.org/dist/.

Извлекаем содержимое скачанного архива:

tar zxf node-v8.0.0.tar.gz

Переместимся в каталог, используя команду cd:

cd node-v8.0.0

Выполняем скрипт configure:

./configure

Команда для компиляции исходного кода:

make

По окончанию данного процесса нужно выполнить общесистемную установку:

make install

Команда make install призвана разместить скомпилированные двоичные файлы в системный путь, чтобы каждый пользователь мог работать с ним, не отвлекаясь на дополнительные настройки. По умолчанию двоичный код Node.js “лежит” в /usr/local/bin/node.

 

Использованные источники:

记录:在bandwagon的VPS上装nodejs

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

Установка Node.js v0.x на CentOS

Примечание. Это легкий способ установки Node.js ?>v0.10.4, но таит в себе вопрос целесообразности, потому что таким способом можно установить только старую версию Node.js, на котором особо современyого ничего не сделать и даже AngularJS требует версию, начиная с Node.js v6. Версии Node.js требуют разные компиляторы, которыми собираются бинарники. В старых версиях требовался старый GCC, а в новых версиях требуется GCC версии GCC v4.8.5 или clang++ v3.4 и выше, таким образом, если попытаетесь установить новую версию Node.js на CentOS может выскочить ошибка в консоли:

WARNING: C++ compiler too old, need g++ 4.8 or clang++ 3.4 (CXX=g++)

Поэтому, если если хотите установить новую версию, то советую почитать эту статью.

Сначала обновляем систему репозиторий:

yum update

Устанавливаем группу инструментов разработки Development Tools:

yum -y groupinstall "Development Tools"

Опция -y позволит избежать подтверждения действий на протяжении установки. После введите команду для установки ПО, необходимого для подключения к VPS и получения доступа к нескольким параллельным сессиям:

yum -y install screen

Переходим в папку /usr/src:

cd /usr/src

Загружаем архив с Node.js при помощи команды wget:

wget http://nodejs.org/dist/v0.10.4/node-v0.10.4.tar.gz

Все доступные архивы кода NodeJS можно посмотреть по ссылке http://nodejs.org/dist/.

Извлекаем содержимое скачанного архива:

tar zxf node-v0.10.4.tar.gz

Переместимся в каталог, используя команду cd:

cd node-v0.10.4

Выполняем скрипт configure:

./configure

Команда для компиляции исходного кода:

make

По окончанию данного процесса нужно выполнить общесистемную установку:

make install

Команда make install призвана разместить скомпилированные двоичные файлы в системный путь, чтобы каждый пользователь мог работать с ним, не отвлекаясь на дополнительные настройки. По умолчанию двоичный код Node.js “лежит” в /usr/local/bin/node.

Отправка Email через PHP из Node.js через Ajax

Рассмотрим пример отправки почты через AJAX — запрос посредством PHP из сервера Node.js. Для начала нам потребуется инструмент генерации AJAX — запросов и для этого будем использовать модуль request. Установить ее очень просто:

npm install request

Отправка из Node.js:

var request = require('request');
request.
    post(
    {
        url:'http://xxx.xxx.xxx.xxx/mail/send.php', 
        form:{
            key: data.key,
            title: "Ваш пароль в сервисе",
            userEmail: data.userEmail,
            message:{
                "Логин":result[0].email,
                "Пароль":result[0].password
            }
        }
    }, 
    function(err, httpResponse, body){
        console.log(err);
        console.log(httpResponse);
        console.log(body);
    });

 

Прием в PHP — файле send.php:

<?php
$message = "";
foreach ($_REQUEST["message"] as $key => $value)
{
    $message .= "<br/><b>".htmlspecialchars($key)."</b>: ".htmlspecialchars($value)."<br/>";
}
// Для отправки HTML-письма должен быть установлен заголовок Content-type
$headers  = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=utf-8' . "\r\n";
// Дополнительные заголовки
$headers[] = 'To: Mary <mary@example.com>, Kelly <kelly@example.com>';
$headers[] = 'From: Birthday Reminder <birthday@example.com>';
$headers[] = 'Cc: birthdayarchive@example.com';
$headers[] = 'Bcc: birthdaycheck@example.com';

// Отправляем
mail($_REQUEST["userEmail"], $_REQUEST["title"], $message, $headers);

echo json_encode($_REQUEST);

Один из методов организации связи по методике REST клиент-серверного приложения

Данный метод не претендует на истинность, поэтому смею себе вольность рассуждать то, как можно организовать связь между клиентом в виде мобильного приложения и сервером. Учитывая, что напрямую к БД у клиента доступа нет, то все взаимодействие выборки, записи, обработки сервером организуются методом передами ключей-запросов(фильтров) POST — методом. Т.е., мы заранее определяем ключевое слово(фильтр) и порядок операций, которые будут сделаны на сервере после его отправки  из клиента и ждем, когда сервер нам ответит через AJAX — ответ и все. Ниже приведу простой пример такого взаимодействия.

Примерные шаги

  1. Создаем URL — адрес для отправки POST — запросов с клиента. К примеру, если запросы обрабатывают данные пользователя, то логично создать URL — адрес www.myhostname.com:3000/user и на него отправлять данные;
  2. Учитывая, что при отправке POST — запроса мы отправляем данные в формате JSON, то один параметр должен быть флагом. Данный флаг нужен, чтобы создавать фильтры внутри одного POST — запроса на один URL.
  3. Организация фильтра запроса в пределах одного сеанса. К примеру {«flag»:»read»,»userName»:»David», …}, что означает, что мы хотим прочесть юзера под определенным логином, после чего сервер возвратит клиенту запрощенные данные.

 

Разбираемся в каждом шаге

Создание URL для запроса — это обычное создание страницы в Node.js, с которого данные будут обрабатывать в POST — запросе. Лучще воспользоваться для этого express.js . Для этого создаем необходимую страницу в директориях views/routes как обычно это делается для приложения express.js. К  примеру так в папке views/users.pug:

extends layout

block content
  h1= title
  p Welcome to #{title}

и файл приема запросов routes/users.js:

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.render('users', { title: 'User Page' });
});

module.exports = router;

После этого по пути www.myhostname.com:3000/users сервер будет принимать данные в POST или в GET режиме. Для обработки этих запросов пишем в главном файлу сервера app.js обработчики этих запросов, учитывая URL — запроса. К примеру, чтобы обработать все запросы к URL www.myhostname.com:3000/users прописываем следующие строки:

...
//Регистрируем новый URL-страницу
var users = require('./routes/users');
...
//Указываем на использование этой страницы и обрабатываем запросы
app.use('/users', users);
app.post('/users', function(req, res) {
    //Даем доступ клиенту на обработку его данных сервером
    res.set({
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
        'Access-Control-Allow-Headers': 'X-Requested-With',
        'Access-Control-Allow-Credentials': 'true'
    });
    
    //Получаем данные от клиента
    var data = req.body;
    
    //...тут какая-то общая логика обработки
    
    //...Обработка для фильтра userReadFromDb
    if(data.key=="userReadFromDb")
    {
        //...тут какая-то локальная логика обработки
        //Отправляем результаты обработки обратно клиенту
        res.json({"key":data.key,"info":"Ok"});
    }else
    //...Обработка для фильтра userWriteToDb
    if(data.key=="userWriteToDb")
    {
        //...тут какая-то лоакльная логика обработки
        //Отправляем результаты обработки обратно клиенту
        res.json({"key":data.key,"info":"Ok"}); 
    }
    ...
});
...

Стоит отметить, что получаемые фильтры от клиента и отправляются ему обратно. Это нужно, чтобы в приложении клиента идентифицировать факт удачного обновления данных или получения определенных данных по указанному фильтру. Это очень удобно и экономит массу строк кода.

 

REST при помощи пулл соединений MySQL/Node.js

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

var mysql = require('mysql');

var dbConfig = {
    host: "myhost",
    user: "user",
    password: "password",
    database: "dbname",
    port:3306
};

var user = require('./routes/user');
app.use('/user', user);
app.post('/user', function(req, res) {
    res.set({
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
        'Access-Control-Allow-Headers': 'X-Requested-With',
        'Access-Control-Allow-Credentials': 'true'
    });
    var data = req.body;
    var mysqlPool  = mysql.createPool(dbConfig);
    if(data.key=="ENTER_CUSTOMER")
    {
        mysqlPool.getConnection(function(err, connection) {
            if(err) throw err;
            connection.query("SELECT * FROM user where email LIKE '"+data.userName+"'", function(err, result, fields) {
                if(err) {
                    connection.release();
                    console.error(err);                   
                    return;
                }
                res.json({"key":data.key,"result":result});
                connection.release();
            });
        }); 
    }
});