Rastreando armadilhas de desempenho no Vue.js

Quando construímos um jogo ou um aplicativo de uma única página e consultamos o domínio, gera um pouco de memória.

O jogo tinha acabado, mas não pude deixá-lo ir. Ele continuou me incomodando. Foi o Vue.js? Foi o Netlify? Era nosso código hacky? Eu tinha que descobrir.

 

O garoto gamer em mim sempre teve uma vontade eterna de fazer videogames. Recentemente, pude satisfazer esse desejo quando participei do meu primeiro Global Game Jam há alguns meses. Minha equipe e eu construímos um jogo chamado "ZeroDaysLeft" para a web como um único aplicativo de página usando Vue.js. O jogo tinha um tema verde, pensamos no impacto ambiental que o capitalismo tem na terra e queríamos colocar um giro informativo sobre ele. Não há muitos jogos feitos usando o Vue.js. Minha equipe estava um dia atrasada e depois de um jogo literal de pedra-papel-tesoura para escolher nossa estrutura, corremos para codificou e no final de semana começou nosso jogo funcionando. Localmente, tudo funcionou muito bem. Naturalmente, estávamos orgulhosos do nosso código Frankenstein e queríamos compartilhá-lo com o mundo.

 

 

Havia apenas um problema — quando construímos o aplicativo e consultamos o domínio, era um pouco de memória. Ele mal funcionou e todas as máquinas em que tentamos executá-la ficariam no inferno de processamento — até mesmo meu sistema baseado em processador Intel i7 falharia. As restrições de tempo do jogo nos trouxeram de volta à realidade, e decidimos deixar de lado nossos problemas de desempenho de produção para que pudéssemos pelo menos lançar um jogo completo em nossos dispositivos. Como quase todos os projetos "concluídos", esquecemos no dia seguinte.

 

Só que não pude deixá-lo ir. Ele continuou me incomodando. Foi o Vue.js? Foi o Netlify? Era nosso código hacky? Eu tinha que descobrir.

 

Investigando a desaceleração

 

Comecei com um teste rápido usando o Lighthouse. Felizmente, o Firefox tem um complemento de navegador para isso. Isto é o que eu tenho de volta.

 

 

89% não é ruim. Na verdade, comparado com muitos sites amplamente utilizados, é decente. O teste menciona problemas potenciais como o índice de velocidade e as primeiras tintas contentes e significativas. Teoricamente, lidar com isso tornaria a pontuação mais alta, mas não necessariamente melhoraria o enorme problema de desempenho dos aplicativos. Temos alguns ativos de imagem e áudio, embora nenhum deles seja grande o suficiente para causar enforcamento. Poderíamos otimizar demais esses ativos já otimizados, mas isso provavelmente não nos ajudaria em nada.

 

O teste não nos ofereceu nenhuma visão real sobre o que poderia estar causando esse problema de desempenho. Neste momento, eu estou pensando "é Vue?" Não tenho razão para pensar assim, mas seria tolice não verificar. Eu checo o console para o local implantado e ele está em branco. Avisos não são geralmente exibidos em produção. Quando faço o mesmo localmente, sou atingido na cabeça por alguns avisos de Vue.

 

 

Como a maioria dos desenvolvedores, minha opinião sobre os avisos do console é que eles são apenas isso — avisos e não erros — então minha atenção está mais focada em outra coisa. Eu guardo a esperança de que acabar com esses avisos poderia resolver meus problemas de produção. Decidi ir um pouco mais fundo em cada um deles e consertá-los.

 

Todos esses avisos vieram de um componente que criei para exibir opções Cards.vuede modo que o componente pode precisar de muitas regravações.

 

Decidi atacar esses avisos de console em ordem.

 

[Vue warn]: Avoid using non-primitive value as key, use string/number value instead.

 found in 

 --- Cards at src/components/Cards.vue 

Vue.js tem um monte de diretivas que tornam o uso da estrutura mais intuitivo, como v-for, que rapidamente torna uma matriz como uma lista. Quando o usamos, precisamos ter uma :key permitir a reer renderização eficiente dos componentes. No entanto, estávamos usando um objeto como uma chave, que é um valor não primitivo e, portanto, causou esse erro. Decidi usar o index.description como uma nova chave, pois é uma string e faria uma reer renderização mais eficiente sempre que os valores mudassem.

 

[Vue warn]: Duplicate keys detected: '[object Object]'. This may cause an update error.

found in

--- Cards at src/components/Cards.vue

Alterar a :key para uma string-index.description- para o erro anterior corrige este erro de chave duplicado. Só podemos escrever tipos de string para o DOM, então quando passamos um objeto a ser renderizado, ele foi convertido para seu equivalente de string — ou seja, [objeto objeto]— e como essa era anteriormente nossa chave, cada objeto, apesar de manter um valor diferente, seria convertido para [objeto objeto], assim nosso aviso de chave duplicado. Agora que a chave não é um objeto, o aviso desaparece. Então, yay para eficiência. 

 

[Vue warn]: You may have an infinite update loop in a component render function.

found in

--- Cards at src/components/Cards.vue

Para um aviso muito vago, este parece ser o mais importante. Loops infinitos são sinônimos de drenos de memória. A mensagem não nos diz o que pode estar errado. Ele sugere que tem algo a ver com a função render no componente. Talvez com nosso código hacky, estamos acionando atualizações sem parar e isso está ocupando tanto poder de computação que ele trava o navegador e, em seguida, o dispositivo.

 

