загружать скрипты асинхронно


125

Я использую несколько плагинов, пользовательских виджетов и некоторые другие библиотеки из JQuery. в результате у меня есть несколько файлов .js и .css. Мне нужно создать загрузчик для моего сайта, потому что он загружается некоторое время. было бы хорошо, если бы я мог отобразить загрузчик перед импортом всех:

<script type="text/javascript" src="js/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="js/myFunctions.js"></script>
<link type="text/css" href="css/main.css" rel="stylesheet" />
... 
....
 etc

Я нашел несколько руководств, которые позволяют мне асинхронно импортировать библиотеку JavaScript. например, я могу сделать что-то вроде:

  (function () {
        var s = document.createElement('script');
        s.type = 'text/javascript';
        s.async = true;
        s.src = 'js/jquery-ui-1.8.16.custom.min.js';
        var x = document.getElementsByTagName('script')[0];
        x.parentNode.insertBefore(s, x);
    })();

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

В любом случае

Вот о чем я подумал ... Я считаю, что в браузерах есть кеш, поэтому загрузка страницы в первый раз занимает много времени, а в следующий раз загрузка происходит быстро. поэтому я собираюсь заменить мою страницу index.html страницей, которая загружает все эти файлы асинхронно. когда ajax будет загружен, все эти файлы будут перенаправлены на страницу, которую я планирую использовать. при использовании этой страницы загрузка не займет много времени, поскольку файлы уже должны быть включены в кеш браузера. на моей индексной странице (страница, где файлы .js и .css загружаются асинхронно), я не забочусь об ошибках. Я просто покажу загрузчик и перенаправлю страницу, когда закончу ...

Это хорошая альтернатива? или я должен продолжать попытки реализовать асинхронные методы?


РЕДАКТИРОВАТЬ

способ загрузки всего асинхронного:

importScripts();

function importScripts()
{
    //import: jquery-ui-1.8.16.custom.min.js
    getContent("js/jquery-1.6.2.min.js",function (code) {
                var s = document.createElement('script');
                s.type = 'text/javascript';
                //s.async = true;
                s.innerHTML=code;
                var x = document.getElementsByTagName('script')[0];
                x.parentNode.insertBefore(s, x);
                setTimeout(insertNext1,1);
            });


    //import: jquery-ui-1.8.16.custom.min.js
    function insertNext1()
    {
        getContent("js/jquery-ui-1.8.16.custom.min.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext2,1);
                });
    }

    //import: jquery-ui-1.8.16.custom.css
    function insertNext2()
    {

        getContent("css/custom-theme/jquery-ui-1.8.16.custom.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext3,1);
                });
    }

    //import: main.css
    function insertNext3()
    {

        getContent("css/main.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext4,1);
                });
    }

    //import: jquery.imgpreload.min.js
    function insertNext4()
    {
        getContent("js/farinspace/jquery.imgpreload.min.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext5,1);
                });
    }


    //import: marquee.js
    function insertNext5()
    {
        getContent("js/marquee.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext6,1);
                });
    }


    //import: marquee.css
    function insertNext6()
    {

        getContent("css/marquee.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext,1);
                });
    }



    function insertNext()
    {
        setTimeout(pageReadyMan,10);        
    }
}


// get the content of url and pass that content to specified function
function getContent( url, callBackFunction )
{
     // attempt to create the XMLHttpRequest and make the request
     try
     {
        var asyncRequest; // variable to hold XMLHttpRequest object
        asyncRequest = new XMLHttpRequest(); // create request object

        // register event handler
        asyncRequest.onreadystatechange = function(){
            stateChange(asyncRequest, callBackFunction);
        } 
        asyncRequest.open( 'GET', url, true ); // prepare the request
        asyncRequest.send( null ); // send the request
     } // end try
     catch ( exception )
     {
        alert( 'Request failed.' );
     } // end catch
} // end function getContent

// call function whith content when ready
function stateChange(asyncRequest, callBackFunction)
{
     if ( asyncRequest.readyState == 4 && asyncRequest.status == 200 )
     {
           callBackFunction(asyncRequest.responseText);
     } // end if
} // end function stateChange

и что странно, так это то, что весь стиль работает плюс все функции javascript. хотя страница почему-то зависла ...

Ответы:


176

Пара решений для асинхронной загрузки:

