charger des scripts de manière asynchrone

125

J'utilise plusieurs plugins, widgets personnalisés et quelques autres bibliothèques de JQuery. par conséquent, j'ai plusieurs fichiers .js et .css. J'ai besoin de créer un chargeur pour mon site car le chargement prend un certain temps. ce sera bien si je peux afficher le chargeur avant d'importer tous les:

<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

J'ai trouvé plusieurs tutoriels qui me permettent d'importer une bibliothèque JavaScript de manière asynchrone. par exemple, je peux faire quelque chose comme:

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

pour une raison quelconque, lorsque je fais la même chose pour tous mes fichiers, les pages ne fonctionnent pas. J'essaie depuis si longtemps d'essayer de trouver où se situe le problème, mais je ne le trouve tout simplement pas. J'ai d'abord pensé que c'était probablement parce que certaines fonctions javascript dépendaient des autres. mais je les ai chargés dans le bon ordre en utilisant la fonction de temporisation quand l'un est terminé, j'ai continué avec le suivant et la page se comporte toujours bizarrement. par exemple je ne peux pas cliquer sur les liens etc ... les animations fonctionnent quand même ..

Quoi qu'il en soit

Voici ce à quoi j'ai pensé ... Je pense que les navigateurs ont un cache, c'est pourquoi il faut beaucoup de temps pour charger la page pour la première fois et la prochaine fois c'est rapide. donc ce que je pense faire est de remplacer ma page index.html par une page qui charge tous ces fichiers de manière asynchrone. quand ajax a fini de charger tous ces fichiers, redirigez vers la page que je prévois d'utiliser. lors de l'utilisation de cette page, le chargement ne devrait pas prendre longtemps car les fichiers doivent déjà être inclus dans le cache du navigateur. sur ma page d'index (page où les fichiers .js et .css sont chargés de manière asynchrone) je ne me soucie pas des erreurs. Je vais simplement afficher un chargeur et rediriger la page une fois terminé ...

Cette idée est-elle une bonne alternative? ou devrais-je continuer à essayer d'implémenter les méthodes de manière asynchrone?


ÉDITER

la façon dont je charge tout asynchrone est comme:

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

et la partie étrange est que tout le travail du style plus toutes les fonctions javascript. la page est gelée pour une raison quelconque ...

Tono Nam
la source

Réponses:

176

Quelques solutions pour le chargement asynchrone:

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

Si vous avez déjà jQuery sur la page, utilisez simplement:

$.getScript(url, successCallback)*

De plus, il est possible que vos scripts soient chargés / exécutés avant la fin du chargement du document, ce qui signifie que vous devrez attendre document.readyavant que les événements puissent être liés aux éléments.

Il n'est pas possible de dire précisément quel est votre problème sans voir le code.

La solution la plus simple est de garder tous vos scripts en ligne au bas de la page, de cette façon ils ne bloquent pas le chargement du contenu HTML pendant leur exécution. Cela évite également le problème de devoir charger de manière asynchrone chaque script requis.

Si vous avez une interaction particulièrement sophistiquée qui n'est pas toujours utilisée et qui nécessite un script plus volumineux, il peut être utile d'éviter de charger ce script particulier jusqu'à ce qu'il soit nécessaire (chargement paresseux).

* les scripts chargés avec $.getScriptne seront probablement pas mis en cache


Pour tous ceux qui peuvent utiliser des fonctionnalités modernes telles que l' Promiseobjet, la loadScriptfonction est devenue nettement plus simple:

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

Sachez que cette version n'accepte plus d' callbackargument car la promesse retournée gérera le rappel. Ce qui aurait été auparavant le loadScript(src, callback)serait maintenant loadScript(src).then(callback).

Cela a l'avantage supplémentaire de pouvoir détecter et gérer les pannes, par exemple on pourrait appeler ...

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

... et il gérerait gracieusement les pannes de CDN.

