cargar scripts asincrónicamente


125

Estoy usando varios complementos, widgets personalizados y algunas otras bibliotecas de JQuery. Como resultado, tengo varios archivos .js y .css. Necesito crear un cargador para mi sitio porque tarda un poco en cargar. Sería bueno si puedo mostrar el cargador antes de importar todo el:

<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

He encontrado varios tutoriales que me permiten importar una biblioteca de JavaScript de forma asincrónica. por ejemplo puedo hacer algo como:

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

por alguna razón, cuando hago lo mismo para todos mis archivos, las páginas no funcionan. He intentado durante mucho tiempo tratar de encontrar dónde está el problema, pero simplemente no puedo encontrarlo. Primero pensé que probablemente era porque algunas funciones de JavaScript dependían de las demás. pero los cargué en el orden correcto usando la función de tiempo de espera cuando se completó, pasé al siguiente y la página todavía se comporta de manera extraña. por ejemplo, no puedo hacer clic en enlaces, etc ... aunque las animaciones todavía funcionan ...

De todos modos

Esto es lo que he estado pensando ... Creo que los navegadores tienen un caché, por eso lleva mucho tiempo cargar la página por primera vez y la próxima vez que sea rápida. así que lo que estoy pensando hacer es reemplazar mi página index.html con una página que carga todos estos archivos de forma asincrónica. cuando ajax termina de cargar todos esos archivos redirigen a la página que planeo usar. cuando use esa página, no debería demorar mucho en cargar, ya que los archivos ya deberían estar incluidos en la memoria caché del navegador. en mi página de índice (página donde se cargan los archivos .js y .css de forma asíncrona) No me importa recibir errores. Solo mostraré un cargador y redirigiré la página cuando termine ...

¿Es esta idea una buena alternativa? ¿O debería seguir intentando implementar los métodos asincrónicos?


EDITAR

la forma en que cargo todo asíncrono es como:

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

Y la parte extraña es que todo el trabajo del estilo más todas las funciones de JavaScript. Sin embargo, la página está congelada por alguna razón ...

Respuestas:


176

Un par de soluciones para la carga asíncrona:

//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 ya tienes jQuery en la página, solo usa:

$.getScript(url, successCallback)* *

Además, es posible que sus scripts se carguen / ejecuten antes de que el documento termine de cargarse, lo que significa que deberá esperar document.readyantes de que los eventos puedan vincularse a los elementos.

No es posible determinar específicamente cuál es su problema sin ver el código.

La solución más simple es mantener todos sus scripts en línea en la parte inferior de la página, de esa manera no bloqueen la carga de contenido HTML mientras se ejecutan. También evita el problema de tener que cargar asincrónicamente cada script requerido.

Si tiene una interacción particularmente sofisticada que no siempre se usa y que requiere un script más grande de algún tipo, podría ser útil evitar cargar ese script en particular hasta que sea necesario (carga diferida).

* los scripts cargados $.getScriptprobablemente no se almacenarán en caché


Para cualquiera que pueda usar características modernas como el Promiseobjeto, la loadScriptfunción se ha vuelto significativamente más 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);
    });
}

Tenga en cuenta que esta versión ya no acepta un callbackargumento, ya que la promesa devuelta se encargará de la devolución de llamada. Lo que antes habría sido loadScript(src, callback)ahora sería loadScript(src).then(callback).

Esto tiene la ventaja adicional de poder detectar y manejar fallas, por ejemplo, uno podría llamar ...

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

... y manejaría los cortes de CDN con gracia.


Estoy a punto de comenzar a probar eso. pero si cargo esos scripts en una página diferente cuando redirijo la página a una página diferente y esa página tiene los mismos scripts, no debería tomar tiempo cargarlos. deberían estar en el caché ¿verdad? Entonces, ¿no debería implementar esa técnica en su lugar?
Tono Nam

1
Cambié y agregué al niño a la etiqueta de la cabeza en lugar del cuerpo ... ..var x = document.getElementsByTagName ('cabeza') [0]; x.appendChild (s);
Tono Nam

1
@TonoNam, podría usar document.head.appendChild, sin embargo, hay algunos problemas de nicho al agregar scripts a <head>; nada que impida que los scripts se carguen y ejecuten.
zzzzBov

1
Tuve que usar t.parentNode.insertBefore (en lugar de t.parent).
Phil LaNasa

1
@MuhammadUmer, si está escribiendo una etiqueta de secuencia de comandos adicional document.writeantes de que se cargue el documento, la secuencia de comandos se ejecutará sincrónicamente como parte de la carga de la página y no incluirá ningún comportamiento de devolución de llamada asíncrono por sí solo. Si está creando un elemento de secuencia de comandos y lo agrega a la página, tendrá acceso para agregar oyentes de eventos para que se activen de forma asincrónica. tl; dr: depende de cómo esté cargando el script con JS.
zzzzBov

33

Se supone que el nuevo atributo 'asíncrono' de HTML5 funciona. 'aplazar' también es compatible con la mayoría de los navegadores si te interesa IE.

