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

Деплой проекта Django в продакшн с Nginx + Gunicorn + Supervisor

В данном посте рассмотрим один из современных вариантов деплоя готового проекта Django в готовый режим работы. В посте будет рассмотрены моменты настройки и интеграции с Django таких инструментов, как:

  • Nginx — WEB-сервер;
  • Gunicorn — HTTP-сервер Python WSGI;
  • Supervisor — менеджер процессов.

Исходные данные

Для использования данного поста требуются следующие исходные данные по ПО:

  • готовый удаленный сервер Linux в виде VPS/VDS с выделенным IP;
  • установленный и настроенный Python 3 c виртуальными средами и разрабатываемым проектом на Django;

Исходные данные по неймингу:

  • /home/myprojectenv/ — абсолютный путь к папке виртуальной среды;
  • /home/myprojectenv/myproject/ — абсолютный путь к папке проекта на Django;
  • (myprojectenv)user@host: /myprojectenv/ —  вид строки запроса в консоли под активной виртуальной средой Python;
  • 11.22.33.44IP нашего удаленного сервера VDS/VPS;
  • 8000 — порт, на котором будет подвешен наш проект.

 

Установка и настройка Nginx и Gunicorn

Для начала нужно сделать апгрейд

sudo apt-get update
sudo apt-get upgrade

Ставим Nginx

sudo apt-get install nginx

Теперь заходим под виртуальную среду нашего(или вашего) проекта

cd /home/myprojectenv/
source bin/activate

Под активной виртуальной средой ставим Gunicorn

(myprojectenv)user@host: /home/myprojectenv# pip[pip3] install gunicorn

Замечание. Помните про версии pip. Если Python 2.x, то пишем pip, если Python 3.xpip3. Это по дефолту, если у вас не настроено иначе.

Теперь, перед тем, как идти дальше, протестируем удачную установку Gunicorn и интеграцию с проектом Django и для этого выполняем команду

(myprojectenv)user@host: /home/myprojectenv/myproject# gunicorn myproject.wsgi:application --bind 11.22.33.44:8000

Идем в браузер и запускаем по адресу страницу 11.22.33.44:8000 и убеждаемся, что все ок.

Далее настраиваем папку статических файлов и для этого открываем файл настроек проекта в папке /home/myprojectenv/myproject/myproject/settings.py

nano settings.py

Добавляем в этот файл параметр STATIC_ROOT, если его нет, со следующим значением

...
STATIC_ROOT = '/home/myprojectenv/myproject/static/'
...

Сохраняем, выходим и далее выполняем из папки проекта команду

(myprojectenv)user@host: /home/myprojectenv/myproject# python manage.py collectstatic

После чего в папке проекта появится новая папка с именем static.

Теперь идем и настраиваем Nginx по пути в файле

cd /etc/nginx/sites-available/

открываем файлик default

nano default

и перепишем некоторые моменты, как ниже

server {
    listen 80;
    server_name 11.22.33.44; #либо ip, либо доменное имя
    access_log  /var/log/nginx/example.log;

    location /static/ {
        root /home/myprojectenv/myproject/;
        expires 30d;
    }

    location / {
        proxy_pass http://127.0.0.1:8000; 
        proxy_set_header Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
  }

сохраняем и выходим.

Выполняем перезапуск Nginx, чтобы изменения вступили в силу

sudo service nginx restart

 

Активируем нашу среду и заходим в папку проекта для проверки запуска в связке Gunicon + Nginx

(myprojectenv)user@host: /home/myprojectenv/myproject# gunicorn myproject.wsgi:application

В браузере набираем 11.22.33.44:8000 и убеждаемся, что все ок.

Установка и настройка Supervisor

Чтобы ваше приложение стартовало после любого непредвиденного рестарта системы или сбоя, нам нужно использовать в деле supervisor.

Установим supervisor

apt-get install supervisor

Создадим конфигурационный файл для gunicorn в подпапке проекта (myproject/myproject/) рядом с settings.py

cd /home/myprojectenv/myproject/myproject
touch gunicorn.conf.py
nano gunicorn.conf.py

и записываем туда такие данные, которые означают, что данный проект будет запущен на IP и порте 0.0.0.0:8000, что будет означать внешний адрес нашего VPS. Если у вас сервер локальный, то ставим 127.0.0.1

bind = '0:8000'
workers = 3
user = "nobody"

Теперь создаем конфигурационный файл для supervisor

cd /etc/supervisor/conf.d/
touch myproject.conf
nano myproject.conf

и записываем туда такие данные

[program:myproject]
command=/home/myprojectenv/bin/gunicorn myproject.wsgi:application -c /home/myprojectenv/myproject/myproject/gunicorn.conf.py
directory=/home/myprojectenv/myproject
user=nobody
autorestart=true
redirect_stderr=true

 

Запускаем проект через supervisor

supervisorctl restart myproject

В браузере набираем 11.22.33.44:8000 и убеждаемся, что все ок.

Дополнительный тест. Можно также перезагрузить сервер

shutdown -r now

и убедиться, что наш проекта на Django сам автоматом встал и заработал при помощи supervisor.

При использовании Supervisor необходимо использовать его подпроцесс управления supervisorctl. Остальные основные команды supervisor

  • supervisorctl start myproject — запуск процесса;
  • supervisorctl reread — перезапуск системы;
  • supervisorctl update — обновить систему;
  • supervisorctl status myproject — узнать статус процесса;
  • supervisorctl remove myproject — удалить процесс.

 

Возможные ошибки использования Supervisor и Gunicorn

Ошибки в файлах конфигурации

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

  • Файл конфигурации Gunicorn в пределах текущего проекта /home/myprojectenv/myproject/myproject/gunicorn.conf.py
  • Файл конфигурации проекта в виде управляемого процесса внутри Supervisor /etc/supervisor/conf.d/myproject.conf

 

Ошибка запуска из-за занятости стандартного порта Supervisor

Обычно, эта ошибка выглядит так, при запуске команды запуска supervisorctl start myproject

cloudApp: ERROR (spawn error)

или так, при запуске команды поиска ошибок supervisord -n

Error: Another program is already listening on a port that one of our HTTP servers is configured to use.  Shut this program down first before starting supervisord

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

Первый. Выводим весь список процессов Supervisor и убиваем нужный по PID(в списке он будет после первого)

ps -ef | grep supervisord
sudo kill -9 PID_OUR_PROJECT

или

ps -ef | grep supervisord
kill -s SIGTERM PID_OUR_PROJECT

 

Второй. Убиваем сам supervisord

sudo pkill supervisord

 

Третий. Удаляем файл supervisor.sock и запускаем supervisor обратно

sudo unlink /run/supervisor.sock
sudo /etc/init.d/supervisor start

Теперь перезапускаем проект

supervisorctl start cloudApp
Ошибка ERROR (no such process) при запуске нового процесса

Данная ошибка появляется, когда мы создали новый проект, но он не обновился в системе Supervisot и для решения этой проблемы нам нужно перезаписать все процессы и перезагрузить supervisorctl

supervisorctl reread
supervisorctl reload

 

Если не удается идентифицировать ошибку

Если проблему трудно найти или идентифицировать, то будет полезной использовать команду

supervisord -n

который выведет весь список ошибок в файлах или сервисах, которые мешают запускаться процессу Supervisor.

 

Использованные материалы:

  1. Django + Python3 + Nginx + Gunicorn + DO;
  2. Setting up Django with Nginx, Gunicorn, virtualenv, supervisor and PostgreSQL

 

Как построить Bootstrap из исходников

Настройка инструментов

Bootstrap использует скрипты NPM для их построения. Наш package.json включает подходящие методы для работы с фреймворком, включающие компиляцию кода, запуск тестов и многое другое.

Используя систему построения Bootstrap можно запустить документацию локально, вам всего лишь нужно скачать исходники последней версии Bootstrap и Node.js. В следующих шагах предполагается, что вы готовы к подводным камням:

  1. Загружаем и устанавливаем Node.js, который будет необходим для управления зависимостями Bootstrap;
  2. Переходим в корень папки скачанных исходников /bootstrap  и выполняем команду npm install для установки наших локальных зависимостей, находящихся в списке файла package.json;
  3. Устанавливаем Ruby, устанавливаем Bundler при помощи команды gem install bundler, и в конце запускаем команду установки bundle install. Это должно установить все зависимости Ruby, такие как Jekyll и плагины.

Замечания для пользователей Windows. Необходимо читать это руководство для получения Jekyll и установить без проблем.

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

Bootstrap использует скрипты NPM для построения. Наш package.json включает методы для работы с фреймворком, включая компиляции кода, запуска тестов и другое.

 

Использование скриптом NPM

Наш package.json включает следующие команды и задачи:

Задача Описание
npm run dist npm run dist создает папку /dist с скомпилированными файлами. Используются SassAutoprefixer и UglifyJS.
npm test Похоже на npm run dist и плюс это запустит документацию локально.
npm run docs Построение и склейка CSS/JavaScript для документации. Вы сможете затем запустить документацию локально npm run docs-serve.

Обновление WordPress без данных FTP

В зависимости от настройки серверов обновление движка/тем/плагинов может требовать от пользователя адрес сервера, логин и пароль для FTP. Обычно это происходит, когда веб-сервер запущен от имени другого пользователя. Попробуйте добавить в wp-config.php строку:

define('FS_METHOD', 'direct');

…и обновление будет происходить напрямую.

Установка темы Zurb Foundation на свой WordPress — сайт

Что, если вы решили изменить библиотеке Bootstrap и решили попробовать на вкус альтернативный вариант в виде Zurb Foundation? Эта статья покажет, как его установить и скомпилить.

Начальные условия

У нас есть установленный голый движок WordPress, который работает у нас либо на локальном или удаленном сервере.

Перед работой с темой

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

curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo apt-get install -y build-essential
  1. Первым делом скачиваем исходники темы-скелетона по адресу или на GitHub.
  2. Устанавливаем эту тему в нашу систему WordPress, но если сейчас открыть сайт, то ничего не заработает, потому что тема еще не скомпилирована.
  3. Входим в корневую папку темы через командную строку системы и выполняем процесс компиляции и настройки:

Устанавливаем все зависимости

npm install

Конфигурируем и для этого необходимо файл config-default.yml скопировать под именем config.yml и произвести ряд настроек (актуально, если у вас локальный сервер).

Запускаем процесс построения

npm run build

Создание конечного архива с темой

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

npm run package

Данная команда соберет все скомпилированные исходники без лишних вспомогательных файлов в один zip — архив темы в папке packaged.

Примечания

Если выходи ошибка npm ERR! Failed at the node-sass@4.7.2 postinstall script. , надо установить зависимости для sass

sudo npm i gulp-sass -ES --unsafe-perm=true

 

Раскрытие выпадающего меню при наведении в Bootstrap 3/4

По-умолчанию выпадающиее меню в Bootstrap раскрывается при клике и сворачивается тоже только при клике мышкой.

Было бы не плохо, чтоб раскрытие и сворачивание меню происходило при наведении мышки. И это сделать совершенно не сложно! Для этого нужно лишь добавить CSS правило:

.dropdown:hover > .dropdown-menu { 
    display: block; 
}

И выпадающее меню реагирует на наведение.

Отключение обновлений в WordPress

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

Запретить обновление темы

Рассмотрим отключение обновлений тем. Тут привести 3 способа, как это сделать.

Способ 1.

Суть первого способа заключается в изменении файла стилей. Да да, именно файла стилей — style.css. В начале этого файла, пишутся некоторые параметры, например — название темы, автор и нужная нам версия.

Выглядит такая строка примерно так — Version: 1.0, смотрите внимательно. она где-то в начале. Так вот, чтобы отключить обновления, Вам просто нужно изменить версию на другую, которая будет побольше, например так — Version: 999.0.

Теперь получается, что любая версия будет меньше указанной, а значит не актуальной и тема не будет обновляться.

 

Способ 2. 

Второй способ потребует от Вас тоже небольшого вмешательства в код темы, а именно в файл пользовательских функций — functions.php. Открываем его для редактирования, а потом в самый конец перед закрывающим тегом PHP — ?>, если такого нет, то просто в конец, добавляем такие вот функции.

// отключаем обновление тем
remove_action( 'load-update-core.php', 'wp_update_themes' );
add_filter( 'pre_site_transient_update_themes', '__return_null' );
 
// отключаем авто обновления
add_filter( 'auto_update_theme', '__return_false' ); 
 
// спрячем имеющиеся уведомления
add_action('admin_menu','hide_admin_notices');
function hide_admin_notices() {
    remove_action( 'admin_notices', 'update_nag', 3 );
}

По комментариям видно что мы отключаем сразу несколько параметров — обновление тем, авто обновления и уведомления о надобности обновится.

Способ 3.

Последний способ, наверное самый простой, но требует установки плагина. Скачивает с сайта WordPress плагин — Easy Updates Manager или WordPress Theme Updates, ну и подобные им, которые сделают все за Вас.

Как по мне, то лучше выбрать один из ручных методов, ведь использование плагинов лишний раз нагружает Ваш WordPress, хоть и не сильно, но все же.

Запретить обновление плагина

В некоторых случаях может оказаться, что обновлять плагин вам не нужно. Это может произойти, если вы самостоятельно внесли доработки и изменения в код плагина. Тогда обновление перезапишет все ваши правки. Либо же если авторы плагина в чем-то изменили концепцию, и новая версия вас больше не устраивает. В моем случае так случилось с замечательным плагином NextGen Gallery, который после обновления до версии 2 стал неповоротливым и тяжело модифицируемым.

Способ 1.

Версия плагина прописывается в начале его главного файла. Открываем список плагинов, выбираем нужный и нажимаем «Изменить».

<?php
/*
Plugin Name: NextGEN Gallery
Plugin URI: http://www.nextgen-gallery.com/
Description: A NextGENeration Photo Gallery for WordPress
Author: Photocrati
Author URI: http://www.photocrati.com/
Version: 1.9.13

Если заменить значение «Version:» на большое число, например 99.99, то проверка обновлений будет сообщать, что установленная  версия новее, и не будет предлагать обновиться.

Способ 2.

Пропишем фильтр, позволяющий блокировать обновления конкретных плагинов централизованно. Для этого нужно в файл wp-config.php добавить массив:

<?php
$DISABLE_UPDATE = array( 'nextgen-gallery', 'nospamnx', 'другие-плагины');

Где значением элемента массива является название директории, в которой расположен плагин,  или его название, если плагин не имеет своей категории (состоит из одного файла).

Затем в файл functions.php текущей темы следует добавить код, который будет обрабатывать данный массив и запрещать для указанных плагинов обновления:

<?php
// запрет обновления выборочных плагинов
function filter_plugin_updates( $update ) {    
    global $DISABLE_UPDATE; // см. wp-config.php
    if( !is_array($DISABLE_UPDATE) || count($DISABLE_UPDATE) == 0 ){  return $update;  }
    foreach( $update->response as $name => $val ){
        foreach( $DISABLE_UPDATE as $plugin ){
            if( stripos($name,$plugin) !== false ){
                unset( $update->response[ $name ] );
            }
        }
    }
    return $update;
}
add_filter( 'site_transient_update_plugins', 'filter_plugin_updates' );

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

Источники:

  1. Как отключить обновления темы WordPress
  2. Запрещаем обновление конкретного плагина в WordPress