1. Este site usa cookies. Ao continuar a usar este site está a concordar com o nosso uso de cookies. Saber Mais.
  2. Remover anúncio

RequestAnimationFrame e optimização de web-site

Discussão em 'Web Development' iniciada por Filipe_O, 19 de Abril de 2018. (Respostas: 7; Visualizações: 321)

  1. Filipe_O

    Filipe_O Power Member

    Boas pessoal!

    Estou a tirar um curso online de front-end e neste momento estou num capitulo dedicado á optimização de sites. Capitulo este que se tem revelado de particular dificuldade.

    Para avaliar a performance do site em questão, eu utilizo o inspector do google chrome.
    Acontece que, os resultados que obtenho não me permitem tirar uma ilação solida de boa ou má performance após efectuar algumas alterações, por exemplo:

    1. No separador de performance carrego no botão de record e actualizo a página para saber o tempo que a mesma demorou a carregar. Umas vezes o resultado é 1100ms, outras é 1600ms, chega até 2100 ms, e eu fico sem saber se determinada alteração que fiz foi benéfica ou não. Para quem, tem de resolver problemas de optimização de sites, isto também vos acontece?
    2. No curso aprendi que efectuar alterações visuais através de RequestAnimationFrame é benéfico para o desempenho. Eu tenho um for loop de 0 a 100 em que em cada iteração é adicionado um elemento á DOM. Experimentei trocar o for por um if e colocar tudo dentro de um RAF. Para testar o desempenho, mantive o for antigo e coloquei um console.log para obter o tempo demorado com RAF e sem RAF. Com RAF cada iteração era consideravelmente mais rápida mas depois no separador do performance, a duração total da fase de script aumentava brutalmente. Como pode ser isto possível, se cada iteração é mais rápida?
    Obrigado e desculpem a tese de mestrado
     
  2. xbostonx

    xbostonx Power Member

    1. Para carregamentos da página não terás mais interesse na tab de Network, para perceberes que ficheiros carregam mais depressa ou devagar (assumindo que tens o disable cache ligado) ?
    Estás a usar que framework JS e que ferramenta de tooling? Isto são coisas geralmente otimizadas a esse nível, com minification/uglifying, code splitting para separares a tua app em vários bundles que só carregam quando necessário, etc.
    Se o que te interessa é atingires mais cedo o First Meaningful Paint (tempo que demoras a ver conteúdo pela primeira vez) tens também opções como SSR (Server Side Rendering).

    2. Podes postar o código antigo e novo para tentar perceber melhor?

    Não sou grande expert em otimização mas posso tentar raciocinar contigo.

    Por curiosidade, estás a iniciar-te em frontend e já vais em tópicos avançados como este? Claro que é importante saber estes conceitos, mas olha que há muita sopa para comer antes sequer disto ser relevante... Este tópico tem interesse para quem já trabalha em ambiente empresarial/aplicações grandes, em que há efetivamente problemas destes para resolver.

    BTW parabéns pelas tuas perguntas, finalmente uma oportunidade para discutir webdev a sério em vez de esparguete de PHP que de webdev não tem nada.
     
  3. Filipe_O

    Filipe_O Power Member

    Framework de JS apenas utilizo jQuery. Ferramenta de tooling, não sei.
    Sim, pretendo atingir a primeira frame em menos de 1 segundo. Quanto ao Server Side Rendering, fica como ótima referência para eu pesquisar mas neste exercício não se aplica. Obrigado na mesma.

    Código:
          for (var i = 0; i < count; i++) {
            var story = document.createElement('div');
            story.id = 's-' + stories[i];
            story.classList.add('story');
            main.appendChild(story);
            APP.Data.getStoryById(stories[i], onStoryData.bind(this, stories[i]));
          }
    É neste pedaço que código. Cada iteração é executada mais rapidamente com RAF, mas como um todo, é executado mais lentamente.
    Já experimentei colocar num web-worker; adicionar as div's a um document fragment dentro do array, e fora do array adicionar o documentFragment ao body ... e nada.
    E este problema sucedesse igualmente com a função despoletada em cada iteração.

    Nota: Talvez a sugestão mais pertinente a este código seria adicionar os elementos a um array,por exemplo, dentro do loop e fora do loop adicionar o array de elementos ao body.
    Mas são 100 elementos e não é comportável esperar tanto tempo para adicionar todos os elementos á pagina.
    A mim ensinaram-me que mais vale quem quer do que quem pode ;)

    Estou a finalizar o curso. Daí que já esteja num tópico mais avançado.
    Estou apenas a fazer por crescer na área :).
    Sim, há muito mais para além de sintaxe.
     
    Última edição: 20 de Abril de 2018
  4. xbostonx

    xbostonx Power Member

    Poderás ter um ganho marginal ao user o `requestAnimationFrame` porque tens a garantia que a iteração seguinte (próxima chamada ao callback) vai ocorrer antes do repaint seguinte, coisa que não tens com o ciclo for que pode interromper um repaint a meio e forçar o browser a começar de novo. Mas parece-me que o ganho será marginal.

    Não podes fazer acessos à DOM através dum WebWorker, é uma thread. Quanto muito podias construir um array de nodes que depois passas ao main process para adicionar à DOM. Parece-me super rebuscado para 100 elementos, que não é nada.

    O que podes fazer é por o teu WebWorker a fazer fetch das stories (idealmente vais buscá-las em batch e não individualmente) e assim que tiveres os dados passas ao main, e aí sim no main invocas o `requestAnimationFrame` uma vez para adicionar os elementos à DOM.

    Esse método de fazer fetch da story tá super manhoso btw, cheio de mutability. Os métodos devem ser o mais puros possíveis, i.e. não alterar o estado dum objeto/array implicitamente.

    ex:
    Código:
          for (var i = 0; i < count; i++) {
            APP.Data.getStoryById(stories[i]).  //Promise que retorna uma story individual que inclui o campo id da story
              .then(data => {
                  let story = document.createElement('div');
                  story.classList.add('story');
                  let result = Object.assign({}, story, data);
                  main.appendChild(result);
               });
          }
    
     
  5. xbostonx

    xbostonx Power Member

    Deverias também usar métodos funcionais em vez de ciclos for imperativos, p.exemplo
    Código:
    stories.forEach((element, index) => {})
    Again, recomendo a não te matares muito com a performance nem a meteres os carros à frente dos bois, como te disse tens muita sopa para comer até teres que te preocupar com isso. Foca-te em escrever bom código legível e sem side-effects para já ;)
     
  6. Filipe_O

    Filipe_O Power Member

    Ok, obrigado.
    Vou estudar o teu código e tentar colocar o APP.DATA como web-worker.

    Em relação ao foreach, recomendas por uma questão de convenção?

    Obrigado pelas ajudas :)
     
  7. xbostonx

    xbostonx Power Member

    Hmm não, porque um ciclo for te obriga a ter variáveis de controlo como o "count" e o "i" que só introduzem entropia no código.

    Nesse caso na verdade não usaria um forEach à partida, mudava esse método e a API para ir buscar as stories todas que quero duma só vez, e depois só tinha de resolver uma promise em vez de múltiplas, para além de reduzir os pedidos HTTP a apenas 1, que é de longe muito mais eficiente.

    Procura e lê sobre Functional JavaScript.
     
  8. Filipe_O

    Filipe_O Power Member

    Depois de algum trabalho, desenvolvi a minha solução:
    • (Main Thread) Crio todos os elementos na DOM com a "ajuda" de RAF
    • (Main Thread) Envio um array com todos os id's ao Web Worker
    • (Web Worker) Faz uma iteração pelo array de id's em cada iteração devolve o id e a info correspondente (details)
    • (Main Thread) em cada devolução de dados, executa uma função que dará "vida" á informação recebida, para cada id.

    Código:
        function loadStoryAnimation() {
          if (i < stories.length) {
            var story = document.createElement('div');
            story.id = 's-' + stories[i];
            story.classList.add('story');
            main.appendChild(story);
            i++;
            requestAnimationFrame(loadStoryAnimation);
          }
        }
        requestAnimationFrame(loadStoryAnimation);
    
        dataWorker.postMessage([stories, 2]);
        dataWorker.onmessage = function(e) {
          key = e.data[0];
          details = e.data[1];
          onStoryData(key, details);
        }
    Têm alguma sugestão quanto a melhorar a performance do código?
     

Partilhar esta Página