В чем разница между Require.js и простым созданием элемента ‹script› в DOM?

В чем разница между использованием Require.JS и простым созданием элемента <script> в DOM?

Насколько я понимаю Require.JS, он предлагает возможность загрузки зависимостей, но нельзя ли это сделать просто, создав элемент <script>, загружающий необходимый внешний JS-файл?

Например, предположим, что у меня есть функция doStuff(), для которой требуется функция needMe(). doStuff() находится во внешнем файле do_stuff.js, а needMe() — во внешнем файле need_me.js.

Делая это способом Require.JS:

define(['need_me'],function(){
    function doStuff(){
        //do some stuff
        needMe();
        //do some more stuff
    }
});

Это можно сделать, просто создав элемент скрипта:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';
    document.getElementsByTagName('head')[0].appendChild(scriptElement);

    //do some stuff
    needMe();
    //do some more stuff
}

Оба они работают. Однако вторая версия не требует загрузки всей библиотеки Require.js. Я не вижу никакой функциональной разницы...


person maxedison    schedule 06.02.2011    source источник
comment
как насчет кеширования браузера, не мешает ли ему requirejs?   -  person Muhammad Umer    schedule 28.01.2015
comment
Я снова открываю это, потому что он просит о разнице между двумя очень похожими вещами. На него можно ответить объективно, и я не понимаю, при чем тут мнение.   -  person RamenChef    schedule 14.01.2017


Ответы (4)


Вот хорошая статья на ajaxian.com о том, зачем ее использовать:

RequireJS: асинхронная загрузка JavaScript

  • какой-то #include/import/require
  • возможность загрузки вложенных зависимостей
  • простота использования для разработчика, но затем поддерживается инструментом оптимизации, который помогает развертыванию
person Sarfraz    schedule 06.02.2011
comment
Я читал их, но теперь, когда я думаю об этом больше, я понимаю, что идея вложенных зависимостей не может быть достигнута простым написанием тегов ‹script›. Спасибо. - person maxedison; 06.02.2011
comment
простота использования для разработчика не может быть дальше от истины. У него определенно есть крутая кривая обучения для вас и всех, кто придет работать в этом проекте. - person Sahat Yalkabov; 30.09.2013
comment
@TwilightPony Я считаю себя не таким умным, и мне было несложно получить requirejs. Это избавляет вас от необходимости беспокоиться о зависимостях и ускоряет страницу. Ваш код становится более совместимым с программированием на стороне сервера в том, как вы объявляете свои зависимости, что я лично нахожу освежающим и простым. Синтаксис был минимальным и замкнутым по дизайну, а затем задавал план для производства, чтобы легко комбинировать ваши сценарии. Кроме того, отладка аналогична статическим объявлениям. Не уверен, что это проще. Гораздо сложнее в другую сторону, как я сделал в другую сторону. - person King Friday; 03.08.2014
comment
Я пытаюсь. Особенно с модулями, которые пытаются прикрепиться к глобальным объектам. (модули реакции)... - person geilt; 03.03.2015
comment
Комментарии на этой странице на самом деле оставили у меня ощущение, что нужно бежать от требования, а не к нему. Особенно тот, который внизу ссылается на stevesouders.com/tests/require.php. - person Dave Kanter; 30.09.2015
comment
этот пример stevesounders упускает суть. Если скрипт 2 зависит от символов из скрипта 1, то скрипт 2 должен требовать скрипт 1. В этом весь смысл объявления зависимостей. Если вы не объявите их, конечно, это не будет правильно. В идеале вы никогда не должны волшебным образом передавать символы между модулями. Если вы волшебным образом передаете символы между модулями и помещаете что-то в window/global, вам нужно вручную объявить свои зависимости. - person gman; 06.01.2017

Какие преимущества предлагает Require.JS по сравнению с простым созданием элемента в DOM?

В вашем примере вы создаете тег скрипта асинхронно, что означает, что ваша функция needMe() будет вызываться до завершения загрузки файла need_me.js. Это приводит к неперехваченным исключениям, когда ваша функция не определена.

Вместо этого, чтобы то, что вы предлагаете, действительно работало, вам нужно сделать что-то вроде этого:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';

    scriptElement.addEventListener("load", 
        function() { 
            console.log("script loaded - now it's safe to use it!");

            // do some stuff
            needMe();
            //do some more stuff

        }, false);

    document.getElementsByTagName('head')[0].appendChild(scriptElement);

}

Возможно, лучше или не лучше использовать менеджер пакетов, такой как RequireJS, или использовать стратегию чистого JavaScript, как показано выше. Хотя ваше веб-приложение может загружаться быстрее, вызов функций и функций на сайте будет происходить медленнее, поскольку для выполнения этого действия потребуется дождаться загрузки ресурсов.

Если веб-приложение построено как одностраничное, учтите, что пользователи не будут часто перезагружать страницу. В этих случаях предварительная загрузка всего поможет ускорить работу при фактическом использовании приложения. В этих случаях вы правы, можно просто загрузить все ресурсы, просто включив теги script в заголовок или тело страницы.

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