//this function will work cross-browser for loading scripts asynchronously
function loadScript(src, callback)
{
  var s,
      r,
      t;
  r = false;
  s = document.createElement('script');
  s.type = 'text/javascript';
  s.src = src;
  s.onload = s.onreadystatechange = function() {
    //console.log( this.readyState ); //uncomment this line to see which ready states are called.
    if ( !r && (!this.readyState || this.readyState == 'complete') )
    {
      r = true;
      callback();
    }
  };
  t = document.getElementsByTagName('script')[0];
  t.parentNode.insertBefore(s, t);
}

Если у вас уже есть jQuery на странице, просто используйте:

$.getScript(url, successCallback)*

Кроме того, возможно, что ваши скрипты загружаются / выполняются до завершения загрузки документа, а это означает, что вам нужно подождать, document.readyпока события не будут привязаны к элементам.

Невозможно точно сказать, в чем заключается ваша проблема, не видя кода.

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

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

* загруженные скрипты $.getScript, скорее всего, не будут кэшироваться


Для тех, кто может использовать современные функции, такие как Promiseобъект, loadScriptфункция стала значительно проще:

function loadScript(src) {
    return new Promise(function (resolve, reject) {
        var s;
        s = document.createElement('script');
        s.src = src;
        s.onload = resolve;
        s.onerror = reject;
        document.head.appendChild(s);
    });
}

Имейте callbackв виду, что эта версия больше не принимает аргумент, поскольку возвращенное обещание будет обрабатывать обратный вызов. То, что раньше было loadScript(src, callback), теперь будет loadScript(src).then(callback).

Это дает дополнительный бонус в виде возможности обнаруживать и обрабатывать сбои, например, можно вызвать ...

loadScript(cdnSource)
    .catch(loadScript.bind(null, localSource))
    .then(successCallback, failureCallback);

... и он будет корректно обрабатывать отказы CDN.


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

1
Я изменил и добавил дочерний элемент к тегу head вместо основного ... ..var x = document.getElementsByTagName ('head') [0]; x.appendChild (ы);
Тоно Нам

1
@TonoNam, вы можете просто использовать document.head.appendChild, однако есть некоторые нишевые проблемы с добавлением скриптов в <head>; ничего, что могло бы помешать загрузке и выполнению скриптов.
zzzzBov

1
Мне пришлось использовать t.parentNode.insertBefore (в отличие от t.parent).
Фил Ланаса

1
@MuhammadUmer, если вы пишете дополнительный тег скрипта document.writeдо загрузки документа, скрипт будет выполняться синхронно как часть загрузки страницы и сам по себе не будет включать в себя какое-либо асинхронное поведение обратного вызова. Если вы создаете элемент сценария и добавляете его на страницу, у вас будет доступ для добавления прослушивателей событий для асинхронного запуска. tl; dr: это зависит от того, как вы загружаете скрипт с помощью JS.
zzzzBov

33

Новый атрибут HTML5 async должен помочь. 'defer' также поддерживается в большинстве браузеров, если вам небезразличен IE.

async - HTML

<script async src="siteScript.js" onload="myInit()"></script>

defer - HTML

<script defer src="siteScript.js" onload="myInit()"></script>

При анализе нового кода рекламного блока AdSense я заметил атрибут, и поиск привел меня сюда: http://davidwalsh.name/html5-async


Насколько я понимаю, после прочтения stackoverflow.com/questions/2774373/… asyn предназначен не для загрузки, а для определения того, когда запускать содержащийся javascript.
JackDev

9
Атрибут defer заставляет браузер откладывать выполнение скрипта до тех пор, пока документ не будет загружен и проанализирован и не будет готов к работе. Атрибут async заставляет браузер запускать сценарий как можно скорее, но не блокирует синтаксический анализ документа во время загрузки сценария
shuseel

1
"asyn не для загрузки, а для определения, когда запускать содержащийся javascript" Это не противоречит; Во всех случаях браузер начнет загружать скрипт как можно скорее. Но без него async или deferон будет блокировать рендеринг во время загрузки. Настройка asyncскажет ему не блокировать и запустить скрипт как можно скорее. Настройка deferскажет ему не блокировать, а подождать с запуском скрипта, пока страница не будет готова. Обычно запускать скрипты как можно скорее не проблема, если у них нет никаких зависимостей (например, jQuery). Если один сценарий зависит от другого, используйте, onLoadчтобы дождаться этого.
Stijn de Witt

