carregar scripts de forma assíncrona

125

Estou usando vários plugins, widgets personalizados e algumas outras bibliotecas do JQuery. Como resultado, tenho vários arquivos .js e .css. Preciso criar um carregador para o meu site, pois leva algum tempo para carregar. será bom se eu puder exibir o carregador antes de importar todos os:

<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

Encontrei vários tutoriais que me permitem importar uma biblioteca JavaScript de forma assíncrona. por exemplo, eu posso fazer 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 algum motivo, quando faço a mesma coisa em todos os meus arquivos, as páginas não funcionam. Eu venho tentando há tanto tempo tentar descobrir onde está o problema, mas simplesmente não consigo encontrá-lo. Primeiro, pensei que era provavelmente porque algumas funções javascript dependiam das outras. mas eu os carreguei na ordem correta, usando a função de tempo limite, quando uma foi concluída, continuei com a próxima e a página ainda se comporta de forma estranha. por exemplo, não consigo clicar nos links etc ... as animações ainda funcionam ..

Enfim

Aqui está o que eu tenho pensado ... Acredito que os navegadores tenham um cache, por isso leva muito tempo para carregar a página pela primeira vez e na próxima vez que é rápido. então o que estou pensando em fazer é substituir minha página index.html por uma página que carregue todos esses arquivos de forma assíncrona. quando o ajax terminar de carregar todos os arquivos redirecionados para a página que pretendo usar. ao usar essa página, não deve demorar muito para carregar, pois os arquivos já devem ser incluídos no cache do navegador. na minha página de índice (página em que os arquivos .js e .css são carregados de forma assíncrona), não me importo de obter erros. Vou apenas exibir um carregador e redirecionar a página quando terminar ...

Essa ideia é uma boa alternativa? ou devo continuar tentando implementar os métodos assíncronos?


EDITAR

do jeito que eu carrego tudo assíncrono é 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

e a parte estranha é que todo o trabalho do estilo, além de todas as funções javascript. a página está congelada por algum motivo ...

Tono Nam
fonte

Respostas:

176

Algumas soluções para carregamento assíncrono:

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

Se você já possui o jQuery na página, use:

$.getScript(url, successCallback)*

Além disso, é possível que seus scripts estejam sendo carregados / executados antes do carregamento do documento, o que significa que seria necessário aguardar document.readyantes que os eventos possam ser vinculados aos elementos.

Não é possível dizer especificamente qual é o seu problema sem ver o código.

A solução mais simples é manter todos os seus scripts alinhados na parte inferior da página, para que eles não bloqueiem o carregamento do conteúdo HTML durante a execução. Também evita o problema de carregar assincronamente cada script necessário.

Se você tiver uma interação particularmente sofisticada, nem sempre usada, que exija algum tipo de script maior, pode ser útil evitar carregar esse script específico até que seja necessário (carregamento lento).

* scripts carregados $.getScriptprovavelmente não serão armazenados em cache


Para quem pode usar recursos modernos, como o Promiseobjeto, a loadScriptfunção se tornou significativamente mais simples:

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

Esteja ciente de que esta versão não aceita mais um callbackargumento, pois a promessa retornada tratará do retorno de chamada. O que anteriormente teria sido loadScript(src, callback)agora seria loadScript(src).then(callback).

Isso tem o bônus adicional de poder detectar e lidar com falhas, por exemplo, pode-se chamar ...

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

... e lidaria com interrupções de CDN normalmente.

