ładuj skrypty asynchronicznie


125

Używam kilku wtyczek, niestandardowych widżetów i kilku innych bibliotek JQuery. w rezultacie mam kilka plików .js i .css. Muszę utworzyć program ładujący dla mojej witryny, ponieważ ładowanie zajmuje trochę czasu. byłoby miło, gdybym mógł wyświetlić program ładujący przed zaimportowaniem wszystkich:

<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

Znalazłem kilka samouczków, które umożliwiają mi asynchroniczne importowanie biblioteki JavaScript. na przykład mogę zrobić coś takiego:

  (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);
    })();

z jakiegoś powodu, gdy robię to samo dla wszystkich moich plików, strony nie działają. Od tak dawna próbuję znaleźć przyczynę problemu, ale po prostu nie mogę go znaleźć. Najpierw pomyślałem, że to prawdopodobnie dlatego, że niektóre funkcje javascript zależą od innych. ale załadowałem je we właściwej kolejności, korzystając z funkcji limitu czasu, gdy jedna została zakończona, przeszedłem do następnej, a strona nadal zachowuje się dziwnie. na przykład nie mogę klikać linków itp ... animacje jednak nadal działają ..

Tak czy inaczej

Oto, o czym myślałem ... Uważam, że przeglądarki mają pamięć podręczną, dlatego ładowanie strony po raz pierwszy zajmuje dużo czasu, a następnym razem jest szybkie. więc myślę o zastąpieniu mojej strony index.html stroną, która ładuje wszystkie te pliki asynchronicznie. po zakończeniu ładowania AJAX wszystkie te pliki przekierowują do strony, której zamierzam użyć. podczas korzystania z tej strony ładowanie tej strony nie powinno zająć dużo czasu, ponieważ pliki powinny już znajdować się w pamięci podręcznej przeglądarki. na mojej stronie indeksu (strona, na której pliki .js i .css są ładowane asynchronicznie) nie obchodzą mnie błędy. Będę po prostu wyświetlać program ładujący i przekierowywać stronę po zakończeniu ...

Czy to dobra alternatywa? czy powinienem nadal próbować implementować metody asynchroniczne?


EDYTOWAĆ

sposób, w jaki ładuję wszystko asynchronicznie, wygląda następująco:

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

a najdziwniejsze jest to, że działa cały styl plus wszystkie funkcje javascript. strona jest jednak zawieszona z jakiegoś powodu ...

Odpowiedzi:


176

Kilka rozwiązań do ładowania asynchronicznego:

//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);
}

Jeśli masz już jQuery na stronie, po prostu użyj:

$.getScript(url, successCallback)*

Ponadto możliwe jest, że Twoje skrypty są ładowane / wykonywane przed zakończeniem ładowania dokumentu, co oznacza, że ​​musisz poczekać, document.readyaż zdarzenia zostaną powiązane z elementami.

Nie można dokładnie określić problemu bez zapoznania się z kodem.

Najprostszym rozwiązaniem jest umieszczenie wszystkich skryptów w treści na dole strony, dzięki czemu nie będą one blokować ładowania zawartości HTML podczas wykonywania. Pozwala to również uniknąć problemu asynchronicznego ładowania każdego wymaganego skryptu.

Jeśli masz szczególnie wymyślną interakcję, która nie zawsze jest używana, która wymaga jakiegoś większego skryptu, przydatne może być unikanie ładowania tego konkretnego skryptu, dopóki nie będzie potrzebny (ładowanie z opóźnieniem).

* skrypty załadowane za pomocą $.getScriptprawdopodobnie nie będą buforowane


Dla każdego, kto potrafi korzystać z nowoczesnych funkcji, takich jak Promiseobiekt, loadScriptfunkcja stała się znacznie prostsza:

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);
    });
}

Należy pamiętać, że ta wersja nie akceptuje już callbackargumentu, ponieważ zwrócona obietnica obsługuje wywołanie zwrotne. To, co wcześniej by było loadScript(src, callback), teraz będzie loadScript(src).then(callback).

Ma to dodatkową zaletę w postaci możliwości wykrywania i obsługi awarii, na przykład można zadzwonić ...

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

... iz wdziękiem poradziłby sobie z awariami CDN.