@StijndeWitt Что делать, если не указаны ни async, ни defer?
Mohammed Shareef C

@MohammedShareefC Если вы не укажете ни то, ни другое, вы получите что-то очень похожее на поведение синхронизации; браузер блокирует дальнейшую обработку, пока загружает и запускает скрипт. Таким образом, поведение по умолчанию - синхронизация. Но поскольку это пагубно сказывается на производительности, браузеры стараются как можно больше его обойти. Они могут продолжить синтаксический анализ, загрузку и применение CSS и т. Д. Однако они не могут начать выполнение следующего фрагмента скрипта. Так что все будет заблокировано. Устанавливая async, вы сообщаете браузеру, что все в порядке, и убедитесь, что вы заставите его работать самостоятельно.
Stijn de Witt

8

Я загрузил скрипты асинхронно (эта функция есть в html 5), когда все скрипты, где они загрузились, я перенаправил страницу на index2.html, где index2.html использует те же библиотеки. Поскольку в браузерах есть кеш после перенаправления страницы на index2.html, index2.html загружается менее чем за секунду, потому что в нем есть все необходимое для загрузки страницы. На моей странице index.html я также загружаю изображения, которые планирую использовать, чтобы браузер поместил эти изображения в кеш. поэтому мой index.html выглядит так:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>Project Management</title>

    <!-- the purpose of this page is to load all the scripts on the browsers cache so that pages can load fast from now on -->

    <script type="text/javascript">

        function stylesheet(url) {
            var s = document.createElement('link');
            s.type = 'text/css';
            s.async = true;
            s.src = url;
            var x = document.getElementsByTagName('head')[0];
            x.appendChild(s);
        }

        function script(url) {
            var s = document.createElement('script');
            s.type = 'text/javascript';
            s.async = true;
            s.src = url;
            var x = document.getElementsByTagName('head')[0];
            x.appendChild(s);
        }

        //load scritps to the catche of browser
        (function () {            
                stylesheet('css/custom-theme/jquery-ui-1.8.16.custom.css');
                stylesheet('css/main.css');
                stylesheet('css/marquee.css');
                stylesheet('css/mainTable.css');

                script('js/jquery-ui-1.8.16.custom.min.js');
                script('js/jquery-1.6.2.min.js');
                script('js/myFunctions.js');
                script('js/farinspace/jquery.imgpreload.min.js');
                script('js/marquee.js');            
        })();

    </script>

    <script type="text/javascript">
       // once the page is loaded go to index2.html
        window.onload = function () {
            document.location = "index2.html";
        }
    </script>

</head>
<body>

<div id="cover" style="position:fixed; left:0px; top:0px; width:100%; height:100%; background-color:Black; z-index:100;">Loading</div>

<img src="images/home/background.png" />
<img src="images/home/3.png"/>
<img src="images/home/6.jpg"/>
<img src="images/home/4.png"/>
<img src="images/home/5.png"/>
<img src="images/home/8.jpg"/>
<img src="images/home/9.jpg"/>
<img src="images/logo.png"/>
<img src="images/logo.png"/>
<img src="images/theme/contentBorder.png"/>

</body>
</html>

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


1
@MohammedShareefC Это ирония. Специальная страница только для предварительной загрузки не кажется хорошей идеей с точки зрения SEO. Предварительная загрузка - это здорово, но для этого не нужно перенаправлять. Просто вставьте ссылки в скрытый div, и браузер позаботится об этом автоматически. Тогда не перенаправляйте, а просто визуализируйте фактическую страницу. ПРОЧИТАЙТЕ ЭТО : Пожалуйста, измените кодировку ISO-88591 на UTF-8 перед использованием любого из этого HTML, чтобы остальные из нас могли в ближайшее время выбраться из ада кодирования. Спасибо!
Stijn de Witt

Пожалуйста, посмотрите мой новый ответ: stackoverflow.com/questions/7718935/load-scripts-asynchronously/…
asmmahmud 03

8

Пример из гугла

<script type="text/javascript">
  (function() {
    var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
    po.src = 'https://apis.google.com/js/plusone.js?onload=onLoadCallback';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
  })();
</script>

8

