Node js & Подключение модулей без явного использования require или аналог namespace: различия между версиями

Материал из support.qbpro.ru
imported>Vix
Нет описания правки
imported>Vix
Нет описания правки
Строка 1: Строка 1:
  При работе с node.js часто приходится подключать файлы, расположенные не в текущей директории, и даже не в поддереве текущего каталога. Поэтому часто в модулях можно наблюдать что-то вроде
  При работе с node.js часто приходится подключать файлы, расположенные не в
текущей директории, и даже не в поддереве текущего каталога. Поэтому часто в
модулях можно наблюдать что-то вроде


  var obj = require('../../../a/b/c/someModule');  
  var obj = require('../../../a/b/c/someModule');  




Для меня гораздо удобнее подключать файлы относительно корня проекта (модуля), например
Для меня гораздо удобнее подключать файлы относительно корня проекта (модуля),
например


  var obj = require(base_dir + '/a/b/c/someModule');  
  var obj = require(base_dir + '/a/b/c/someModule');  




Однако и в этом случае довольно много писанины и необходимо откуда-то получать base_dir.
Однако и в этом случае довольно много писанины и необходимо откуда-то получать
Для себя я нашел решение проблемы, позволяющее больше не писать require и не узнавать base_dir:
base_dir.
Для себя я нашел решение проблемы, позволяющее больше не писать require и не
узнавать base_dir:


  var obj = lib.a.b.c.someModule;  
  var obj = lib.a.b.c.someModule;  
Строка 19: Строка 24:




Решение проблемы — создание хитрого «класса» (да простят меня гуру javascript за такое слово), свойствами которого являются директории и файлы указанной при создании класса директории. Если свойство-директория не является модулем, то данное свойство также является объектом нашего класса, свойствами которого являются директории и файлы этой директории.
Решение проблемы — создание хитрого «класса» (да простят меня гуру javascript за
такое слово), свойствами которого являются директории и файлы указанной при
создании класса директории. Если свойство-директория не является модулем, то
данное свойство также является объектом нашего класса, свойствами которого
являются директории и файлы этой директории.


Чтобы стало понятнее — приведу пример. Предположим у нас имеется следующая структура проекта:
Чтобы стало понятнее — приведу пример. Предположим у нас имеется следующая
структура проекта:




Строка 29: Строка 39:
  \a\index.js
  \a\index.js


  var LazyLoader = require('./LazyLoader'); global.lib = new LazyLoader(__dirname);  var foo = new lib.a.b.Foo(); console.log(foo.name); console.log(foo.bar.name);  
  var LazyLoader = require('./LazyLoader'); global.lib = new
LazyLoader(__dirname);  var foo = new lib.a.b.Foo(); console.log(foo.name);
console.log(foo.bar.name);  




Строка 35: Строка 47:
  Foo.js
  Foo.js


  module.exports = function () {    this.name = 'Foo';    this.bar = new lib.a.c.Bar(); };  
  module.exports = function () {    this.name = 'Foo';    this.bar = new
lib.a.c.Bar(); };  




Строка 47: Строка 60:
  LazyLoader.js
  LazyLoader.js


  var fs = require('fs'); var path = require('path');  function getter(key, dir, file) {    delete this[key];      var fullPath = path.join(dir, file);    try {        require.resolve(fullPath);    } catch (e) {        if (fs.existsSync(fullPath)) {            this[key] = new LazyLoader(path.join(dir, file));        }        return this[key];    }     this[key] = require(fullPath);    return this[key]; }  function LazyLoader(dir) {      var loader = this;      if (!fs.existsSync(dir)) {        return;    }     fs.readdirSync(dir).forEach(function(file) {        var key = file.replace(/\.js$/, '');        loader.__defineGetter__(key, getter.bind(loader, key, dir, file))    }); }  module.exports = LazyLoader;   
  var fs = require('fs'); var path = require('path');  function getter(key, dir,
file) {    delete this[key];      var fullPath = path.join(dir, file);    try
{        require.resolve(fullPath);    } catch (e) {        if
(fs.existsSync(fullPath)) {            this[key] = new
LazyLoader(path.join(dir, file));        }        return this[key];    }    
this[key] = require(fullPath);    return this[key]; }  function LazyLoader(dir)
{      var loader = this;      if (!fs.existsSync(dir)) {        return;    }  
  fs.readdirSync(dir).forEach(function(file) {        var key =
file.replace(/\.js$/, '');        loader.__defineGetter__(key,
getter.bind(loader, key, dir, file))    }); }  module.exports = LazyLoader;   