Zaraz zacznę to wypróbowywać. ale jeśli załaduję te skrypty na innej stronie, gdy przekierowuję stronę na inną stronę, a ta strona ma te same skrypty, ładowanie nie powinno zająć czasu. powinny być w pamięci podręcznej, prawda? więc czy nie powinienem zamiast tego wdrożyć tej techniki?
Tono Nam

1
Zmieniłem i dołączyłem dziecko do tagu głowy zamiast ciała ... ..var x = document.getElementsByTagName ('head') [0]; x.appendChild (s);
Tono Nam

1
@TonoNam, możesz po prostu użyć document.head.appendChild, jednak są pewne niszowe problemy z dodawaniem skryptów do <head>; nic, co uniemożliwiłoby ładowanie i wykonywanie skryptów.
zzzzBov

1
Musiałem użyć t.parentNode.insertBefore (w przeciwieństwie do t.parent).
Phil LaNasa

1
@MuhammadUmer, jeśli piszesz dodatkowy tag skryptu z document.writeprzed załadowaniem dokumentu, skrypt zostanie wykonany synchronicznie jako część ładowania strony i sam nie będzie zawierał żadnego asynchronicznego zachowania wywołania zwrotnego. Jeśli tworzysz element skryptu i dodajesz go do strony, będziesz mieć dostęp do dodawania detektorów zdarzeń, które będą wyzwalane asynchronicznie. tl; dr: to zależy od tego, jak ładujesz skrypt za pomocą JS.
zzzzBov

33

Nowy atrybut „async” HTML5 powinien załatwić sprawę. Większość przeglądarek obsługuje również „defer”, jeśli zależy Ci na IE.

async - plik HTML

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

defer - kod HTML

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

Analizując nowy kod jednostki reklamowej AdSense, zauważyłem, że atrybut i wyszukiwanie prowadzą mnie tutaj: http://davidwalsh.name/html5-async


z mojego zrozumienia po przeczytaniu stackoverflow.com/questions/2774373/… , asyn nie służy do ładowania, ale do określenia, kiedy uruchomić zawarty javascript.
JackDev,

9
Atrybut odroczenia powoduje, że przeglądarka odracza wykonanie skryptu do czasu załadowania i przeanalizowania dokumentu i gotowości do manipulacji. Atrybut async powoduje, że przeglądarka uruchamia skrypt tak szybko, jak to możliwe, ale nie blokuje parsowania dokumentu podczas pobierania skryptu
shuseel

1
„asyn nie służy do ładowania, ale do określania, kiedy uruchomić zawarty javascript”. Nie jest to sprzeczne; We wszystkich przypadkach przeglądarka rozpocznie ładowanie skryptu jak najszybciej. Ale bez async lub deferzablokuje renderowanie podczas ładowania. Ustawienie asyncpowie mu, aby nie blokował i nie uruchamiał skryptu jak najszybciej. Ustawienie deferpowie mu, aby nie blokował, ale czekał z uruchomieniem skryptu, aż strona zostanie ukończona. Zwykle nie ma problemu z uruchamianiem skryptów jak najszybciej, o ile nie mają one żadnych zależności (np. JQuery). Jeśli jeden skrypt jest zależny od innego, użyj, onLoadaby poczekać.
Stijn de Witt

@StijndeWitt Co się stanie, jeśli nie określono ani async, ani odroczenia?
Mohammed Shareef C

@MohammedShareefC Jeśli nie określisz żadnego, otrzymasz coś przypominającego zachowanie synchronizacji; przeglądarka blokuje dalsze przetwarzanie podczas pobierania i uruchamiania skryptu. W efekcie domyślnym zachowaniem jest synchronizacja. Ale ponieważ jest to szkodliwe dla wydajności, przeglądarki będą obchodzić to tak często, jak tylko mogą. Mogą kontynuować analizowanie, ładowanie i stosowanie CSS itp ... Nie mogą jednak rozpocząć wykonywania następnego fragmentu skryptu. Więc coś się zablokuje. Ustawiając async, mówisz przeglądarce, że możesz iść dalej i upewnisz się, że sam to zadziała.
Stijn de Witt

8

Załadowałem skrypty asynchronicznie (HTML 5 ma tę funkcję) po zakończeniu ładowania wszystkich skryptów przekierowałem stronę do index2.html, gdzie index2.html używa tych samych bibliotek. Ponieważ przeglądarki mają pamięć podręczną, gdy strona przekierowuje do index2.html, index2.html ładuje się w mniej niż sekundę, ponieważ ma wszystko, czego potrzeba do załadowania strony. Na mojej stronie index.html ładuję również obrazy, których planuję użyć, aby przeglądarka umieściła te obrazy w pamięci podręcznej. więc mój index.html wygląda następująco:

<!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>

Kolejną fajną rzeczą jest to, że mogę umieścić program ładujący na stronie, a po zakończeniu ładowania strony program ładujący zniknie i po kilku milisekundach nowa strona będzie uruchomiona.


1
@MohammedShareefC To ironia. Specjalna strona służąca tylko do wstępnego ładowania rzeczy nie wygląda na dobry pomysł pod względem SEO. Wstępne ładowanie jest świetne, ale nie musisz przekierowywać, aby to osiągnąć. Po prostu umieść linki w ukrytym div, a przeglądarka automatycznie zajmie się tym za Ciebie. Następnie nie przekierowuj, tylko wyrenderuj rzeczywistą stronę. KOPIOWANIE-PASTERZE PRZECZYTAJ : Proszę zmienić kodowanie ISO-88591 na UTF-8 przed użyciem któregokolwiek z tego HTML, aby reszta z nas mogła w najbliższym czasie wydostać się z piekła kodowania. Dzięki!
Stijn de Witt


8

Przykład z 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

Kilka uwag:

  • s.async = truenie jest zbyt poprawny dla dokumentu HTML5, poprawny s.async = 'async'(w rzeczywistości użycie true jest poprawne , dzięki amn, który wskazał to w komentarzu poniżej)
  • Używanie limitów czasu do kontrolowania zamówienia nie jest zbyt dobre i bezpieczne, a także znacznie wydłużasz czas ładowania, aby wyrównać sumę wszystkich limitów czasu!

Ponieważ istnieje niedawny powód, aby ładować pliki asynchronicznie, ale w porządku, polecam nieco bardziej funkcjonalny sposób na twój przykład (usuń console.logdo użytku produkcyjnego :)):

(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 = truejest poprawne . Właściwość asyncjest wyraźnie określona jako wartość logiczna w zaleceniu W3C dotyczącym HTML 5 pod adresem w3.org/TR/html5/scripting-1.html#attr-script-async . Mylisz async atrybut z asyncwłaściwością udostępnianą przez obiekty implementujące HTMLScriptElementinterfejs DOM. Rzeczywiście, manipulując odpowiednim atrybutem elementu za pomocą czegoś podobnego Element.setAttribute, powinieneś używać, element.setAttribute("async", "async")ponieważ wszystkie atrybuty HTML są przede wszystkim tekstem.
amn

„Ponieważ istnieje ostatnio powód, aby ładować pliki asynchronicznie, ale w porządku, zalecałbym nieco bardziej funkcjonalny sposób na przykład [...]”. Wiem, że asynchronizacja jest zwykle „lepsza”, ale do jakiego „niedawnego” powodu się odnosisz?
BluE

Zmieniłem 'if (! Files.length) complete ();' dodanie 'return': 'if (! files.length) {complete (); return;} '
Fil

4

Dzięki HTML5 możesz teraz zadeklarować skrypty, które chcesz ładować asynchronicznie, dodając „async” w tagu:

<script async>...</script>

Uwaga: Atrybut async jest przeznaczony tylko dla skryptów zewnętrznych (i powinien być używany tylko wtedy, gdy atrybut src jest obecny).

Uwaga: istnieje kilka sposobów wykonania zewnętrznego skryptu:

  • Jeśli występuje async: skrypt jest wykonywany asynchronicznie z resztą strony (skrypt zostanie wykonany, gdy strona będzie kontynuować analizowanie)
  • Jeśli nie ma async i istnieje odroczone: skrypt jest wykonywany po zakończeniu analizowania strony
  • Jeśli nie ma ani async, ani odroczenia: skrypt jest pobierany i wykonywany natychmiast, zanim przeglądarka kontynuuje analizowanie strony

Zobacz: http://www.w3schools.com/tags/att_script_async.asp


2

Uzupełniłbym odpowiedź zzzzBov sprawdzeniem obecności callback i pozwoliłbym na przekazywanie argumentów:

    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

Oto świetne, współczesne rozwiązanie asynchronicznego ładowania skryptów, chociaż dotyczy ono tylko skryptu js z async false .

Jest świetny artykuł napisany na www.html5rocks.com - Zanurz się głęboko w mętne wody ładowania skryptów .

Po rozważeniu wielu możliwych rozwiązań autor doszedł do wniosku, że dodanie skryptów js na końcu elementu body jest najlepszym sposobem na uniknięcie blokowania renderowania strony przez skrypty js.

W międzyczasie autor dodał kolejne dobre alternatywne rozwiązanie dla osób, które desperacko chcą ładować i wykonywać skrypty asynchronicznie.

Biorąc pod uwagę, że masz cztery nazwane skrypty script1.js, script2.js, script3.js, script4.js, możesz to zrobić, stosując 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);
});