Несколько примечаний:

  • s.async = trueне очень правильно для документа HTML5, правильно s.async = 'async'(на самом деле использование true правильно , спасибо amn, который указал на это в комментарии чуть ниже)
  • Использование тайм-аутов для управления порядком не очень хорошо и безопасно, к тому же вы значительно увеличиваете время загрузки, равное сумме всех тайм-аутов!

Поскольку недавно появилась причина загружать файлы асинхронно, но по порядку, я бы порекомендовал немного более функциональный способ по сравнению с вашим примером (удалите console.logдля производственного использования :)):

(function() {
    var prot = ("https:"===document.location.protocol?"https://":"http://");

    var scripts = [
        "path/to/first.js",
        "path/to/second.js",
        "path/to/third.js"
    ];

    function completed() { console.log('completed'); }  // FIXME: remove logs

    function checkStateAndCall(path, callback) {
        var _success = false;
        return function() {
            if (!_success && (!this.readyState || (this.readyState == 'complete'))) {
                _success = true;
                console.log(path, 'is ready'); // FIXME: remove logs
                callback();
            }
        };
    }

    function asyncLoadScripts(files) {
        function loadNext() { // chain element
            if (!files.length) completed();
            var path = files.shift();
            var scriptElm = document.createElement('script');
            scriptElm.type = 'text/javascript';
            scriptElm.async = true;
            scriptElm.src = prot+path;
            scriptElm.onload = scriptElm.onreadystatechange = \
                checkStateAndCall(path, loadNext); // load next file in chain when
                                                   // this one will be ready 
            var headElm = document.head || document.getElementsByTagName('head')[0];
            headElm.appendChild(scriptElm);
        }
        loadNext(); // start a chain
    }

    asyncLoadScripts(scripts);
})();

4
s.async = trueявляется правильным . Свойство asyncявно указано как логическое в рекомендации W3C HTML 5 по адресу w3.org/TR/html5/scripting-1.html#attr-script-async . Вы путаете async атрибут со asyncсвойством, предоставляемым объектами, реализующими HTMLScriptElementинтерфейс DOM. В самом деле, при манипулировании соответствующим атрибутом элемента с помощью чего-то вроде Element.setAttributeэтого следует использовать, element.setAttribute("async", "async")поскольку все атрибуты HTML - это прежде всего текст.
amn

«Поскольку недавно появилась причина загружать файлы асинхронно, но по порядку, я бы порекомендовал немного более функционально ориентированный способ по сравнению с вашим примером [...]». Я знаю, что асинхронный режим обычно «лучше», но на какую «недавнюю» причину вы ссылаетесь?
BluE

Я изменил 'if (! Files.length) completed ();' добавление 'return': 'if (! files.length) {completed (); return;} '
Fil

4

Благодаря HTML5 теперь вы можете объявить скрипты, которые хотите загружать асинхронно, добавив в тег «async»:

<script async>...</script>

Примечание. Атрибут async предназначен только для внешних сценариев (и должен использоваться только при наличии атрибута src).

Примечание. Существует несколько способов выполнения внешнего сценария:

  • Если присутствует асинхронность: сценарий выполняется асинхронно с остальной частью страницы (сценарий будет выполняться, пока страница продолжает синтаксический анализ)
  • Если async отсутствует, а defer присутствует: сценарий выполняется, когда страница завершает синтаксический анализ.
  • Если ни async, ни defer не присутствует: сценарий извлекается и выполняется немедленно, прежде чем браузер продолжит синтаксический анализ страницы.

См. Это: http://www.w3schools.com/tags/att_script_async.asp


2

Я бы завершил ответ zzzzBov проверкой наличия обратного вызова и разрешил передачу аргументов:

    function loadScript(src, callback, args) {
      var s, r, t;
      r = false;
      s = document.createElement('script');
      s.type = 'text/javascript';
      s.src = src;
      if (typeof(callback) === 'function') {
        s.onload = s.onreadystatechange = function() {
          if (!r && (!this.readyState || this.readyState === 'complete')) {
            r = true;
            callback.apply(args);
          }
        };
      };
      t = document.getElementsByTagName('script')[0];
      t.parent.insertBefore(s, t);
    }

2

Вот отличное современное решение для асинхронной загрузки скрипта, хотя оно обращается только к js-скрипту с async false .

На www.html5rocks.com есть отличная статья - Погрузитесь в мутные воды загрузки скриптов .

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

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

