Я работаю над библиотекой JavaScript для обработки JSON/XML. Моя библиотека работает как в браузере, так и в Node.js (с модулями xmldom
и xmlhttprequest
).
Один из пользователей недавно попросил поддержку RequireJS. Я взглянул на RequireJS/AMD и думаю, что это хороший подход, поэтому я хотел бы предоставить его.
Однако я хотел бы сохранить переносимость: моя библиотека должна работать в браузерах (с RequireJS и без него), а также в Node.js. И в среде браузера я не завишу от xmldom
или xmlhttprequest
, поскольку эти вещи предоставляются самим браузером.
Мой вопрос: как мне реализовать мою библиотеку, чтобы она работала в браузерах так же, как и в Node.js без RequireJS?
Немного истории и мое текущее решение
Я изначально написал свою библиотеку для браузеров. Поэтому он просто создал объект глобальной области видимости и поместил в него все:
var Jsonix = { ... };
Позже пользователи обратились за поддержкой Node.js. Поэтому я добавил:
if(typeof require === 'function'){
module.exports.Jsonix = Jsonix;
}
Мне также пришлось импортировать несколько модулей, упомянутых выше. Я сделал это условно, в зависимости от того, доступна функция require
или нет:
if (typeof require === 'function')
{
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
return new XMLHttpRequest();
}
Вот такая история с RequireJS. Если присутствует RequireJS, то также присутствует функция require
. Но загрузка модуля работает по-другому, я должен использовать функцию define
и т. д. Я также не могу просто require
делать вещи, так как require
имеет асинхронный API в RequireJS. Более того, если моя библиотека загружается через RequireJS, она вроде бы обрабатывает исходный код и обнаруживает require('something')
, даже если я делаю это условно, например
if (typeof require === 'function' && typeof require.specified !== 'function) ...
RequireJS по-прежнему обнаруживает require('xmlhttprequest')
и пытается загрузить соответствующий файл JS.
В настоящее время я прихожу к следующему решению.
// Module factory function, AMD style
var _jsonix = function(_jsonix_xmldom, _jsonix_xmlhttprequest, _jsonix_fs)
{
// Complete Jsonix script is included below
var Jsonix = { ... };
// Complete Jsonix script is included above
return { Jsonix: Jsonix };
};
// If require function exists ...
if (typeof require === 'function') {
// ... but define function does not exists, assume we're in the Node.js environment
// In this case, load the define function via amdefine
if (typeof define !== 'function') {
var define = require('amdefine')(module);
define(["xmldom", "xmlhttprequest", "fs"], _jsonix);
}
else {
// Otherwise assume we're in the RequireJS environment
define([], _jsonix);
}
}
// Since require function does not exists,
// assume we're neither in Node.js nor in RequireJS environment
// This is probably a browser environment
else
{
// Call the module factory directly
var Jsonix = _jsonix();
}
И вот как я сейчас проверяю зависимости:
if (typeof _jsonix_xmlhttprequest !== 'undefined')
{
var XMLHttpRequest = _jsonix_xmlhttprequest.XMLHttpRequest;
return new XMLHttpRequest();
}
Если у меня есть require
, но нет define
, я предполагаю, что это среда Node.js. Я использую amdefine
для определения модуля и передачи необходимых зависимостей.
Если у меня есть require
и define
, я предполагаю, что это среда RequireJS, поэтому я просто использую функцию define
. В настоящее время я также предполагаю, что это среда браузера, поэтому такие зависимости, как xmldom
и xmlhttprequest
, недоступны и не требуют их. (Вероятно, это неправильно.)
Если у меня нет функции require
, я предполагаю, что это среда браузера без поддержки RequireJS/AMD, поэтому я напрямую вызываю фабрику модулей _jsonix
и экспортирую результат как глобальный объект.
Итак, это мой подход на данный момент. Мне это кажется немного неудобным, и, как новичок в RequireJS/AMD, я ищу совета. Это правильный подход? Есть ли лучшие способы решения проблемы? Буду признателен за вашу помощь.