Teraz Spec mówi : Pobierz razem, wykonuj po kolei, gdy tylko wszystko zostanie pobrane.

Firefox <3.6, Opera mówi: Nie mam pojęcia, co to jest „asynchronizacja”, ale tak się składa, że ​​wykonuję skrypty dodane przez JS w kolejności, w jakiej są dodawane.

Safari 5.0 mówi: Rozumiem „async”, ale nie rozumiem ustawiania go na „false” w JS. Wykonam twoje skrypty, gdy tylko wylądują, w dowolnej kolejności.

IE <10 mówi: Nie mam pojęcia o „async”, ale można obejść ten problem przy użyciu „onreadystatechange”.

Wszystko inne mówi: jestem twoim przyjacielem, zrobimy to na podstawie książki.

Teraz pełny kod z obejściem 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

Jednym z powodów, dla których skrypty ładują się tak wolno, jest to, że podczas ładowania strony uruchamiane były wszystkie skrypty, na przykład:

callMyFunctions();

zamiast:

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

Ten drugi fragment skryptu czeka, aż przeglądarka całkowicie załaduje cały kod JavaScript, zanim rozpocznie wykonywanie któregokolwiek z twoich skryptów, dzięki czemu użytkownik będzie miał wrażenie, że strona załadowała się szybciej.

Jeśli chcesz poprawić wrażenia użytkownika poprzez skrócenie czasu ładowania, nie wybrałbym opcji „ekran ładowania”. Moim zdaniem byłoby to znacznie bardziej irytujące niż wolniejsze ładowanie strony.


1

Proponuję spojrzeć na Modernizr . Jest to mała, lekka biblioteka, którą możesz asynchronicznie ładować swój javascript z funkcjami, które pozwalają sprawdzić, czy plik jest załadowany i wykonać skrypt w innym określonym przez Ciebie.

Oto przykład ładowania 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

Napisałem mały post, aby w tym pomóc, możesz przeczytać więcej tutaj https://timber.io/snippets/asynchronously-load-a-script-in-the-browser-with-javascript/ , ale załączam klasa pomocnicza poniżej. Automatycznie zaczeka na załadowanie skryptu i zwróci określony atrybut okna, gdy to zrobi.

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])
      }
    })
  }
}

Użycie jest takie:

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

Cóż, x.parentNodezwraca element HEAD, więc wstawiasz skrypt tuż przed tagiem head. Może to jest problem.

Spróbuj x.parentNode.appendChild()zamiast tego.




0

Czy rozważałeś użycie funkcji Fetch Injection? Zainstalowałem bibliotekę open source o nazwie fetch-inject, aby obsługiwać takie przypadki. Oto jak może wyglądać program ładujący przy użyciu biblioteki 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'
})

Aby zapewnić kompatybilność wsteczną, skorzystaj z wykrywania funkcji i powróć do XHR Injection lub Script DOM Elements lub po prostu wstaw tagi do strony za pomocą document.write.


0

Oto moje niestandardowe rozwiązanie eliminujące JavaScript blokujący renderowanie:

// 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)

Krótko mówiąc, ładuje wszystkie skrypty asynchronicznie, a następnie wykonuje je w konsekwencji.

Repozytorium Github : https://github.com/mudroljub/js-async-loader


1
Nie działaNo 'Access-Control-Allow-Origin' header is present on the requested resource
Abram


0

Tutaj mała funkcja ES6, jeśli ktoś chce jej użyć na przykład w Reakcie

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

Proponuję najpierw zająć się minifikacją plików i sprawdzić, czy to daje wystarczająco duże zwiększenie prędkości. Jeśli Twój host działa wolno, możesz spróbować umieścić tę statyczną zawartość w sieci CDN.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.