zzzzBov
la source
Je suis sur le point de commencer à essayer ça. mais si je charge ces scripts sur une page différente lorsque je redirige la page vers une page différente et que cette page se trouve avoir les mêmes scripts, ils ne devraient pas prendre de temps à se charger. ils devraient être dans le droit de cache? alors ne devrais-je pas mettre en œuvre cette technique à la place?
Tono Nam
1
J'ai changé et ajouté l'enfant à la balise head au lieu du body ... ..var x = document.getElementsByTagName ('head') [0]; x.appendChild (s);
Tono Nam
1
@TonoNam, vous pouvez simplement utiliser document.head.appendChild, mais il y a quelques problèmes de niche avec l'ajout de scripts au <head>; rien qui empêcherait le chargement et l'exécution des scripts.
zzzzBov
1
J'ai dû utiliser t.parentNode.insertBefore (par opposition à t.parent).
Phil LaNasa
1
@MuhammadUmer, si vous écrivez une balise de script supplémentaire avec document.writeavant le chargement du document, le script sera exécuté de manière synchrone lors du chargement de la page et n'inclura pas de comportement de rappel asynchrone à lui seul. Si vous créez un élément de script et l'ajoutez à la page, vous aurez alors accès pour ajouter des écouteurs d'événements à déclencher de manière asynchrone. tl; dr: cela dépend de la façon dont vous chargez le script avec JS.
zzzzBov
33

Le nouvel attribut «async» de HTML5 est censé faire l'affaire. «différer» est également pris en charge dans la plupart des navigateurs si vous vous souciez d'IE.

async - Le HTML

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

différer - Le HTML

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

Lors de l'analyse du nouveau code du bloc d'annonces AdSense, j'ai remarqué que l'attribut et une recherche m'ont conduit ici: http://davidwalsh.name/html5-async

Donald Porter
la source
d'après ma compréhension après avoir lu stackoverflow.com/questions/2774373/… , asyn n'est pas pour le chargement, mais pour déterminer quand exécuter le javascript contenu.
JackDev
9
L'attribut defer oblige le navigateur à différer l'exécution du script jusqu'à ce que le document ait été chargé et analysé et soit prêt à être manipulé. L'attribut async fait que le navigateur exécute le script dès que possible mais ne bloque pas l'analyse du document pendant le téléchargement du script
shuseel
1
"asyn n'est pas pour le chargement, mais pour déterminer quand exécuter le javascript contenu" Ce n'est pas contradictoire; Dans tous les cas, le navigateur commencera à charger le script dès que possible. Mais sans async ou defercela bloquera le rendu pendant le chargement. Le réglage lui asyncdira de ne pas bloquer et d'exécuter le script dès que possible. Le réglage lui deferdira de ne pas bloquer mais d'attendre avec l' exécution du script jusqu'à ce que la page soit terminée. En général, ce n'est pas un problème d'exécuter des scripts dès que possible tant qu'ils n'ont pas de dépendances (par exemple jQuery). Si un script a des dépendances sur un autre, utilisez onLoadpour l'attendre.
Stijn de Witt
@StijndeWitt Que faire si ni asynchrone ni report n'est spécifié?
Mohammed Shareef C du
@MohammedShareefC Si vous ne spécifiez ni l'un ni l'autre, vous obtenez quelque chose qui ressemble étroitement au comportement de synchronisation; le navigateur bloque tout traitement ultérieur pendant le téléchargement et l’exécution du script. Donc, effectivement, le comportement par défaut est la synchronisation. Mais comme cela nuit aux performances, les navigateurs contourneront ce problème autant qu'ils le peuvent. Ils peuvent continuer avec l'analyse, le chargement et l'application de CSS, etc. Donc, les choses vont bloquer. En définissant asynchrone, vous dites au navigateur que vous pouvez continuer et vous vous assurerez de le faire fonctionner vous-même.
Stijn de Witt
8

J'ai chargé les scripts de manière asynchrone (html 5 a cette fonctionnalité) lorsque tous les scripts ont été chargés, j'ai redirigé la page vers index2.html où index2.html utilise les mêmes bibliothèques. Étant donné que les navigateurs ont un cache une fois que la page est redirigée vers index2.html, index2.html se charge en moins d'une seconde car il a tout ce dont il a besoin pour charger la page. Dans ma page index.html, je charge également les images que je prévois d'utiliser afin que le navigateur place ces images dans le cache. donc mon index.html ressemble à:

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

une autre bonne chose à ce sujet est que je peux placer un chargeur dans la page et lorsque le chargement de la page est terminé, le chargeur disparaîtra et dans un cache de millisecondes la nouvelle page fonctionnera.