person jmort253    schedule 01.07.2013

Некоторые другие очень важные причины, по которым использование RequireJS имеет смысл:

  1. Управление собственными зависимостями быстро разваливается для крупных проектов.
  2. У вас может быть столько маленьких файлов, сколько вы хотите, и вам не нужно беспокоиться об отслеживании зависимостей или порядке загрузки.
  3. RequireJS позволяет написать целое модульное приложение, не затрагивая объект окна.

Взято из комментариев rmurfey здесь, в этом Gist.

Слои абстракции могут быть кошмаром для изучения и адаптации, но когда они служат цели и делают это хорошо, это просто имеет смысл.

person girls_can_code_too    schedule 20.06.2014
comment
Вам все равно придется управлять всеми этими операторами require и define, файлами конфигурации, коллизиями с другими системами и библиотеками, в которых не реализована спецификация AMD, и т. д. Я пытался использовать Require.JS в проекте node-webkit и Require.JS боролся со мной на каждом шагу... Сравните это с простым заказом скриптов определенным образом... Конечно, вы получаете ленивую загрузку с Require.JS, поэтому я попытался заставить его работать. :) - person jmort253; 20.06.2014
comment
Я полностью согласен с @jmort253, поначалу это было непросто, но теперь мне это очень нравится. Все три пункта верны! И AMDify библиотека не должна быть такой сложной... или использовать прокладку. - person Legends; 06.01.2017

Вот более конкретный пример.

Я работаю над проектом с 60 файлами. У нас есть 2 разных режима работы.

  1. Загрузите составную версию, 1 большой файл. (Производство)

  2. Загрузить все 60 файлов (разработка)

Мы используем загрузчик, поэтому у нас есть только один скрипт на веб-странице.

<script src="loader.js"></script>

По умолчанию используется режим № 1 (загрузка одного большого объединенного файла). Для запуска в режиме #2 (отдельные файлы) мы устанавливаем какой-то флаг. Это может быть что угодно. Ключ в строке запроса. В этом примере мы просто делаем это

<script>useDebugVersion = true;</script>
<script src="loader.js"></script>

loader.js выглядит примерно так

if (useDebugVersion) {
   injectScript("app.js");
   injectScript("somelib.js");
   injectScript("someotherlib.js");
   injectScript("anotherlib.js");
   ... repeat for 60 files ...
} else {
   injectScript("large-concatinated.js");
}

Сценарий сборки - это просто файл .sh, который выглядит так

cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.js

и т.д...

Если добавляется новый файл, мы, вероятно, будем использовать режим № 2, так как мы занимаемся разработкой, нам нужно добавить строку injectScript("somenewfile.js") в loader.js.

Затем позже для производства мы также должны добавить somenewfile.js в наш скрипт сборки. Шаг, который мы часто забываем, а затем получаем сообщения об ошибках.

При переходе на AMD нам не нужно редактировать 2 файла. Проблема синхронизации loader.js и скрипта сборки исчезает. Используя r.js или webpack, он может просто прочитать код для сборки large-concantinated.js.

Он также может иметь дело с зависимостями, например, у нас было загружено 2 файла lib1.js и lib2.js следующим образом.

injectScript("lib1.js");
injectScript("lib2.js");

lib2 нужна lib1. Внутри него есть код, который делает что-то вроде

lib1Api.installPlugin(...);

Но поскольку внедренные скрипты загружаются асинхронно, нет гарантии, что они загрузятся в правильном порядке. Эти 2 скрипта не являются скриптами AMD, но с помощью require.js мы можем указать их зависимости.

require.config({
    paths: {
        lib1: './path/to/lib1',
        lib2: './path/to/lib2',
    },
    shim: {
        lib1: {
            "exports": 'lib1Api',
        },
        lib2: {
            "deps": ["lib1"],
        },
    }
});

Я наш модуль, который использует lib1, мы делаем это

define(['lib1'], function(lib1Api) {
   lib1Api.doSomething(...);
});

Теперь require.js будет внедрять скрипты для нас и не будет внедрять lib2 до тех пор, пока не будет загружена lib1, поскольку мы сказали, что lib2 зависит от lib1. Он также не запустит наш модуль, использующий lib1, пока не загрузятся и lib2, и lib1.

Это делает разработку приятной (без этапа сборки, не нужно беспокоиться о порядке загрузки) и делает производство приятным (нет необходимости обновлять скрипт сборки для каждого добавленного скрипта).

В качестве дополнительного бонуса мы можем использовать плагин babel из webpack, чтобы запускать babel поверх кода для старых браузеров, и опять же нам не нужно поддерживать этот скрипт сборки.

Обратите внимание, что если бы Chrome (наш любимый браузер) начал поддерживать import по-настоящему, мы бы, вероятно, переключились на него для разработки, но на самом деле это ничего не изменило бы. Мы по-прежнему могли бы использовать webpack для создания составного файла, и мы могли бы использовать его для запуска babel над кодом для всех браузеров.

Все это достигается за счет отказа от тегов script и использования AMD.

person gman    schedule 06.01.2017