Строка 57: Строка 79:




В данном примере я использовал глобальную переменную lib. Если вы противник глобальных переменных (или вы просто разрабатываете отдельный публичный модуль) — то можно отказаться от глобальной переменной — в Foo.js придется написать, например, так:
В данном примере я использовал глобальную переменную lib. Если вы противник
глобальных переменных (или вы просто разрабатываете отдельный публичный модуль)
— то можно отказаться от глобальной переменной — в Foo.js придется написать,
например, так:


  var path = require('path'); var LazyLoader = require('../../LazyLoader'); var lib = new LazyLoader(path.join(__dirname, '../../'));  module.exports = function () {    this.name = 'Foo';    this.bar = new lib.a.c.Bar(); };  
  var path = require('path'); var LazyLoader = require('../../LazyLoader'); var
lib = new LazyLoader(path.join(__dirname, '../../'));  module.exports = function
() {    this.name = 'Foo';    this.bar = new lib.a.c.Bar(); };  




И если у вас всего 1-2 подключаемых файла, то возможно данный подход покажется вам неуместным, но при большем количестве — вы должны ощутить существенное облегчение.
И если у вас всего 1-2 подключаемых файла, то возможно данный подход покажется
Кроме того вы можете сохранять ссылки на директории для сокрашения записи, например:
вам неуместным, но при большем количестве — вы должны ощутить существенное
облегчение.
Кроме того вы можете сохранять ссылки на директории для сокрашения записи,
например:


  var lib = new LazyLoader(__dirname); var logLig = lib.dir1.dir2.dir3.log; var f1 = new logLib.ConsoleLogger(); var f2 = new logLib.FileLogger(); // вместо // var f1 = new lib.dir1.dir2.dir3.log.ConsoleLogger(); // var f2 = new lib.dir1.dir2.dir3.log.FileLogger();  //  и вместо // var f1 = new require(__dirname + '/dir1/dir2/dir3/log/ConsoleLogger)(); // var f2 = new require(__dirname + '/dir1/dir2/dir3/log/FileLogger)();  
  var lib = new LazyLoader(__dirname); var logLig = lib.dir1.dir2.dir3.log; var