zzzzBov
fonte
Estou prestes a começar a experimentar isso. mas se eu carregar esses scripts em uma página diferente quando redirecionar a página para uma página diferente e essa página tiver os mesmos scripts, eles não deverão demorar para carregar. eles devem estar no cache certo? então eu não deveria implementar essa técnica?
Tono Nam 10/10
1
Alterei e anexei o filho à tag head em vez do corpo ... ..var x = document.getElementsByTagName ('head') [0]; x.appendChild (s);
Tono Nam 10/10
1
@TonoNam, você pode simplesmente usar document.head.appendChild, no entanto, existem alguns problemas de nicho ao adicionar scripts ao <head>; nada que impeça o carregamento e a execução de scripts.
ZzzzBov 12/12/11
1
Eu tive que usar t.parentNode.insertBefore (em oposição a t.parent).
Phil LaNasa
1
@ MuhammadUmer, se você estiver escrevendo uma tag de script adicional document.writeantes do carregamento do documento, o script será executado de forma síncrona como parte do carregamento da página e não incluirá nenhum comportamento de retorno de chamada assíncrono. Se você estiver criando um elemento de script e adicionando-o à página, terá acesso para adicionar ouvintes de eventos a serem acionados de forma assíncrona. tl; dr: depende de como você está carregando o script com JS.
ZzzzBov 30/10/2014
33

O novo atributo 'assíncrono' do HTML5 deve fazer o truque. 'adiar' também é suportado na maioria dos navegadores, se você se importa com o IE.

async - O HTML

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

adiar - o HTML

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

Ao analisar o novo código do bloco de anúncios do adsense, observei o atributo e uma pesquisa me levou aqui: http://davidwalsh.name/html5-async

Donald Porter
fonte
do meu entendimento, depois de ler stackoverflow.com/questions/2774373/… , o asyn não é para carregar, mas para determinar quando executar o javascript contido.
JackDev
9
O atributo adiar faz com que o navegador adie a execução do script até que o documento tenha sido carregado e analisado e esteja pronto para ser manipulado. O atributo async faz com que o navegador para executar o script, logo que possível, mas não para a análise de documentos bloco enquanto o script está sendo baixado
shuseel
1
"o asyn não é para carregar, mas para determinar quando executar o javascript contido" Isso não é contraditório; Em todos os casos, o navegador começará a carregar o script o mais rápido possível. Mas sem async ou deferele bloqueará a renderização enquanto carrega. A configuração asyncdiz para ele não bloquear e executar o script o mais rápido possível. A configuração deferdiz para ele não bloquear, mas aguarde a execução do script até que a página esteja pronta. Normalmente, não há problema em executar scripts o mais rápido possível, desde que eles não tenham nenhuma dependência (por exemplo, jQuery). Se um script tiver dependências em outro, use onLoadpara aguardar.
Stijn de Witt
@StijndeWitt E se Nem assíncrono nem adiado for especificado?
Mohammed Shareef C 11/11
@MohammedShareefC Se você não especificar nenhum, obterá algo parecido com o comportamento de sincronização; o navegador bloqueia o processamento adicional enquanto baixa e executa o script. Tão efetivamente o comportamento padrão é a sincronização. Mas, como isso é prejudicial para o desempenho, os navegadores irão solucionar o máximo possível. Eles podem continuar analisando, carregando e aplicando CSS, etc ... Eles não podem, no entanto, começar a executar o próximo fragmento de script. Então as coisas vão bloquear. Ao definir async, você está dizendo ao navegador que não há problema em avançar e garantirá que ele funcione sozinho.
Stijn de Witt
8

Carreguei os scripts de forma assíncrona (o html 5 possui esse recurso) quando todos os scripts foram carregados, redirecionei a página para index2.html, onde index2.html usa as mesmas bibliotecas. Como os navegadores têm um cache depois que a página é redirecionada para index2.html, o index2.html é carregado em menos de um segundo, porque ele tem tudo o que precisa para carregar a página. Na minha página index.html, também carrego as imagens que pretendo usar para que o navegador as coloque no cache. então meu index.html se parece com:

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

Outra coisa interessante é que posso colocar um carregador na página e, quando a página terminar de carregar, o carregador desaparecerá e, em um fosco de milissegundos, a nova página estará em execução.

