Node.JS Избавься от require() навсегда

от автора

Анализируя исходные коды прошлых проектов, рейтинг популярности прямых вызовов функций показал, что прямой вызов require() встречается в коде node-модулей почти так же часто, как Array#forEach(). Самое обидное, что чаще всего мы подключаем модули "util", "fs" и "path", чуть реже "url". Наличие других модулей зависит уже от задачи модуля. Причем, говоря о модуле "util" (без него вообще не могу писать ничего кроме одноразовых скриптов), загружается в память node-процесса даже если вы
ни разу его не подключали.

В прошлой статье Node.JS Загрузка модулей по требованию я поведал о возможности автоматической загрузкой модуля при первом обращении
к его именованной ссылке. Если честно, на момент написания той статьи, я не был уверен в том, что такой подход не станет причиной странного поведения node-процесса. Но, уже сегодня с гордостью могу ручаться, что denamdLoad() работает уже пол года в продакшене. Как мы его только не гоняли… Это и нагрузочное тестирование конкретного процесса, и работа denamdLoad() в
worker-процессах кластеров, и работа процесса под небольшой нагрузкой в течении долгого времени. Результаты сравнивались с использованием denamdLoad() и с использованием require(). Никаких существенных отклонений в сравнении не было замечено.

Сегодня речь пойдет уже не о стабильности denamdLoad(). Если кому интересно, задавайте вопросы в комментариях, сделаю скриншоты, могу рассказать о методах и инструментах тестирования, других возможностях использования подхода. Сегодня, как следует из заголовка статьи, мы будем избавляться от успевших уже надоесть require() в шапках каждого node-модуля.


Предположим, мы работаем над проектом с названием "mytestsite.com", и находится он у нас здесь:

~/projects/mytestsite.com

Создадим исполняемый файл нашего проекта, например, по адресу:

~/projects/mytestsite.com/lib/bin/server.js

Внутри попробуем вызвать подключенные модули без предварительных require():

console.log(util.inspect(url.parse('https://habrahabr.ru/')));

Теперь создадим файл "require-all.js" где-нибудь, вне всех проектов.
Например, здесь:

~/projects/general/require-all.js

Согласно документации, все предопределенные переменные и константы каждого node-модуля являются свойствами global. Соответственно, мы можем определять и свои глобальные объекты. Так мы должны поступить со всеми используемыми нами модулями.

Наполним require-all.js списком всех используемых модулей во всех проектах:

// нет смысла оставлять неподгруженным модуль "util", // т.к. его все равно до загрузки подгружает модуль "console". // А console.log(), если ему передать объект единственным параметром, // в свою очередь вызывает util.inspect() global.util = require('util');  // так выглядит подключение других стандартных модулей, например: demandProperty(global, 'fs', 'fs'); demandProperty(global, 'path', 'path'); demandProperty(global, 'url', 'url');  // абсолютно так же выглядит подключение npm-модулей, например: demandProperty(global, 'express', 'express');  // а, вот, например, так можно подключить локальный модуль: demandProperty(global, 'routes', './../mytestsite.com/lib/routes');  // определение demandProperty function demandProperty(obj, name, modPath){ // тело вырезано для простоты схемы // необходимо взять из статьи по ссылке выше. } 

Можно представить список модулей в виде массива или карты (Map), и, например, пройтись по нему/ней циклом, чтобы не повторять строчку кода с вызовом demandProperty(). Как говорится, кому как удобнее.

Теперь, осталось лишь запустить это хозяйство. Добавим ключ --require к
запуску node (версии >= 4.x):

node    --require ~/projects/general/require-all.js \         ~/projects/mytestsite.com/lib/bin/server.js 

Ошибок нет. Скрипт отработал как надо:

Url {   protocol: 'https:',   slashes: true,   auth: null,   host: 'habrahabr.ru',   port: null,   hostname: 'habrahabr.ru',   hash: null,   search: null,   query: null,   pathname: '/',   path: '/',   href: 'https://habrahabr.ru/' }

Если у вас много проектов, для удобства разворачивания проектов, можно создать по своему require-all.js внутри каждого проекта по отдельности.

node    --require ~/projects/mytestsite.com/lib/require-all.js \         ~/projects/mytestsite.com/lib/bin/server.js

Расширяя последний случай, отмечу, можно даже использовать несколько таких require-all.js одновременно:

node    --require ~/projects/general/require-all.js \         --require ~/projects/mytestsite.com/lib/require-all.js \         ~/projects/mytestsite.com/lib/bin/server.js

Повторюсь из прошлой статьи: Если demandProperty() определена не в нашем файле(1) (откуда вызываем demandProperty()), а в каком-нибудь файле(2), причем файл(1) и файл(2) находятся в разных директориях, последним параметром необходимо передавать полный путь до модуля, например:

demandProperty(global, 'routes', path.join(__dirname, './../mytestsite.com/lib/routes'));

Иначе, тот require(), что вызывается из demandProperty() будет искать модуль относительно папки, где расположили тот самый файл(2) с описанием demandProperty(), вместо того, чтобы искать модуль относительно файла(1), откуда мы вызываем demandProperty().

Спасибо за внимание. Всем удачного рефакторинга!

ссылка на оригинал статьи http://habrahabr.ru/post/273405/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *