Я — frontend разработчик и в последнее время мне все чаще приходится пользоваться нодой, будь то использование webpack-а для сборки проекта, либо настройка различных gulp тасков. Хоть у меня и нету большого опыта в использование ноды, со временем у меня накопилось три вещи, которые мне хотелось бы улучшить при работе с модулями:
- Избавиться от кучи require-ов в начале каждого файла
- Подгружать модули только тогда, когда они нужны(особенно это актуально для gulp тасков)
- Иметь возможность работать с локальными модулями проекта, как с внешними модулями, то есть вместо, например,
вызова var core = require(‘../../deep/deep/deep/core/core’), вызывать этот же модуль вот так var core = require(‘core’)
Для решения всех трех задач по отдельности есть уже различные модули и решения, но во-первых, все они имеют, как мне кажется, недостатки, а во-вторых, ни одно из решений не решает все три проблемы вместе.
Например, для подгрузки модулей по требованию(он же lazy load или load on demand), есть модуль gulp-load-plugins. Он решает 1-ую и 2-ую проблему, но не решает 3-юю и имеет еще один недостаток — чтобы подключить модули, нужно в каждом файле, где эти модули нужны, производить инициализацию gulp-load-plugins модуля. Можно, конечно, делать инициализацию в отдельном файле и экспортировать из файла значение, но в таком случае придется подключать этот файл с использованием относительных или абсолютного путей.
Для решения 3-ей проблемы около года назад в npm добавили поддержку так званых локальных модулей. Суть сводится к тому, что
в package.json в dependencies нужно указать в качестве ключей имена модулей, а в значениях — относительные пути к папкам локальных модулей с префиксом «file:», например, вот часть package.json файла:
"dependencies": { "lodash": "^2.0.0", "core": "file:deep/deep/deep/core", "my-other-module": "file:/my-other-module" }
При этом папки локальных модулей должны быть оформлены, как обычные модули, то есть должны содержать свой package.json и readme.md файлы. После запуска npm i ваши локальные модули будут установлены в папку node_modules, как обычные модули. По мне так это крайне неудобно класть каждый файл проекта в отдельную папку, да еще и заводить на него package.json и readme.md файлы.
Одним словом я не нашел хорошего решения для описанных мною проблем, может плохо искал, но облазив различные форумы и прочитав различные свежие статьи пришел к выводу, что люди до сих пор ищут хорошее решение всех этих проблем.
В итоге я решил написать свое решение, возможно, свой велосипед, о котором и хочу поведать вам. На сколько он хорош или плох судить вам. Итак, позвольте представить вам sp-load. Сразу оговорюсь, префикс sp- не несет в себе никакого сакрального смысла, это всего лишь первые буквы моей фамилии и имени и добавлен не с целью прославить меня, а по причине того, что имена «load», «loader» и прочие были уже заняты.
Итак, вы сделали npm i sp-load -S в своем проекте. Предположим, что вы имеете следующее содержимое package.json файла:
{ "name": "your-project", "version": "1.0.0", "main": "index.js", "dependencies": { "lodash": "^3.10.1", "sp-load": "^1.0.0" }, "devDependencies": { "gulp": "^3.9.0", "webpack": "^1.12.9" }, "_localDependencies": { "core": "./core/core", "some-module": "./deep/deep/deep/deep/deep/deep/deep/some-module" } }
И имеете следующую структуру файлов:
your-project/ node_modules sp-load/ ... gulp/ ... lodash/ ... webpack/ ... package.json core/ core.js deep/ deep/ deep/ deep/ deep/ deep/ deep/ some-module.js gulpfile.js index.js
Что вам нужно сделать, чтобы использовать sp-load в простейшем виде? Всего одну вещь, сделать var $ = require(‘sp-load’); внутри какого-либо файла, например, вот содержимое gulpfile.js:
'use strict'; var $ = require('sp-load'), webpackConfig = {}; $.gulp.task("webpack", function (callback) { $.webpack(webpackConfig, function (err, stats) { callback(); }); });
Содержимое some-module.js:
'use strict'; function someModuleFuction() { console.log('I\'m some module function call!'); } module.exports = someModuleFuction;
Содержимое core.js:
'use strict'; function coreModuleFuction() { console.log('I\'m core module function call!'); } module.exports = coreModuleFuction;
Содержимое index.js:
'use strict'; var $ = require('sp-load'); $.someModule(); $.core();
Как вы видите, всё что нужно сделать — подключить sp-load модуль. Он возвращает объект, содержащий список модулей, которые будут подгружены по требованию, то есть модуль будет загружен node-ой при первом обращение по имени модуля, например, $.core().
Также вы, наверное, заметили нестандартное свойство "_localDependencies" в package.json. В этом свойстве вы можете определить список локальных модулей вашего проекта. Ключи объекта — названиям модулей, значения — относительный путь к файлу модуля(путь относительный package.json файла).
Если же вы хотите обращаться к модулям не как к свойствам объекта, а как к переменным, то можете сделать это следующим образом(в примере используется es6 деструктуризация. как включить возможности es6 в nodejs вы можете прочесть в документацие nodejs):
'use strict'; var {someModule, core} = require('sp-load'); someModule(); core();
Или с использованием es5:
'use strict'; var $ = require('sp-load'), someModule = $.someModule, core = $.core; someModule(); core();
В обоих этих примерах, модули someModule и core буду подгружены при присвоение, если же вы хотите, чтобы они были подгружены в момент первого их использования(то есть on demand), то обращайтесь к модулям, как к свойствам объекта $.
Это было простейшее использование sp-load, без каких-либо конфигураций, за исключением использования свойства "_localDependencies" в package.json. Теперь же хочу показать какие настройки поддерживает sp-load. Для того, чтобы конфигурировать sp-load, необходимо добавить свойство "_sp-load" в package.json. Ниже приведен пример package.json файла, в котором указаны все возможные настройки с комментариями о назначение каждой из них:
{ "name": "your-project", "version": "1.0.0", "main": "index.js", "dependencies": { "lodash": "^3.10.1", "sp-load": "^1.0.0" }, "devDependencies": { "gulp": "^3.9.0", "webpack": "^1.12.9" }, "_localDependencies": { "core": "./core/core", "some-module": "./deep/deep/deep/deep/deep/deep/deep/some-module" }, "_sp-load": { /* если значение true, имена модулей будут в виде camel case. например, вместо $['some-module'] будет $.someModule. дефолтное значение - true. */ "camelizing": false, /* эта настройка отвечает за переименование имен модулей. например, вместо $.lodash модуль будет доступен как $._ */ "renaming": { "lodash": "_", "gulp": "supergulp" }, /* если вы хотите заменить часть названия модулей, используйте эту настройку. ключи - регулярные выражения, значения - строки, на которые будет произведена замена. наиболее частый случай использования - gulp плагины, большинство из которых начинаются с префикса gulp-, например, gulp-concat, а вы хотите обращаться к нему как $.concat вместо $.gulpConcat. */ "replacing": { "/^gulp-/": "" } } }
Если же вы не хотите засорять package.json файл, то поместите настройки sp-load и список локальных модулей в файл _sp-load.json, который должен находиться в той же папке, где и package.json, то есть:
yourProject/ package.json _sp-load.json
Вот пример содержимого _sp-load.json файла:
{ "_localDependencies": { "core": "./core/core", "some-module": "./deep/deep/deep/deep/deep/deep/deep/some-module" }, "_sp-load": { "camelizing": false, "renaming": { "lodash": "_", "gulp": "supergulp" }, "replacing": { "/^gulp-/": "" } } }
И последнее, о чем еще не упомянул. Когда вы делаете $ = require(‘sp-load’);, объект $ содержит свойство "_spModulesList" в своем прототипе. Это свойство содержит объект, где ключи — имена модулей, а значения — абсолютный путь к файлу модуля. Вот пример содержимого этого объекта:
{ "lodash": "lodash", "sp-load": "sp-load", "gulp": "gulp", "webpack": "webpack", "core": "D://your-project//core//core.js", "some-module": "D://your-project//deep//deep//deep//deep//deep//deep//deep//some-module.js" }
Для чего это может пригодиться? Например, при использование System.js загрузчика.
Пожалуй, это всё. Перед тем, как опубликовать модуль на npmjs.com, протестировал его, но в реальном проекте ещё его не использовал, поэтому, если будут какие-либо ошибки — буду рад, если сообщите о них.
Ссылка на сам модуль: sp-load.
P.S.: Может, кто-нибудь подскажет, как удалить опубликованный модуль из npmjs.com? Нигде не нашел, как это сделать, а npm unpublish удаляет модуль, но при последующем npm publish приходится увеличивать версию модуля т.к. npm ругается, что текущая версия уже зарегистрирована.
ссылка на оригинал статьи http://habrahabr.ru/post/275011/
Добавить комментарий