завантажувати сценарії асинхронно


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 файл завантажуються асинхронно) Мені не важливо помилок. Я просто показуватиму завантажувач і перенаправлятиму сторінку, коли закінчите ...

Чи є ця ідея гарною альтернативою? чи слід намагатись реалізувати асинхронно методи?


EDIT

як я завантажую все async, як:

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 (s);
Тоно Нам

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

1
Мені довелося використовувати t.parentNode.insertBefore (на відміну від t.parent).
Phil LaNasa

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

33

Новий атрибут "async" HTML5 повинен зробити свою справу. "відстрочка" також підтримується в більшості браузерів, якщо ви дбаєте про IE.

async - HTML

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

відстрочка - HTML

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

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


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

9
Атрибут відстрочки змушує браузер відкладати виконання сценарію до того моменту, коли документ буде завантажений і розібраний і не буде готовий до маніпулювання. Атрибут async змушує браузер запустити сценарій якнайшвидше, але не блокувати розбір документа під час завантаження сценарію
shuseel

1
"Асин не для завантаження, а для визначення часу запуску JavaScript, що міститься" Це не суперечливо; У всіх випадках браузер почне завантажувати сценарій як можна швидше. Але без цього async або deferблокує візуалізацію, поки вона завантажується. Налаштування asyncдозволить йому не блокувати та запускати сценарій як можна швидше. Налаштування deferдозволить не блокувати, а зачекати із запуском сценарію, поки сторінка не буде виконана. Зазвичай це не проблема запускати сценарії ASAP до тих пір, поки вони не мають будь-яких залежностей (наприклад, jQuery). Якщо один скрипт має залежність від іншого, використовуйте його, onLoadщоб зачекати.
Штійн де Вітт

@StijndeWitt Що робити, якщо не вказано ні асинхронізацію, ні відстрочку?
Мохаммед Шаріф C

@MohammedShareefC Якщо ви не вказали жодне, ви отримаєте щось, що нагадує поведінку синхронізації; під час завантаження та запуску сценарію браузер блокує подальшу обробку. Так ефективно поведінка за замовчуванням - це синхронізація. Але оскільки це згубно для продуктивності, браузери будуть працювати навколо нього наскільки це можливо. Вони можуть продовжувати розбір, завантаження та застосування CSS тощо ... Однак вони не можуть почати запускати наступний фрагмент сценарію. Тож матеріал заблокується. Встановивши асинхронізацію, ви говорите браузеру, що нормально йти вперед, і ви переконаєтесь, що змусите його працювати самостійно.
Штійн де Вітт

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-коду, щоб інші ми могли найближчим часом вийти з кодування пекла. Дякую!
Штійн де Вітт

Подивіться , будь ласка , на мій новий відповідь: stackoverflow.com/questions/7718935/load-scripts-asynchronously / ...
asmmahmud

8

Приклад від google

<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 правильне , завдяки Емн, яка вказала на це в коментарі трохи нижче)
  • Використання тайм-аутів для контролю за замовленням не дуже добре і безпечно, а ви також зробите час завантаження набагато більшим, щоб дорівнювати сумі всіх тайм-аутів!

Оскільки є нещодавня причина для завантаження файлів асинхронно, але для того, я рекомендую трохи більш функціонально керувати своїм прикладом (видалити 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чітко вказана як булева в рекомендації HTML 5 W3C за адресою 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) завершено ();' додавання 'return': 'if (! files.length) {завершено (); повернення;} '
Філ

4

Завдяки HTML5 ви тепер можете оголосити сценарії, які ви хочете завантажити асинхронно, додавши в тег "async":

<script async>...</script>

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

Примітка. Існує кілька способів виконання зовнішнього скрипту:

  • Якщо присутній асинхронізація: сценарій виконується асинхронно з рештою сторінки (сценарій буде виконуватися, поки сторінка продовжує розбір)
  • Якщо асинхроніка відсутня і відкладено, сценарій виконується, коли сторінка закінчила розбір
  • Якщо немає асинхронізації або відкладати: сценарій дістається та виконується негайно, перш ніж браузер продовжить розбір сторінки

Дивіться це: 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 з асинхронною помилкою .

На сайті 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 говорить: я розумію "асинхронізацію", але не розумію, як встановити її на "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

Я б запропонував вам поглянути на Модернізр . Це невелика бібліотека з невеликою вагою, в яку ви можете асинхронно завантажувати свій 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/asynchronmous-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()


0

Добре, x.parentNodeповертає елемент HEAD, тому ви вставляєте сценарій безпосередньо перед тегом head. Можливо, в цьому і проблема.

Спробуйте x.parentNode.appendChild()замість цього.



0

Ви можете використовувати LABJS або RequreJS

Навантажувачі сценаріїв, як-от LABJS, RequireJSпокращать швидкість та якість вашого коду.


0

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

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 або просто вбудувати теги на сторінку за допомогою 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 repo : 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.