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

138

В чем разница между использованием 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. Функциональной разницы особо не вижу ...

Maxedison
источник
1
как насчет кеширования браузера, мешает ли requirejs?
Мухаммад Умер
Я снова открываю это, потому что он просит разницы между двумя очень похожими вещами. На него можно ответить объективно, и я не вижу, где это связано с мнением.
RamenChef

Ответы:

43

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

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

  • какой-то # include / import / require
  • возможность загружать вложенные зависимости
  • простота использования для разработчика, но с поддержкой инструмента оптимизации, который помогает развертыванию
Сарфраз
источник
2
Я читал их, но теперь, когда я думаю об этом больше, я понимаю, что идея вложенных зависимостей не может быть достигнута простым написанием тегов <script>. Спасибо.
maxedison 06
37
«Простота использования для разработчика» не может быть дальше от истины. У него определенно есть крутая кривая обучения для вас и всех, кто придет работать в этом проекте.
Сахат Ялкабов
3
@TwilightPony Я не считаю себя таким уж умным, и мне было несложно получить requirejs. Это избавляет вас от необходимости беспокоиться о зависимостях и ускоряет страницу. Ваш код становится более встроенным в серверное программирование в том, как вы объявляете свои зависимости, что я лично считаю свежим и простым. Синтаксис был минимальным и закрытым по замыслу проекта, а затем устанавливает план для производства, чтобы легко комбинировать ваши скрипты. Кроме того, отладка похожа на статические объявления. Не уверен, что может быть проще. Гораздо сложнее, чем я.
Джейсон Себринг,
Я пытаюсь. Особенно с модулями, которые пытаются прикрепиться к глобальным объектам. (Модули React) ...
geilt 03
1
Комментарии на этой странице заставили меня почувствовать, что нужно убегать от требований, а не навстречу им. Особенно тот, что внизу, который ссылается на stevesouders.com/tests/require.php
Дэйв Кантер,
52

Какие преимущества предлагает 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, как показано выше. Хотя ваше веб-приложение может загружаться быстрее, вызов функций и функций на сайте будет происходить медленнее, так как это потребует ожидания загрузки ресурсов, прежде чем это действие может быть выполнено.

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

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

jmort253
источник
10

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

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

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

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

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

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

Я работаю в проекте с 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 над кодом для всех браузеров.

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

gman
источник