O aviso pelo menos nos diz para verificar Cards.vueentão meu primeiro pensamento é verificar as propriedades reativas no componente como se poderia estar causando o erro. Propriedades reativas, quando alteradas acionam uma re-renderização.

 

Estamos exibindo dados de index.days e index.description. No entanto, não estamos mudando esses dados. Nós derivamos index da matriz cardInfo 

 

v-for="index in cardInfo.sort(() = Math.random() - 0.7).slice(0,4)"

Usamos este bloco de código para classificar aleatoriamente os elementos na matriz e, em seguida, obter os quatro primeiros elementos para exibir como opções para um jogador escolher. Quando um usuário clica em uma opção, a função effects() é chamada e, além de fazer cálculos sobre como uma ação afeta o estado do jogo, ele usa o protótipo de emenda no cardInfo para remover os quatro elementos principais. 

 

Como ele vai com propriedades reativas como cardInfo em uma estrutura como o Vue que utiliza um DOM virtual, sempre que o valor da propriedade de dados muda, uma re-renderização é acionada. No nosso caso, estamos mudando-o diretamente com o sort() então excluindo elementos apenas para classificá-los novamente. Tudo isso desencadeia re-renderizações "infinitas", assim o aviso. 

 

Decidi mudar a lógica por trás de como os dados estavam sendo filtrados e parar as múltiplas alterações em uma propriedade reativa, cardInfo. Instalei lodash.shuffle e defini uma propriedade computada, shuffledList(), que criaria uma cópia do cardInfo chamado list. Apliquei a operação shuffle a ele e devoltei um resultado "congelado", que seria fatiado para ter quatro cartas exibidas. Usamos Object.freeze() que tornaria o objeto que estamos devolvendo imutável completamente parando todas as re-renderizações. Problema resolvido.

 

Sendo tropeçado pela minha estrutura

 

Para ser honesto, quando comecei minha investigação, achei que teria que otimizar muitos dos meus bens. Ele só vai mostrar o quão cuidadosos temos que ser ao usar muitas das abstrações de estrutura — no Vue especificamente, cada diretiva tem que ser usada corretamente e apenas quando necessário, porque eles definitivamente têm compensações.

 

Isso me fez pensar sobre o que mais eu estava fazendo que poderia adicionar problemas de desempenho desnecessários à minha aplicação. A maioria das estruturas front-end modernas abstraem muito e tornam muito mais fácil para nós fazer aplicações para a web, é importante lembrar os problemas de desempenho subjacentes que podem surgir quando usamos as coisas. Eu uso o Vue.js muito e decidi explorar algumas diretivas que uso sem sequer pensar em implicações de desempenho que eles podem ter na minha aplicação. Três diretivas muito populares, em particular, se destacaram para mim.

 

v-if e v-show

Ambas as diretivas são usadas para renderizar elementos condicionalmente, mas funcionam de forma muito diferente nos bastidores e, claro, como resultado disso, precisam ser usadas de forma diferente. v-if não renderiza seu componente inicialmente e só renderiza componentes quando a condição é verdadeira. Isso significa que se você alternar a visibilidade de um componente várias vezes ele será re renderizado repetidamente. Você não quer usar isso se estiver mudando a visibilidade de um componente várias vezes. Afetará seu desempenho.

 

Uma boa alternativa é v-show Isso tornará seu componente independentemente do uso do CSS, mas só o tornará visível dependendo se a condição é verdadeira ou falsa. Este método tem suas desvantagens, pois não adiará a renderização de componentes não essenciais para um momento em que você realmente precisa deles na tela. Isso funciona bem se sua renderização inicial não for pesada.

 

v-for

Esta diretiva é geralmente usada para renderizar listas de matrizes. Ele tem uma sintaxe especial na forma de item in listonde list é o array de dados de origem e o item é um pseudônimo para o elemento array que está sendo iterado. Por padrão, o Vue adiciona observadores no array de dados de origem que aciona uma re-renderização sempre que uma alteração ocorre. Essa re-renderização constante pode ter um efeito adverso no desempenho do aplicativo. É importante pensar em utilidade, se você só quiser visualizar os objetos, então o Object.freeze() é uma boa solução e pode melhorar drasticamente o desempenho. No entanto, é importante lembrar que você não será capaz de atualizar o componente ou editar os dados do objeto.

 

Fazendo isso e percebendo que o Lighthouse poderia verificar para ver o desempenho dos aplicativos que poderiam possivelmente afetar uma experiência do usuário de uma forma mais direta, fiquei perguntando como rastrear o desempenho do aplicativo nos servidores. Deixamos isso à toa e à suposição de que os desenvolvedores sabem o que estão fazendo e estão usando as melhores práticas? Independentemente disso, toda essa experiência me deixou com uma perspectiva diferente sobre o desempenho do aplicativo de uma única página.

 

 

O Avance Network é uma comunidade fácil de usar que fornece segurança de primeira e não requer muito conhecimento técnico. Com uma conta, você pode proteger sua comunicação e seus dispositivos. O Avance Network não mantém registros de seus dados; portanto, você pode ter certeza de que tudo o que sai do seu dispositivo chega ao outro lado sem inspeção.


Strong

5178 Blogg inlägg

Kommentarer