async - El HTML

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

diferir - El HTML

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

Al analizar el nuevo código de bloque de anuncios adsense, noté el atributo y una búsqueda me condujo aquí: http://davidwalsh.name/html5-async


Según tengo entendido después de leer stackoverflow.com/questions/2774373/… , asyn no es para cargar, sino para determinar cuándo ejecutar el javascript contenido.
JackDev

9
El atributo de aplazamiento hace que el navegador difiera la ejecución de la secuencia de comandos hasta que el documento se haya cargado y analizado y esté listo para ser manipulado. El atributo asíncrono hace que el navegador para ejecutar el script tan pronto como sea posible, pero no para documentar bloque de análisis, mientras que el guión está siendo descargado
shuseel

1
"asyn no es para cargar, sino para determinar cuándo ejecutar el javascript contenido" Esto no es contradictorio; En todos los casos, el navegador comenzará a cargar el script lo antes posible. Pero sin async o deferbloqueará el renderizado mientras se carga. La configuración asyncle indicará que no bloquee y ejecute el script lo antes posible. La configuración deferle indicará que no se bloquee, pero espere con la ejecución del script hasta que se complete la página. Por lo general, no es un problema ejecutar scripts lo antes posible siempre que no tengan dependencias (por ejemplo, jQuery). Si un script tiene dependencias de otro, utilícelo onLoadpara esperarlo.
Stijn de Witt

@StijndeWitt ¿Qué sucede si no se especifica ni asincr ni diferir?
Mohammed Shareef C

@MohammedShareefC Si no especifica ninguno de los dos, obtendrá algo parecido al comportamiento de sincronización; el navegador bloquea el procesamiento adicional mientras descarga y ejecuta el script. Entonces, efectivamente, el comportamiento predeterminado es la sincronización. Pero debido a que esto es perjudicial para el rendimiento, los navegadores lo evitarán tanto como puedan. Pueden continuar con el análisis, la carga y la aplicación de CSS, etc. Sin embargo, no pueden comenzar a ejecutar el siguiente fragmento de script. Entonces las cosas se bloquearán. Al configurar asíncrono, le está diciendo al navegador que está bien continuar y se asegurará de que funcione usted mismo.
Stijn de Witt

8

Cargué los scripts de forma asincrónica (html 5 tiene esa característica) cuando todos los scripts se cargaron, redirigí la página a index2.html donde index2.html usa las mismas bibliotecas. Debido a que los navegadores tienen una memoria caché una vez que la página redirige a index2.html, index2.html se carga en menos de un segundo porque tiene todo lo que necesita para cargar la página. En mi página index.html también cargo las imágenes que planeo usar para que el navegador coloque esas imágenes en el caché. entonces mi index.html se ve así:

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

Otra cosa buena de esto es que puedo colocar un cargador en la página y cuando la página termine de cargarse, el cargador desaparecerá y en cuestión de milisegundos la nueva página se ejecutará.


1
@MohammedShareefC Es una ironía. Una página especial solo para precargar cosas no parece una buena idea SEO. La precarga es excelente, pero no necesita redirigir para lograrlo. Simplemente pegue los enlaces en un div oculto y el navegador se encargará de usted automáticamente. Entonces no redirija, solo renderice la página real. COPIAR-PASTERES LEA ESTO : Cambie la codificación ISO-88591 a UTF-8 antes de usar cualquiera de estos HTML para que el resto de nosotros podamos salir de la codificación en cualquier momento pronto. ¡Gracias!
Stijn de Witt


8

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

8

Varias notas:

  • s.async = trueno es muy correcto para HTML5 doctype, correcto es s.async = 'async'(en realidad el uso true es correcto , gracias a amn que lo señaló en el comentario a continuación)
  • ¡Usar tiempos de espera para controlar el pedido no es muy bueno ni seguro, y también hace que el tiempo de carga sea mucho mayor, para igualar la suma de todos los tiempos de espera!

Dado que hay una razón reciente para cargar archivos de forma asincrónica, pero en orden, recomendaría un poco más de manera funcional sobre su ejemplo (eliminar console.logpara uso de producción :)):

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

44
s.async = truees correcta . La propiedad asyncse especifica explícitamente como un booleano en la recomendación HTML 5 del W3C en w3.org/TR/html5/scripting-1.html#attr-script-async . Está confundiendo el async atributo con la asyncpropiedad expuesta por los objetos que implementan la HTMLScriptElementinterfaz DOM. De hecho, al manipular el atributo correspondiente del elemento a través de algo así Element.setAttribute, debe usarlo element.setAttribute("async", "async")ya que todos los atributos HTML son, ante todo, texto.
amn

"Dado que hay una razón reciente para cargar archivos de forma asincrónica, pero en orden, recomendaría un poco más de manera funcional sobre su ejemplo [...]". Sé que la sincronización suele ser "mejor", pero ¿a qué razón "reciente" se refiere?
BluE

Cambié 'if (! Files.length) complete ();' agregando un 'retorno': 'if (! files.length) {complete (); volver;} '
Fil

