Стиль узла требуется для JavaScript в браузере? - PullRequest
71 голосов
/ 07 августа 2011

Существуют ли библиотеки для JavaScript в браузере, обеспечивающие такую ​​же гибкость / модульность / простоту использования, как у Node's require?

Чтобы предоставить более подробную информацию: причина require настолько хороша, что она:

  1. Позволяет динамически загружать код из других мест (на мой взгляд, стилистически лучше, чем связывать весь ваш код в HTML)
  2. Предоставляет согласованный интерфейс для построения модулей
  3. Для модулей легко зависеть от других модулей (так что я мог бы написать, например, API, который требует jQuery, чтобы я мог использовать jQuery.ajax()
  4. Загруженный javascript имеет значение scoped , что означает, что я могу загрузить с помощью var dsp = require("dsp.js");, и я смогу получить доступ к dsp.FFT, что не будет мешать моему локальному var FFT

Мне еще предстоит найти библиотеку, которая делает это эффективно. Обходные пути, которые я обычно использую:

  • coffeescript-concat - достаточно просто потребовать другие js, но вы должны скомпилировать его, что означает, что он менее хорош для быстрой разработки (например, для создания тестируемых API)

  • RequireJS - Это популярно, просто и решает 1-3, но недостаток области видимости действительно нарушает условия сделки (я думаю, что head.js похож в том смысле, что ему не хватает области видимости, хотя у меня никогда не было возможности его использовать. Точно так же LABjs может загружать и .wait() действительно смягчает проблемы с зависимостями, но все равно не определяет область видимости)

Насколько я могу судить, кажется, что существует много решений для динамической и / или асинхронной загрузки javascript, но они, как правило, имеют те же проблемы с областью видимости, что и просто загрузка js из HTML. Больше всего на свете мне нужен способ загрузки javascript, который вообще не загрязняет глобальное пространство имен, но все же позволяет загружать и использовать библиотеки (так же, как это требуется для узла).

РЕДАКТИРОВАТЬ (МОЙ ОТВЕТ): С момента написания этого я широко использовал RequireJS (который теперь имеет гораздо более четкую документацию). RequireJS действительно был правильным выбором на мой взгляд. Я хотел бы уточнить, как система работает для людей, которые так же запутались, как и я:

Вы можете использовать require в повседневной разработке. Модуль может быть чем угодно, возвращаемым функцией (обычно объектом или функцией) и имеет область видимости в качестве параметра. Вы также можете скомпилировать проект в один файл для развертывания, используя r.js (на практике это почти всегда быстрее, даже если require может загружать сценарии параллельно).

Основное отличие между RequireJS и стилем узла, например, в виде browserify (классный проект, предложенный tjameson), заключается в том, как спроектированы и необходимы модули:

  • RequireJS использует AMD (определение модуля Async). В AMD require принимает список модулей (файлы javascript) для загрузки и функцию обратного вызова. Когда он загрузил каждый из модулей, он вызывает обратный вызов с каждым модулем в качестве параметра обратного вызова. Таким образом, он действительно асинхронный и поэтому хорошо подходит для Интернета.
  • Узел использует CommonJS. В CommonJS require - это блокирующий вызов, который загружает модуль и возвращает его как объект. Это нормально работает для Node, поскольку файлы считываются из файловой системы, что достаточно быстро, но плохо работает в Интернете, поскольку синхронная загрузка файлов может занять гораздо больше времени.

На практике многие разработчики использовали Node (и, следовательно, CommonJS) еще до того, как увидели AMD. Кроме того, многие библиотеки / модули написаны для CommonJS (путем добавления объектов в объект exports), а не для AMD (путем возврата модуля из функции define). Таким образом, многие разработчики Node, ставшие веб-пользователями, хотят использовать библиотеки CommonJS в Интернете. Это возможно, поскольку загрузка из тега <script> блокируется. Решения, такие как browserify, принимают модули CommonJS (Node) и упаковывают их, чтобы вы могли включать их в теги сценария.

Поэтому, если вы разрабатываете свой собственный многофайловый проект для Интернета, я настоятельно рекомендую RequireJS, поскольку это действительно модульная система для Интернета (хотя, если честно, я считаю AMD гораздо более естественной, чем CommonJS). В последнее время это различие стало менее важным, поскольку RequireJS теперь позволяет вам по существу использовать синтаксис CommonJS. Кроме того, RequireJS можно использовать для загрузки модулей AMD в Node (хотя я предпочитаю node-amd-loader ).

Ответы [ 7 ]

15 голосов
/ 07 августа 2011

Выезд ender .Это делает многое из этого.

Кроме того, browserify довольно хорошо.Я использовал require-kiss , и это работает.Возможно, есть и другие.

Я не уверен насчет RequireJS.Это просто не то же самое, что и узел.У вас могут возникнуть проблемы с загрузкой из других мест, но это может сработать.Пока есть метод обеспечения или что-то, что можно вызвать.

TL; DR - я бы порекомендовал browserify или require-kiss.

Обновить:

require-kiss теперь мертв, и автор удалил его.С тех пор я использую RequireJS без проблем.Автор require-kiss написал pakmanager и pakman .Полное раскрытие, я работаю с разработчиком.

Лично мне больше нравится RequireJS.Гораздо проще отлаживать (вы можете иметь отдельные файлы в разработке и один развернутый файл в работе), и он построен на твердом «стандарте».

13 голосов
/ 21 июля 2012

Я написал небольшой скрипт, который позволяет асинхронную и синхронную загрузку файлов Javascript, которые могут быть здесь полезны.Он не имеет зависимостей и совместим с Node.js & CommonJS.Вы даже можете объединить несколько модулей в один файл, чтобы уменьшить количество HTTP-запросов на производственных серверах.Использование довольно просто:

<script type="text/javascript" src="require.js"></script>
<script type="text/javascript">
    var ModuleA = require('modulea') // Sync loading of a script in the module directory
    ModuleA.funcA();

    require('./path/moduleb.js', callbackB); // Async loading of a script anywhere else
    function callbackB(ModuleB) {
        ModuleB.funcB();
    }
</script>

Более подробную информацию и код можно найти в моем блоге: http://pixelsvsbytes.com/2013/02/js-require-for-browsers-better-faster-stronger/ Код также находится на GitHub: https://github.com/letorbi/tarp.require

7 голосов
/ 28 октября 2013

Вариант Илья Харламов, отличный ответ , с небольшим кодом, чтобы сделать его приятным с инструментами разработчика Chrome.

//
///- REQUIRE FN
// equivalent to require from node.js
function require(url){
    if (url.toLowerCase().substr(-3)!=='.js') url+='.js'; // to allow loading without js suffix;
    if (!require.cache) require.cache=[]; //init cache
    var exports=require.cache[url]; //get from cache
    if (!exports) { //not cached
            try {
                exports={};
                var X=new XMLHttpRequest();
                X.open("GET", url, 0); // sync
                X.send();
                if (X.status && X.status !== 200)  throw new Error(X.statusText);
                var source = X.responseText;
                // fix (if saved form for Chrome Dev Tools)
                if (source.substr(0,10)==="(function("){ 
                    var moduleStart = source.indexOf('{');
                    var moduleEnd = source.lastIndexOf('})');
                    var CDTcomment = source.indexOf('//@ ');
                    if (CDTcomment>-1 && CDTcomment<moduleStart+6) moduleStart = source.indexOf('\n',CDTcomment);
                    source = source.slice(moduleStart+1,moduleEnd-1); 
                } 
                // fix, add comment to show source on Chrome Dev Tools
                source="//@ sourceURL="+window.location.origin+url+"\n" + source;
                //------
                var module = { id: url, uri: url, exports:exports }; //according to node.js modules 
                var anonFn = new Function("require", "exports", "module", source); //create a Fn with module code, and 3 params: require, exports & module
                anonFn(require, exports, module); // call the Fn, Execute the module
                require.cache[url]  = exports = module.exports; //cache obj exported by module
            } catch (err) {
                throw new Error("Error loading module "+url+": "+err);
            }
    }
    return exports; //require returns object exported by module
}
///- END REQUIRE FN
5 голосов
/ 05 августа 2013
(function () {
    // c is cache, the rest are the constants
    var c = {},s="status",t="Text",e="exports",E="Error",r="require",m="module",S=" ",w=window;
    w[r]=function R(url) {
        url+=/.js$/i.test(url) ? "" : ".js";// to allow loading without js suffix;
        var X=new XMLHttpRequest(),module = { id: url, uri: url }; //according to the modules 1.1 standard
        if (!c[url])
            try {
                X.open("GET", url, 0); // sync
                X.send();
                if (X[s] && X[s] != 200) 
                    throw X[s+t];
                Function(r, e, m, X['response'+t])(R, c[url]={}, module); // Execute the module
                module[e] && (c[url]=module[e]);
            } catch (x) {
                throw w[E](E+" in "+r+": Can't load "+m+S+url+":"+S+x);
            }
        return c[url];
    }
})();

Лучше не использовать в производстве из-за блокировки. (В node.js require () является блокирующим вызовом).

1 голос
/ 11 января 2015

Require-stub - обеспечивает совместимость с узлами require в браузере, разрешает как модули, так и относительные пути. Использует технику, аналогичную TKRequire (XMLHttpRequest). Полученный код полностью доступен для просмотра, в этом случае require-stub может служить заменой для watchify.

1 голос
/ 26 июля 2012

Webmake связывает модули в стиле Node с браузером, попробуйте.

0 голосов
/ 26 октября 2014

Вот расширение фантастического ответа Люсио М. Тато, который допускает рекурсивную загрузку модулей с относительными путями.

Вот проект github для размещения решения и примеркак его использовать:

https://github.com/trausti/TKRequire.js

Чтобы использовать TKRequire.js, включите в заголовок следующую строку

...