Tono Nam
la source
1
@MohammedShareefC C'est de l'ironie. Une page spéciale juste pour précharger des éléments ne semble pas être une bonne idée en termes de référencement. Le préchargement est excellent, mais vous n'avez pas besoin de rediriger pour y parvenir. Collez simplement les liens dans un div caché et le navigateur s'en chargera automatiquement pour vous. Ensuite, ne redirigez pas mais affichez simplement la page réelle. COPY-PASTERS LISEZ CECI : Veuillez changer le codage ISO-88591 en UTF-8 avant d'utiliser l'un de ces HTML afin que le reste d'entre nous puisse sortir de l'enfer de l'encodage de sitôt. Merci!
Stijn de Witt
Veuillez regarder ma nouvelle réponse: stackoverflow.com/questions/7718935/load-scripts-asynchronously
...
8

Exemple de 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>
Kernel Med
la source
8

Plusieurs remarques:

  • s.async = truen'est pas très correct pour le doctype HTML5, c'est correct s.async = 'async'(en fait l'utilisation true est correcte , merci à amn qui l'a souligné dans le commentaire ci-dessous)
  • Utiliser des délais pour contrôler la commande n'est pas très bon et sûr, et vous augmentez également considérablement le temps de chargement, pour égaler la somme de tous les délais!

Puisqu'il y a une raison récente de charger des fichiers de manière asynchrone, mais dans l'ordre, je recommanderais une manière un peu plus fonctionnelle par rapport à votre exemple (supprimer console.logpour une utilisation en production :)):

(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);
})();
shaman.sir
la source
4
s.async = trueest correct . La propriété asyncest spécifiée explicitement comme un booléen dans la recommandation HTML 5 du W3C à w3.org/TR/html5/scripting-1.html#attr-script-async . Vous confondez l' async attribut avec la asyncpropriété exposée par les objets implémentant l' HTMLScriptElementinterface DOM. En effet, lorsque vous manipulez l'attribut correspondant de l'élément via quelque chose comme Element.setAttribute, vous devez l'utiliser element.setAttribute("async", "async")car tous les attributs HTML sont avant tout du texte.
amn le
"Puisqu'il y a une raison récente de charger des fichiers de manière asynchrone, mais dans l'ordre, je recommanderais une manière un peu plus fonctionnelle par rapport à votre exemple [...]". Je sais qu'async est généralement «meilleur», mais à quelle raison «récente» parlez-vous?
BluE
J'ai changé 'if (! Files.length) completed ();' ajout d'un 'return': 'if (! files.length) {completed (); return;} '
Fil
4

Grâce à HTML5, vous pouvez désormais déclarer les scripts que vous souhaitez charger de manière asynchrone en ajoutant "async" dans la balise:

<script async>...</script>

Remarque: l'attribut async est uniquement destiné aux scripts externes (et ne doit être utilisé que si l'attribut src est présent).

Remarque: Il existe plusieurs manières d'exécuter un script externe:

  • Si async est présent: le script est exécuté de manière asynchrone avec le reste de la page (le script sera exécuté pendant que la page continue l'analyse)
  • Si async n'est pas présent et que defer est présent: le script est exécuté lorsque la page a terminé l'analyse
  • Si ni asynchrone ni différer ne sont présents: le script est récupéré et exécuté immédiatement, avant que le navigateur ne poursuive l'analyse de la page

Voir ceci: http://www.w3schools.com/tags/att_script_async.asp

Odroz
la source
2

Je compléterais la réponse de zzzzBov par un contrôle de la présence de callback et autoriserais le passage d'arguments:

    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);
    }
Tibo
la source
2

Voici une excellente solution contemporaine au chargement de script asynchrone bien qu'elle ne traite que le script js avec async false .

Il y a un excellent article écrit sur www.html5rocks.com - Plongez dans les eaux troubles du chargement de scripts .

Après avoir examiné de nombreuses solutions possibles, l'auteur a conclu que l'ajout de scripts js à la fin de l'élément body est la meilleure façon d'éviter de bloquer le rendu de page par les scripts js.

Dans le même temps, l'auteur a ajouté une autre bonne solution alternative pour les personnes qui désespèrent de charger et d'exécuter des scripts de manière asynchrone.

Étant donné que vous avez nommé quatre scripts, script1.js, script2.js, script3.js, script4.jsvous pouvez le faire en appliquant 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);
});

Maintenant, Spec dit : Téléchargez ensemble, exécutez dans l'ordre dès que tous les téléchargements.