Учитывая, что у вас есть четыре названных скрипта, script1.js, script2.js, script3.js, script4.jsвы можете сделать это, применив async = false :

[
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});

Теперь Spec говорит : «Загрузите вместе, выполните по порядку, как только все загрузятся».

Firefox <3.6, Opera говорит: я понятия не имею, что это за «асинхронность», но так получилось, что я выполняю сценарии, добавленные через JS, в том порядке, в котором они добавляются.

Safari 5.0 говорит: я понимаю «async», но не понимаю, как устанавливать для него значение «false» с помощью JS. Я выполню ваши сценарии, как только они появятся, в любом порядке.

IE <10 говорит: « Нет представления об« асинхронности », но есть обходной путь с использованием« onreadystatechange ».

Все остальное говорит: я твой друг, мы сделаем это по книге.

Теперь полный код с обходным решением IE <10:

var scripts = [
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];

// Watch scripts load in IE
function stateChange() {
  // Execute as many scripts in order as we can
  var pendingScript;
  while (pendingScripts[0] && ( pendingScripts[0].readyState == 'loaded' || pendingScripts[0].readyState == 'complete' ) ) {
    pendingScript = pendingScripts.shift();
    // avoid future loading events from this script (eg, if src changes)
    pendingScript.onreadystatechange = null;
    // can't just appendChild, old IE bug if element isn't closed
    firstScript.parentNode.insertBefore(pendingScript, firstScript);
  }
}