Tono Nam
fonte
1
@MohammedShareefC É ironia. Uma página especial apenas para pré-carregar coisas não soa como uma boa ideia sobre SEO. O pré-carregamento é excelente, mas você não precisa redirecionar para alcançá-lo. Basta colocar os links em uma div oculta e o navegador cuidará disso automaticamente. Então não redirecione, apenas renderize a página real. COPY-PASTERS LEIA ISTO : Altere a codificação ISO-88591 para UTF-8 antes de usar qualquer um desses HTML para que o resto de nós possa sair do inferno da codificação em breve. Obrigado!
Stijn de Witt
Por favor, olhe a minha nova resposta: stackoverflow.com/questions/7718935/load-scripts-asynchronously/...
asmmahmud
8

Exemplo do 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
fonte
8

Várias notas:

  • s.async = truenão é muito correto para o doctype HTML5, o correto é s.async = 'async'(na verdade, usar true está correto , graças a amn que apontou no comentário logo abaixo)
  • Usar tempos limite para controlar o pedido não é muito bom e seguro, e você também aumenta o tempo de carregamento para igualar a soma de todos os tempos limite!

Como existe um motivo recente para carregar arquivos de forma assíncrona, mas em ordem, eu recomendaria uma maneira um pouco mais funcional sobre o seu exemplo (remova console.logpara uso em produção :)):

(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
fonte
4
s.async = trueestá correto . A propriedade asyncé especificada explicitamente como um booleano na recomendação HTML 5 do W3C em w3.org/TR/html5/scripting-1.html#attr-script-async . Você está confundindo o async atributo com a asyncpropriedade exposta por objetos que implementam a HTMLScriptElementinterface DOM. De fato, ao manipular o atributo correspondente do elemento por meio de algo como Element.setAttribute, você deve usar, element.setAttribute("async", "async")pois todos os atributos HTML são, antes de tudo, texto.
amn
"Como existe um motivo recente para carregar arquivos de forma assíncrona, mas em ordem, eu recomendaria uma maneira um pouco mais funcional em relação ao seu exemplo [...]". Eu sei que o assíncrono geralmente é "melhor", mas a qual motivo "recente" você está se referindo?
BluE
Mudei 'if (! Files.length) complete ();' adicionando um 'return': 'if (! files.length) {complete (); return;} '
Fil
4

Graças ao HTML5, agora você pode declarar os scripts que deseja carregar de forma assíncrona, adicionando "async" na tag:

<script async>...</script>

Nota: O atributo async é apenas para scripts externos (e deve ser usado apenas se o atributo src estiver presente).

Nota: Existem várias maneiras de executar um script externo:

  • Se assíncrono estiver presente: O script é executado de forma assíncrona com o restante da página (o script será executado enquanto a página continua a análise)
  • Se o assíncrono não estiver presente e o adiamento estiver presente: O script será executado quando a página terminar de analisar
  • Se nenhum assíncrono ou adiamento estiver presente: O script é buscado e executado imediatamente, antes que o navegador continue analisando a página

Veja isto: http://www.w3schools.com/tags/att_script_async.asp

odroz
fonte
2

Gostaria de concluir a resposta do zzzzBov com uma verificação da presença de retorno de chamada e permitir a passagem de 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);
    }
Tibo
fonte
2

Aqui está uma ótima solução contemporânea para o carregamento de script assíncrono, embora ele endereça apenas o script js com async false .

Há um ótimo artigo escrito em www.html5rocks.com - Mergulhe nas águas turvas do carregamento de scripts .

Depois de considerar muitas soluções possíveis, o autor concluiu que a adição de scripts js ao elemento final do corpo é a melhor maneira possível de evitar o bloqueio da renderização da página pelos scripts js.

Nesse meio tempo, o autor adicionou outra boa solução alternativa para aquelas pessoas que estão desesperadas para carregar e executar scripts de forma assíncrona.

Considerando que você tem quatro scripts nomeados script1.js, script2.js, script3.js, script4.js, pode fazê-lo com a aplicação de 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);
});

Agora, Spec diz : Faça o download em conjunto, execute em ordem assim que o download for feito.

Firefox <3.6, o Opera diz: Não tenho idéia do que é essa coisa de "assíncrono", mas acontece que executo scripts adicionados via JS na ordem em que são adicionados.

O Safari 5.0 diz: Eu entendo "assíncrono", mas não entendo configurá-lo como "falso" com JS. Vou executar seus scripts assim que eles chegarem, em qualquer ordem.

O IE <10 diz: Nenhuma idéia sobre "assíncrono", mas existe uma solução alternativa usando "onreadystatechange".

Tudo o resto diz: Eu sou seu amigo, vamos fazer isso de acordo com o livro.

Agora, o código completo com o IE <10 solução 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>');
  }
}
asmmahmud
fonte
1

Uma razão pela qual seus scripts podem ser carregados tão lentamente é se você estivesse executando todos os seus scripts enquanto carregava a página, assim:

callMyFunctions();

ao invés de:

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

Esse segundo bit de script aguarda até que o navegador carregue completamente todo o seu código Javascript antes de começar a executar qualquer um dos seus scripts, fazendo parecer ao usuário que a página foi carregada mais rapidamente.

Se você deseja melhorar a experiência do usuário diminuindo o tempo de carregamento, eu não usaria a opção "tela de carregamento". Na minha opinião, isso seria muito mais irritante do que apenas carregar a página mais lentamente.


fonte
1

Eu sugiro que você dê uma olhada no Modernizr . É uma pequena biblioteca leve que você pode carregar assincronamente seu javascript com recursos que permitem verificar se o arquivo está carregado e executar o script na outra que você especificar.

Aqui está um exemplo de carregamento do 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
fonte
1

Eu escrevi um pequeno post para ajudar com isso, você pode ler mais aqui https://timber.io/snippets/asynchronously-load-a-script-in-the-browser-with-javascript/ , mas eu anexei a classe auxiliar abaixo. Ele aguardará automaticamente o carregamento de um script e retornará um atributo de janela especificado.

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

O uso é assim:

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
fonte
1

Você pode achar este artigo wiki interessante: http://ajaxpatterns.org/On-Demand_Javascript

Explica como e quando usar essa técnica.

tereško
fonte
Infelizmente, o link parece estar morto. :(
Eric Seastrand /
@ Eric é porque esse é um método antigo de carregar dinamicamente o código: D
tereško
0

Bem, x.parentNoderetorna o elemento HEAD, para que você esteja inserindo o script antes da tag head. Talvez esse seja o problema.

Tente em x.parentNode.appendChild()vez disso.

alekop
fonte
0

Confira este https://github.com/stephen-lazarionok/async-resource-loader . Ele tem um exemplo que mostra como carregar JS, CSS e vários arquivos com uma única captura.

Stephen L.
fonte
É possível alterá-lo um pouco para se livrar da dependência jquery
Stephen L.
0

Você pode usar LABJS ou RequreJS

Os carregadores de scripts LABJS, como , RequireJSmelhorarão a velocidade e a qualidade do seu código.

Kaushik
fonte
0

Você já pensou em usar a injeção de busca? Criei uma biblioteca de código-fonte aberto chamada fetch-inject para lidar com casos como esses. Aqui está a aparência do seu carregador usando a 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 compatibilidade com versões anteriores, aproveite a detecção de recursos e faça o retorno aos Elementos DOM de Injeção XHR ou Script, ou simplesmente coloque as tags na página usando document.write.

Josh Habdas
fonte
0

Aqui está minha solução personalizada para eliminar o JavaScript de bloqueio de renderização:

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

Em resumo, ele carrega todos os seus scripts de forma assíncrona e os executa consequentemente.

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

Damjan Pavlica
fonte
1
Não funciona #No 'Access-Control-Allow-Origin' header is present on the requested resource
Abram
1
Esse é um problema do CORS. stackoverflow.com/questions/20035101/…
Damjan Pavlica
0

Aqui está uma pequena função do ES6 se alguém quiser usá-lo no React, por exemplo

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
fonte
-3

Eu sugeriria procurar primeiro reduzir os arquivos e ver se isso oferece um aumento de velocidade grande o suficiente. Se o seu host estiver lento, tente colocar esse conteúdo estático em uma CDN.

Jason Miesionczek
fonte