f1 = new logLib.ConsoleLogger(); var f2 = new logLib.FileLogger(); // вместо //
var f1 = new lib.dir1.dir2.dir3.log.ConsoleLogger(); // var f2 = new
lib.dir1.dir2.dir3.log.FileLogger();  //  и вместо // var f1 = new
require(__dirname + '/dir1/dir2/dir3/log/ConsoleLogger)(); // var f2 = new
require(__dirname + '/dir1/dir2/dir3/log/FileLogger)();  




Строка 75: Строка 110:
1. Указанный подход позволяет имитировать аналог namespace в других языках.
1. Указанный подход позволяет имитировать аналог namespace в других языках.


2. Приведенный в примере LazyLoader легко расширить для работы с несколькими директориями, например, для смешвания или переопределения модулей:
2. Приведенный в примере LazyLoader легко расширить для работы с несколькими
директориями, например, для смешвания или переопределения модулей:


  var lib = new LazyLoader('globalLibDir', 'applicationSpecificLibDir');  
  var lib = new LazyLoader('globalLibDir', 'applicationSpecificLibDir');  
Строка 81: Строка 117:




3. Если Вы нашли неточности, опечатки или у вас есть альтернативные решения подключения модулей — пишите в ЛС.
3. Если Вы нашли неточности, опечатки или у вас есть альтернативные решения
подключения модулей — пишите в ЛС.


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

Версия от 14:55, 13 июня 2014

При работе с node.js часто приходится подключать файлы, расположенные не в

текущей директории, и даже не в поддереве текущего каталога. Поэтому часто в модулях можно наблюдать что-то вроде

var obj = require('../../../a/b/c/someModule'); 


Для меня гораздо удобнее подключать файлы относительно корня проекта (модуля), например

var obj = require(base_dir + '/a/b/c/someModule'); 


Однако и в этом случае довольно много писанины и необходимо откуда-то получать base_dir. Для себя я нашел решение проблемы, позволяющее больше не писать require и не узнавать base_dir:

var obj = lib.a.b.c.someModule; 


Идея решения


Решение проблемы — создание хитрого «класса» (да простят меня гуру javascript за такое слово), свойствами которого являются директории и файлы указанной при создании класса директории. Если свойство-директория не является модулем, то данное свойство также является объектом нашего класса, свойствами которого являются директории и файлы этой директории.

Чтобы стало понятнее — приведу пример. Предположим у нас имеется следующая структура проекта:


Содержимое файлов:

\a\b\Foo.js
\a\c\Bar.js
\a\index.js
var LazyLoader = require('./LazyLoader'); global.lib = new

LazyLoader(__dirname); var foo = new lib.a.b.Foo(); console.log(foo.name); console.log(foo.bar.name);


Foo.js
module.exports = function () {     this.name = 'Foo';     this.bar = new

lib.a.c.Bar(); };


Bar.js
module.exports = function () {     this.name = 'Bar'; }; 


LazyLoader.js
var fs = require('fs'); var path = require('path');  function getter(key, dir,

file) { delete this[key]; var fullPath = path.join(dir, file); try { require.resolve(fullPath); } catch (e) { if (fs.existsSync(fullPath)) { this[key] = new LazyLoader(path.join(dir, file)); } return this[key]; } this[key] = require(fullPath); return this[key]; } function LazyLoader(dir) { var loader = this; if (!fs.existsSync(dir)) { return; }

  fs.readdirSync(dir).forEach(function(file) {         var key =

file.replace(/\.js$/, ); loader.__defineGetter__(key, getter.bind(loader, key, dir, file)) }); } module.exports = LazyLoader;


При запуске node index.js в консоли распечатается (как и ожидалось)

Foo Bar 


В данном примере я использовал глобальную переменную lib. Если вы противник глобальных переменных (или вы просто разрабатываете отдельный публичный модуль) — то можно отказаться от глобальной переменной — в Foo.js придется написать, например, так:

var path = require('path'); var LazyLoader = require('../../LazyLoader'); var

lib = new LazyLoader(path.join(__dirname, '../../')); module.exports = function () { this.name = 'Foo'; this.bar = new lib.a.c.Bar(); };


И если у вас всего 1-2 подключаемых файла, то возможно данный подход покажется вам неуместным, но при большем количестве — вы должны ощутить существенное облегчение. Кроме того вы можете сохранять ссылки на директории для сокрашения записи, например:

var lib = new LazyLoader(__dirname); var logLig = lib.dir1.dir2.dir3.log; var

f1 = new logLib.ConsoleLogger(); var f2 = new logLib.FileLogger(); // вместо // var f1 = new lib.dir1.dir2.dir3.log.ConsoleLogger(); // var f2 = new lib.dir1.dir2.dir3.log.FileLogger(); // и вместо // var f1 = new require(__dirname + '/dir1/dir2/dir3/log/ConsoleLogger)(); // var f2 = new require(__dirname + '/dir1/dir2/dir3/log/FileLogger)();



Заключение


1. Указанный подход позволяет имитировать аналог namespace в других языках.

2. Приведенный в примере LazyLoader легко расширить для работы с несколькими директориями, например, для смешвания или переопределения модулей:

var lib = new LazyLoader('globalLibDir', 'applicationSpecificLibDir'); 


3. Если Вы нашли неточности, опечатки или у вас есть альтернативные решения подключения модулей — пишите в ЛС.

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