4

Gracias a HTML5, ahora puede declarar los scripts que desea cargar de forma asincrónica agregando "async" en la etiqueta:

<script async>...</script>

Nota: El atributo asíncrono es solo para scripts externos (y solo debe usarse si el atributo src está presente).

Nota: Hay varias formas de ejecutar un script externo:

  • Si está presente la sincronización: el script se ejecuta de forma asíncrona con el resto de la página (el script se ejecutará mientras la página continúa el análisis)
  • Si async no está presente y el aplazamiento está presente: el script se ejecuta cuando la página ha terminado de analizar
  • Si no hay asíncrono ni aplazamiento: el script se recupera y ejecuta de inmediato, antes de que el navegador continúe analizando la página

Vea esto: http://www.w3schools.com/tags/att_script_async.asp


2

Completaría la respuesta de zzzzBov con una comprobación de la presencia de devolución de llamada y permitiría pasar argumentos:

    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

Aquí hay una gran solución contemporánea para la carga de scripts asíncronos, aunque solo aborda el script js con async false .

Hay un gran artículo escrito en www.html5rocks.com : sumérgete en las aguas turbias de la carga de guiones .

Después de considerar muchas soluciones posibles, el autor concluyó que agregar scripts js al final del elemento body es la mejor manera posible de evitar el bloqueo de la representación de páginas por scripts js.

Mientras tanto, el autor agregó otra buena solución alternativa para aquellas personas que están desesperadas por cargar y ejecutar scripts de forma asincrónica.

Teniendo en cuenta que tiene cuatro scripts nombrados script1.js, script2.js, script3.js, script4.js, puede hacerlo aplicando 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);
});

Ahora, Spec dice : Descargar juntos, ejecutar en orden tan pronto como se descargue todo.

Firefox <3.6, Opera dice: No tengo idea de qué es esta cosa "asíncrona", pero sucede que ejecuto scripts agregados a través de JS en el orden en que se agregaron.

Safari 5.0 dice: Entiendo "async", pero no entiendo configurarlo en "false" con JS. Ejecutaré tus scripts tan pronto como lleguen, en cualquier orden.

IE <10 dice: No tengo idea sobre "async", pero hay una solución alternativa usando "onreadystatechange".

Todo lo demás dice: soy tu amigo, vamos a hacer esto según el libro.

Ahora, el código completo con IE <10 solución alternativa:

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

Una razón por la cual sus scripts podrían cargarse tan lentamente es si estaba ejecutando todos sus scripts mientras cargaba la página, de esta manera:

callMyFunctions();

en vez de:

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

Este segundo fragmento de secuencia de comandos espera hasta que el navegador haya cargado por completo todo su código Javascript antes de comenzar a ejecutar cualquiera de sus secuencias de comandos, lo que hace que parezca que la página se ha cargado más rápido.

Si está buscando mejorar la experiencia del usuario al disminuir el tiempo de carga, no elegiría la opción "pantalla de carga". En mi opinión, eso sería mucho más molesto que simplemente hacer que la página se cargue más lentamente.


1

Te sugiero que eches un vistazo a Modernizr . Es una biblioteca pequeña y liviana que puede cargar asincrónicamente su javascript con características que le permiten verificar si el archivo está cargado y ejecutar el script en el otro que especifique.

Aquí hay un ejemplo de carga 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'
  }
]);

1

Escribí una pequeña publicación para ayudar con esto, puedes leer más aquí https://timber.io/snippets/asynchronously-load-a-script-in-the-browser-with-javascript/ , pero adjunto la clase de ayuda a continuación. Esperará automáticamente a que se cargue un script y devolverá un atributo de ventana especificado una vez que lo haga.

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

El uso es así:

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

Bueno, x.parentNodedevuelve el elemento HEAD, por lo que está insertando el script justo antes de la etiqueta head. Quizás ese sea el problema.

Intenta en su x.parentNode.appendChild()lugar.



0

Puedes usar LABJS o RequreJS

Los cargadores de scripts como LABJS, RequireJSmejorarán la velocidad y la calidad de su código.


0

¿Has considerado usar Fetch Injection? Hice rodar una biblioteca de código abierto llamada fetch-inject para manejar casos como estos. Así es como se vería su cargador usando 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'
})

Para la compatibilidad con la versión anterior, aproveche la detección de funciones y recurra a XHR Injection o Script DOM Elements, o simplemente inserte las etiquetas en la página usando document.write.


0

Aquí está mi solución personalizada para eliminar JavaScript que bloquea el renderizado:

// 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 resumen, carga todos sus scripts de forma asíncrona y luego los ejecuta en consecuencia.

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


1
No funcionaNo 'Access-Control-Allow-Origin' header is present on the requested resource
Abram


0

Aquí una pequeña función ES6 si alguien quiere usarla en React, por ejemplo

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

Sugeriría buscar minificar los archivos primero y ver si eso te da un aumento de velocidad lo suficientemente grande. Si su host es lento, podría intentar poner ese contenido estático en un CDN.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.