Firefox <3.6, Opera dit: Je n'ai aucune idée de ce qu'est ce truc «asynchrone», mais il se trouve que j'exécute les scripts ajoutés via JS dans l'ordre dans lequel ils sont ajoutés.

Safari 5.0 dit: Je comprends «async», mais je ne comprends pas le définir sur «faux» avec JS. J'exécuterai vos scripts dès leur arrivée, dans n'importe quel ordre.

IE <10 dit: Aucune idée de «async», mais il existe une solution de contournement en utilisant «onreadystatechange».

Tout le reste dit: je suis votre ami, nous allons le faire par le livre.

Maintenant, le code complet avec la solution de contournement 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>');
  }
}
asmmahmud
la source
1

Une des raisons pour lesquelles vos scripts pourraient se charger si lentement est si vous exécutiez tous vos scripts pendant le chargement de la page, comme ceci:

callMyFunctions();

au lieu de:

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

Ce deuxième bit de script attend que le navigateur ait complètement chargé tout votre code Javascript avant de commencer à exécuter l'un de vos scripts, faisant croire à l'utilisateur que la page s'est chargée plus rapidement.

Si vous cherchez à améliorer l'expérience de l'utilisateur en diminuant le temps de chargement, je n'irais pas pour l'option "écran de chargement". À mon avis, ce serait beaucoup plus ennuyeux que de simplement charger la page plus lentement.


la source
1

Je vous suggère de jeter un œil à Modernizr . C'est une petite bibliothèque légère que vous pouvez charger de manière asynchrone votre javascript avec des fonctionnalités qui vous permettent de vérifier si le fichier est chargé et d'exécuter le script dans l'autre que vous spécifiez.

Voici un exemple de chargement de 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'
  }
]);
Shawn Mclean
la source
1

J'ai écrit un petit article pour vous aider, vous pouvez en lire plus ici https://timber.io/snippets/asynchronously-load-a-script-in-the-browser-with-javascript/ , mais j'ai joint la classe d'assistance ci-dessous. Il attendra automatiquement le chargement d'un script et retournera un attribut de fenêtre spécifié une fois qu'il le fera.

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

L'utilisation est comme ceci:

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()
Zach
la source
1

Vous trouverez peut-être cet article wiki intéressant: http://ajaxpatterns.org/On-Demand_Javascript

Il explique comment et quand utiliser une telle technique.

tereško
la source
Malheureusement, le lien semble mort. :(
Eric Seastrand
@Eric c'est parce que c'est une ancienne méthode de chargement dynamique du code: D
tereško
0

Eh bien, x.parentNoderenvoie l'élément HEAD, donc vous insérez le script juste avant la balise head. C'est peut-être ça le problème.

Essayez à la x.parentNode.appendChild()place.

Alekop
la source
0

Vous pouvez utiliser LABJS ou RequreJS

Les chargeurs de scripts comme LABJS, RequireJSamélioreront la vitesse et la qualité de votre code.

Kaushik
la source
0

Avez-vous envisagé d'utiliser Fetch Injection? J'ai lancé une bibliothèque open source appelée fetch-inject pour gérer des cas comme ceux-ci. Voici à quoi pourrait ressembler votre chargeur en utilisant la 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'
})

Pour une compatibilité ascendante, tirez parti de la détection des fonctionnalités et du retour à l'injection XHR ou aux éléments DOM de script, ou simplement en ligne les balises dans la page en utilisant document.write.

Josh Habdas
la source
0

Voici ma solution personnalisée pour éliminer le JavaScript bloquant le rendu:

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

En bref, il charge tous vos scripts de manière asynchrone, puis les exécute en conséquence.

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

Damjan Pavlica
la source
1
Ne fonctionne pasNo 'Access-Control-Allow-Origin' header is present on the requested resource
Abram
1
C'est le problème CORS. stackoverflow.com/questions/20035101/…
Damjan Pavlica
0

Voici une petite fonction ES6 si quelqu'un veut l'utiliser dans React par exemple

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

JoxieMedina
la source
-3

Je suggérerais de commencer par minimiser les fichiers et de voir si cela vous donne une augmentation de vitesse suffisamment importante. Si votre hôte est lent, essayez de mettre ce contenu statique sur un CDN.

Jason Miesionczek
la source