// loop through our script urls
while (src = scripts.shift()) {
  if ('async' in firstScript) { // modern browsers
    script = document.createElement('script');
    script.async = false;
    script.src = src;
    document.head.appendChild(script);
  }
  else if (firstScript.readyState) { // IE<10
    // create a script and add it to our todo pile
    script = document.createElement('script');
    pendingScripts.push(script);
    // listen for state changes
    script.onreadystatechange = stateChange;
    // must set src AFTER adding onreadystatechange listener
    // else we’ll miss the loaded event for cached scripts
    script.src = src;
  }
  else { // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  }
}

1

Одна из причин, по которой ваши скрипты могут загружаться так медленно, заключается в том, что вы запускали все свои скрипты во время загрузки страницы, например:

callMyFunctions();

вместо того:

$(window).load(function() {
      callMyFunctions();
});

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

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


1

Я предлагаю вам взглянуть на Modernizr . Это небольшая легкая библиотека, в которую вы можете асинхронно загружать свой javascript с функциями, которые позволяют вам проверять, загружен ли файл, и выполнять сценарий в другом указанном вами файле.

Вот пример загрузки jquery:

Modernizr.load([
  {
    load: '//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js',
    complete: function () {
      if ( !window.jQuery ) {
            Modernizr.load('js/libs/jquery-1.6.1.min.js');
      }
    }
  },
  {
    // This will wait for the fallback to load and
    // execute if it needs to.
    load: 'needs-jQuery.js'
  }
]);

1

Я написал небольшой пост, чтобы помочь с этим, вы можете прочитать больше здесь https://timber.io/snippets/asynchronously-load-a-script-in-the-browser-with-javascript/ , но я прикрепил вспомогательный класс ниже. Он будет автоматически ждать загрузки скрипта и, как только это произойдет, вернуть указанный атрибут окна.

export default class ScriptLoader {
  constructor (options) {
    const { src, global, protocol = document.location.protocol } = options
    this.src = src
    this.global = global
    this.protocol = protocol
    this.isLoaded = false
  }

  loadScript () {
    return new Promise((resolve, reject) => {
      // Create script element and set attributes
      const script = document.createElement('script')
      script.type = 'text/javascript'
      script.async = true
      script.src = `${this.protocol}//${this.src}`

      // Append the script to the DOM
      const el = document.getElementsByTagName('script')[0]
      el.parentNode.insertBefore(script, el)

      // Resolve the promise once the script is loaded
      script.addEventListener('load', () => {
        this.isLoaded = true
        resolve(script)
      })

      // Catch any errors while loading the script
      script.addEventListener('error', () => {
        reject(new Error(`${this.src} failed to load.`))
      })
    })
  }

  load () {
    return new Promise(async (resolve, reject) => {
      if (!this.isLoaded) {
        try {
          await this.loadScript()
          resolve(window[this.global])
        } catch (e) {
          reject(e)
        }
      } else {
        resolve(window[this.global])
      }
    })
  }
}

Использование такое:

const loader = new Loader({
    src: 'cdn.segment.com/analytics.js',
    global: 'Segment',
})

// scriptToLoad will now be a reference to `window.Segment`
const scriptToLoad = await loader.load()

1

Эта статья в вики может показаться вам интересной: http://ajaxpatterns.org/On-Demand_Javascript

В нем объясняется, как и когда использовать такую ​​технику.


К сожалению, ссылка не работает. :(
Эрик Систранд

@Eric, потому что это древний метод динамической загрузки кода: D
tereško

0

Что ж, x.parentNodeвозвращает элемент HEAD, поэтому вы вставляете скрипт непосредственно перед тегом head. Может в этом проблема.

Попробуйте x.parentNode.appendChild()вместо этого.



0

Вы можете использовать LABJS или RequreJS

Загрузчики скриптов, как LABJS, например , RequireJSулучшат скорость и качество вашего кода.


0

Вы рассматривали возможность использования Fetch Injection? Я развернул библиотеку с открытым исходным кодом под названием fetch-inject для обработки подобных случаев. Вот как может выглядеть ваш загрузчик с использованием библиотеки:

fetcInject([
  'js/jquery-1.6.2.min.js',
  'js/marquee.js',
  'css/marquee.css',
  'css/custom-theme/jquery-ui-1.8.16.custom.css',
  'css/main.css'
]).then(() => {
  'js/jquery-ui-1.8.16.custom.min.js',
  'js/farinspace/jquery.imgpreload.min.js'
})

Для обратной совместимости используйте функцию обнаружения и возврата к XHR Injection или Script DOM Elements, или просто вставьте теги в страницу, используя document.write.


0

Вот мое собственное решение для устранения JavaScript, блокирующего рендеринг:

// put all your JS files here, in correct order
const libs = {
  "jquery": "https://code.jquery.com/jquery-2.1.4.min.js",
  "bxSlider": "https://cdnjs.cloudflare.com/ajax/libs/bxslider/4.2.5/jquery.bxslider.min.js",
  "angular": "https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.2/angular.min.js",
  "ngAnimate": "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular-animate.min.js"
}
const loadedLibs = {}
let counter = 0

const loadAsync = function(lib) {
  var http = new XMLHttpRequest()
  http.open("GET", libs[lib], true)
  http.onload = () => {
    loadedLibs[lib] = http.responseText
    if (++counter == Object.keys(libs).length) startScripts()
  }
  http.send()
}

const startScripts = function() {
  for (var lib in libs) eval(loadedLibs[lib])
  console.log("allLoaded")
}

for (var lib in libs) loadAsync(lib)

Короче говоря, он загружает все ваши скрипты асинхронно, а затем выполняет их последовательно.

Репозиторий Github : https://github.com/mudroljub/js-async-loader


1
Не работаетNo 'Access-Control-Allow-Origin' header is present on the requested resource
Абрам


0

Вот небольшая функция ES6, если кто-то хочет использовать ее, например, в React

import {uniqueId} from 'lodash' // optional
/**
 * @param {String} file The path of the file you want to load.
 * @param {Function} callback (optional) The function to call when the script loads.
 * @param {String} id (optional) The unique id of the file you want to load.
 */
export const loadAsyncScript = (file, callback, id) => {
  const d = document
  if (!id) { id = uniqueId('async_script') } // optional
  if (!d.getElementById(id)) {
    const tag = 'script'
    let newScript = d.createElement(tag)
    let firstScript = d.getElementsByTagName(tag)[0]
    newScript.id = id
    newScript.async = true
    newScript.src = file
    if (callback) {
      // IE support
      newScript.onreadystatechange = () => {
        if (newScript.readyState === 'loaded' || newScript.readyState === 'complete') {
          newScript.onreadystatechange = null
          callback(file)
        }
      }
      // Other (non-IE) browsers support
      newScript.onload = () => {
        callback(file)
      }
    }
    firstScript.parentNode.insertBefore(newScript, firstScript)
  } else {
    console.error(`The script with id ${id} is already loaded`)
  }
}


-3

Я бы посоветовал сначала изучить возможность минимизации файлов и посмотреть, даст ли это вам достаточно большой прирост скорости. Если ваш хост работает медленно, можно попробовать разместить этот статический контент в CDN.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.