Veja o processo de renderização do navegador e a otimização de desempenho a partir de 7 perguntas da entrevista

Prefácio

Na era da Internet móvel, os usuários têm requisitos cada vez maiores para a velocidade de abertura de páginas da web. A pesquisa do Departamento de experiência do usuário do Baidu mostra que a relação entre a taxa de abandono de página e o tempo de abertura da página é mostrada na figura abaixo.

Insira a descrição da imagem aqui

De acordo com os resultados da pesquisa do Departamento de Experiência do Usuário do Baidu, o tempo de carregamento da página que os usuários comuns esperam e podem aceitar é de 3 segundos. Se o tempo de carregamento da página for muito lento, os usuários perderão a paciência e decidirão sair.

Sendo a primeira tela voltada para o usuário, a importância da primeira tela é evidente. Otimizar a experiência do usuário é uma das coisas em que precisamos nos concentrar no desenvolvimento de front-end.

Neste artigo, falaremos sobre o processo de renderização do navegador e otimização de desempenho por meio de 8 perguntas de entrevista.

Vamos primeiro responder a essas 8 perguntas para entender o processo de renderização do navegador e daremos soluções mais tarde ~

  • Por que o Javascript é de thread único?
    Estilo negrito

  • Por que o JS bloqueia o carregamento da página?

  • O carregamento do css causará bloqueio?

  • A diferença entre DOMContentLoaded e load?

  • O que é CRP, ou seja, Critical Rendering Path? Como otimizar?

  • Qual é a diferença entre defer e async?

  • Fale sobre refluxo e redesenho do navegador?

Processo e discussão

Processo e thread são os conceitos básicos do sistema operacional.

进程是 CPU 资源分配的最小单位(是能拥有资源和独立运行的最小单位)。

线程是 CPU 调度的最小单位(是建立在进程基础上的一次程序运行单位)。

Os sistemas operacionais modernos podem executar várias tarefas ao mesmo tempo, por exemplo, você pode ouvir música enquanto navega na Internet com um navegador.

对于操作系统来说,一个任务就是一个进程,Por exemplo, abrir um navegador inicia um processo do navegador e abrir um Word inicia um processo do Word.

Alguns processos fazem mais de uma coisa ao mesmo tempo, como o Word, que pode realizar digitação, correção ortográfica, impressão e outras coisas ao mesmo tempo.在一个进程内部,要同时做多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程。

Como cada processo deve fazer pelo menos uma coisa, um processo tem pelo menos um thread. O sistema aloca memória independente para cada processo, de forma que o processo tenha seus próprios recursos independentes. Cada thread no mesmo processo compartilha o espaço de memória do processo (incluindo segmentos de código, conjuntos de dados, heaps, etc.).

Para usar uma metáfora vívida, um processo é como uma fábrica de produção com uma fronteira, e um fio é como um funcionário da fábrica, que pode fazer suas próprias coisas ou cooperar uns com os outros para fazer a mesma coisa.

Ao iniciarmos uma aplicação, o computador criará um processo, o sistema operacional alocará uma parte da memória para o processo, todo o estado da aplicação ficará armazenado nesta memória.

O aplicativo também pode criar vários threads para auxiliar o trabalho, esses threads podem compartilhar os dados nesta parte da memória. Se o aplicativo for fechado, o processo será encerrado e o sistema operacional liberará a memória relacionada.

Insira a descrição da imagem aqui

Arquitetura multiprocessos do navegador

Um bom programa geralmente é dividido em vários módulos independentes e cooperando entre si, assim como os navegadores.

Veja o Chrome como um exemplo. Ele consiste em vários processos, cada um com suas próprias responsabilidades principais. Eles cooperam entre si para concluir a função geral do navegador.

Cada processo contém vários threads, e vários threads em um processo também trabalharão juntos para completar as responsabilidades do processo.

O Chrome adota uma arquitetura de multiprocessos, e há um processo de navegador na camada superior para coordenar outros processos do navegador.Insira a descrição da imagem aqui

vantagem

Como uma página de nova guia é aberta por padrão e um novo processo é criado, um travamento da página de uma única guia não afetará todo o navegador.

Da mesma forma, o travamento de plug-ins de terceiros não afetará todo o navegador.

O multiprocesso pode aproveitar ao máximo as vantagens do multi-core de CPU moderno.

É conveniente usar o modelo sandbox para isolar processos como plug-ins e melhorar a estabilidade do navegador.

Desvantagem

O sistema aloca memória, CPU e outros recursos para o processo recém-aberto do navegador, de forma que o consumo de recursos de memória e CPU seja maior.

No entanto, o Chrome fez um bom trabalho na liberação de memória. A memória básica pode ser liberada rapidamente para que outros programas sejam executados.

Os principais processos e responsabilidades do navegador

Insira a descrição da imagem aqui

主进程Browser Process

Responsável pela exibição e interação da interface do navegador. Gestão de cada página, criação e destruição de outros processos. Gerenciamento de recursos de rede, download, etc.

第三方插件进程 Plugin Process

Cada tipo de plug-in corresponde a um processo e é criado apenas quando o plug-in é usado.

GPU 进程 GPU Process

Apenas um, no máximo, usado para desenho 3D, etc.

渲染进程 Renderer Process

É chamado de processo de renderização do navegador ou kernel do navegador, que é multi-threaded internamente. Principalmente responsável pela renderização de página, execução de script, tratamento de eventos, etc. (Este artigo se concentra na análise)

渲染进程 Renderer Process

O processo de renderização do navegador é multi-thread, vamos ver quais threads ele tem:

Insira a descrição da imagem aqui

1 thread de renderização GUI

  • Responsável por renderizar a interface do navegador, analisar HTML, CSS, construir a árvore DOM e a árvore RenderObject, layout e desenho, etc.

  • Quando a interface precisar ser repintada ou refluída devido a alguma operação, o thread será executado.

  • Observe que o encadeamento de renderização da GUI e o encadeamento do mecanismo JS são mutuamente exclusivos. Quando o mecanismo JS é executado, o encadeamento da GUI será suspenso (equivalente a ser congelado) e a atualização da GUI será salva em uma fila até que o mecanismo JS esteja ocioso. .

2 thread do motor JS

  • O mecanismo Javascript, também conhecido como kernel JS, é responsável pelo processamento de programas de script Javascript. (Por exemplo, motor V8)
  • O encadeamento do mecanismo JS é responsável por analisar scripts Javascript e executar códigos.
  • O mecanismo JS está aguardando a chegada de tarefas na fila de tarefas e, em seguida, processando-as. Há apenas um thread JS executando o programa JS em uma página Tab (processo de renderização) a qualquer momento.
  • Observe que o encadeamento de renderização da GUI e o encadeamento do mecanismo JS são mutuamente exclusivos, portanto, se o tempo de execução do JS for muito longo, isso fará com que a renderização da página seja inconsistente e bloqueie a renderização da página.

3 Tópico de gatilho de evento

  • Pertence ao navegador em vez do mecanismo JS, usado para controlar o loop de eventos (compreensivelmente, o próprio mecanismo JS está muito ocupado e requer que o navegador abra um encadeamento para auxiliar)
  • Quando o mecanismo JS executa blocos de código, como setTimeOut (também de outros threads do kernel do navegador, como cliques do mouse, solicitações assíncronas AJAX, etc.), a tarefa correspondente será adicionada ao thread de evento
  • Quando o evento correspondente atende à condição de gatilho e é disparado, o encadeamento adicionará o evento ao final da fila para ser processado e aguardará que o mecanismo JS processe
  • Observe que, devido ao relacionamento de thread único de JS, os eventos nessas filas pendentes devem ser enfileirados para processamento pelo mecanismo JS (executado apenas quando o mecanismo JS está ocioso)

4. Thread do gatilho de tempo

  • O thread onde o legendário setInterval e setTimeout estão localizados
  • O contador do cronômetro do navegador não é contado pelo mecanismo JavaScript (porque o mecanismo JavaScript é de thread único, se estiver em um estado de thread bloqueado, afetará a precisão do tempo)
  • Portanto, um thread separado é usado para cronometrar e acionar o tempo (após o tempo ser concluído, ele é adicionado à fila de eventos e executado depois que o mecanismo JS está ocioso)
  • Observe que o W3C estipula no padrão HTML que um intervalo de tempo inferior a 4 ms em setTimeout é contado como 4 ms.

5 thread de solicitação assíncrona de http

  • Depois que XMLHttpRequest é conectado, uma nova solicitação de thread é aberta por meio do navegador.
  • Quando uma mudança de estado é detectada, se uma função de retorno de chamada é definida, o encadeamento assíncrono irá gerar um evento de mudança de estado e colocar esse retorno de chamada na fila de eventos. Em seguida, executado pelo mecanismo JavaScript.

6 Suplemento: web worker

Os Web workers são uma forma de o javascript permitir multithreading. É outro segmento de computação. Consulte o log de Ruan Dashen para obter detalhes ;

Resumo: O js que normalmente usamos é apenas um thread no processo do navegador. Muitas operações assíncronas, gerenciamento de retorno de chamada de evento, etc., precisam ser processadas por threads adicionais e, em seguida, o thread javascript é notificado na forma de eventos.

Processo de renderização do navegador

Se você quiser falar sobre o que acontece desde a entrada da url até o carregamento da página, é interminável ... Aqui falamos apenas sobre o processo de renderização do navegador.
Insira a descrição da imagem aqui

  • Analisa os arquivos HTML, constrói a árvore DOM e o processo do navegador principal é responsável pelo download dos arquivos CSS

  • O arquivo CSS é baixado, o arquivo CSS é analisado em uma estrutura de dados em forma de árvore e, em seguida, combinado com a árvore DOM para formar uma árvore RenderObject

  • Layout da árvore RenderObject (Layout / reflow), responsável pelo cálculo do tamanho e posição dos elementos na árvore RenderObject

  • Desenhe a árvore RenderObject (pintar), desenhe as informações de pixel da página

  • O processo do navegador principal passa as camadas padrão e as camadas compostas para o processo da GPU, e o processo da GPU compõe cada camada (composta) e, finalmente, exibe a página

responda

1.为什么 Javascript 要是单线程的 ?

Isso se deve à missão da linguagem de script Javascript! JavaScript lida com a interação do usuário na página e manipula a árvore DOM e a árvore de estilo CSS para apresentar aos usuários uma experiência interativa rica e dinâmica e processamento interativo da lógica do servidor.

Se o JavaScript for uma maneira multiencadeada de manipular esses DOMs da UI, pode haver conflitos nas operações da UI.

Se o Javascript for multithread, sob interação multithread, o nó DOM na UI pode se tornar um recurso crítico.

Supondo que haja dois encadeamentos operando um DOM ao mesmo tempo, um é responsável pela modificação e o outro é responsável pela exclusão, então o navegador deve decidir como efetivar o resultado da execução do encadeamento.

Claro, podemos resolver os problemas acima travando. Mas para evitar a maior complexidade causada pela introdução de bloqueios, o Javascript escolheu a execução de thread único no início.

2. 为什么 JS 阻塞页面加载 ?

Como o JavaScript pode manipular o DOM, se você modificar esses atributos do elemento enquanto renderiza a interface (ou seja, o encadeamento JavaScript e o encadeamento da IU são executados ao mesmo tempo), os dados do elemento obtidos antes e depois do encadeamento de renderização podem ser inconsistentes.

Portanto, para evitar resultados imprevisíveis de renderização, as configurações do navegadorGUI 渲染线程与 JavaScript 引擎为互斥的关系。

当 JavaScript 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行。

Do exposto, podemos inferir que, devido ao relacionamento mutuamente exclusivo entre o thread de renderização da GUI e o thread de execução JavaScript,

Quando o navegador está executando o programa JavaScript, o encadeamento de renderização da GUI será armazenado em uma fila e não continuará até que o programa JS seja executado.

Portanto, se o tempo de execução do JS for muito longo, isso fará com que a renderização da página seja inconsistente, levando à sensação de bloqueio da renderização e do carregamento da página.

3. css 加载会造成阻塞吗 ?

No processo de renderização do navegador acima, podemos ver:

A análise DOM e a análise CSS são dois processos paralelos, portanto CSS 加载不会阻塞 DOM 的解析.

No entanto, como a árvore de renderização depende da árvore DOM e da árvore CSSOM,

Portanto, ele deve esperar até que a árvore CSSOM seja construída, ou seja, o recurso CSS seja carregado (ou o recurso CSS não carregue) antes de renderizar.

Portanto,CSS 加载会阻塞 Dom 的渲染。

Como o JavaScript pode manipular os estilos DOM e CSS, se você modificar esses atributos do elemento enquanto renderiza a interface (ou seja, o thread JavaScript e o thread da IU são executados ao mesmo tempo), os dados do elemento obtidos antes e depois do thread de renderização podem ser inconsistentes.

Portanto, para evitar resultados imprevisíveis de renderização, as configurações do navegador GUI 渲染线程与 JavaScript 引擎为互斥的关系。

Portanto, a folha de estilo será carregada e executada antes que o js subsequente seja executado, entãocss 会阻塞后面 js 的执行。

4 DOMContentLoaded 与 load 的区别 ?

  • Quando o evento DOMContentLoaded é disparado, somente depois que a análise do DOM é concluída, as folhas de estilo e as imagens não são incluídas. Mencionamos anteriormente que o carregamento de CSS bloqueará a renderização de Dom e a execução de js posteriormente, e js bloqueará a análise de Dom, portanto, podemos concluir que,
    quando não há script no documento, o navegador pode acionar o evento DOMContentLoaded após analisar o documento. Se o documento contiver um script, o script bloqueará a análise do documento e o script precisará ser executado após a construção do CSSOM. Em qualquer caso, o acionamento de DOMContentLoaded não precisa esperar que as imagens e outros recursos sejam carregados.

  • Quando o evento onload é disparado, todos os DOM, folhas de estilo, scripts, imagens e outros recursos da página foram carregados.

  • DOMContentLoaded -> carregar。

5. 什么是 CRP,即关键渲染路径(Critical Rendering Path)? 如何优化 ?

O caminho de renderização crítico é uma série de etapas pelas quais o navegador passa para converter HTML CSS JavaScript em conteúdo de pixel renderizado na tela. Este é o processo de renderização do navegador que mencionamos acima.

Para concluir a primeira renderização o mais rápido possível, precisamos minimizar as três variáveis ​​a seguir:

  • Número de recursos críticos: Recursos que podem impedir a primeira renderização da página da web.

  • Comprimento do caminho crítico: o número de viagens de ida e volta ou o tempo total necessário para obter todos os recursos críticos.

  • Seção de palavra-chave: o número total de bytes necessários para obter a primeira renderização de uma página da web é equivalente à soma dos tamanhos de arquivo de todos os recursos principais.

1. Otimize o DOM

  • Exclua códigos e comentários desnecessários, incluindo espaços, e tente minimizar os arquivos.
  • Você pode usar GZIP para compactar arquivos.
  • Combinado com arquivos de cache HTTP.

2. Otimize o CSSOM

Reduzir, compactar e armazenar em cache são igualmente importantes. Para CSSOM, mencionamos anteriormente que isso impedirá a renderização da página, portanto, podemos considerar a otimização desse aspecto.

  • Reduza o número de elementos CSS importantes
  • Quando declaramos folhas de estilo, preste muita atenção aos tipos de consultas de mídia, pois afetam muito o desempenho do CRP.

3. Otimize o JavaScript

Quando o navegador encontra a tag de script, ele impede que o analisador continue a operar. Até que o CSSOM seja construído, o JavaScript será executado e continuará a concluir o processo de construção do DOM.

  • async: Depois de adicionarmos o atributo async à tag de script, o navegador continuará a analisar o DOM ao encontrar essa tag de script, e o script não será bloqueado pelo CSSOM, ou seja, não bloqueará o CRP.

  • Adiar: a diferença com async é que o script precisa ser executado depois que o documento é analisado (antes do evento DOMContentLoaded), enquanto async permite que o script seja executado em segundo plano durante a análise do documento (o processo de download dos dois não bloqueará o DOM, mas a execução irá).

  • Quando nosso script não modifica o DOM ou CSSOM, é recomendado usar async.

  • Pré-carga-pré-carga e pré-busca.

  • Pré-resolução-dns-pré-busca de DNS.

6. defer 和 async 的区别 ?

Quando o navegador encontra o script de script:

  • <script src="script.js">
    Sem adiar ou assíncrono, o navegador carregará e executará o script especificado imediatamente. "Imediatamente" refere-se ao elemento do documento sob a tag do script antes da renderização, ou seja, ele não espera pelo elemento do documento carregado subsequentemente e o carrega quando é lido. E execute.

  • <script async src="script.js">

Com async, o processo de carregamento e renderização de elementos de documento subsequentes será paralelo ao carregamento e execução de script.js (assíncrono).

  • <script defer src="myscript.js">
    Com o adiamento, o processo de carregamento de elementos de documento subsequentes será executado em paralelo com o carregamento de script.js (assíncrono), mas a execução de script.js deve ser concluída após todos os elementos serem analisados ​​e antes que o evento DOMContentLoaded seja acionado.

Do ponto de vista prático, a melhor prática é lançar todos os scripts antes, porque esta é a única opção de otimização para navegadores antigos.Este método pode garantir que todos os outros elementos que não sejam scripts possam ser obtidos na velocidade mais rápida. Carregando e analisando.

A seguir, vamos olhar para uma imagem:Insira a descrição da imagem aqui

A linha azul representa a leitura da rede e a linha vermelha representa o tempo de execução, sendo que ambas são para scripts. A linha verde representa a análise de HTML.

Portanto, podemos concluir:

  • Adiar e assíncrono são iguais na leitura da rede (download), ambos são assíncronos (em comparação com a análise de HTML)

  • A diferença entre os dois é quando o script é executado após o download do script. Obviamente, adiar é o que mais se aproxima de nossos requisitos para o carregamento e a execução do script do aplicativo.

  • Em relação ao adiamento, a parte inacabada dessa figura é que ele executa scripts na ordem de carregamento, que devem ser usados ​​com sabedoria

  • async é um mestre da execução fora de ordem, de qualquer forma, para ele, o carregamento e a execução dos scripts estão lado a lado, então não importa a ordem que você declarar, ele será executado imediatamente enquanto estiver carregado.

  • Pense nisso com cuidado, o async não é muito útil para scripts de aplicativos, porque não considera dependências (até mesmo a execução de ordem mais baixa), mas não depende de nenhum script ou scripts que não dependem de nenhum script. Muito apropriado

7.谈谈浏览器的回流与重绘

回流必将引起重绘,重绘不一定会引起回流。

Refluxo

Quando o tamanho, a estrutura ou algumas propriedades de alguns ou todos os elementos na árvore de renderização mudam, o processo de re-renderizar parte do navegador ou todo o documento é chamado de refluxo.

Operações que causarão refluxo:

Primeira renderização da página

O tamanho da janela do navegador mudou

O tamanho ou a posição do elemento muda. O conteúdo do elemento muda (número do texto ou tamanho da imagem, etc.)

Mudanças no tamanho da fonte do elemento

Adicionar ou remover elementos DOM visíveis

Ative as pseudo classes CSS (por exemplo:: hover)

Consulte certas propriedades ou chame certos métodos

Algumas propriedades e métodos comumente usados ​​que causarão refluxo:

clientWidth、clientHeight、clientTop、clientLeft

offsetWidth、offsetHeight、offsetTop、offsetLeft

scrollWidth、scrollHeight、scrollTop、scrollLeft

scrollIntoView()、scrollIntoViewIfNeeded()

getComputedStyle()

getBoundingClientRect()

scrollTo()

Repintar

Quando a alteração do estilo do elemento na página não afeta sua posição no fluxo do documento (por exemplo: cor, cor de fundo, visibilidade, etc.), o navegador atribuirá o novo estilo ao elemento e o redesenhará. Este processo é chamado de redesenho. pintado.

Impacto no desempenho

回流比重绘的代价要更高。

Às vezes, mesmo que apenas um único elemento seja refluído, seu elemento pai e quaisquer elementos que o seguem serão refluídos. Os navegadores modernos otimizarão as operações de refluxo ou redesenho frequentes: o navegador manterá uma fila e colocará todas as operações que causam refluxo e redesenho na fila. Se o número de tarefas na fila ou o intervalo de tempo atingir um limite, O navegador esvaziará a fila e executará um processo em lote, que pode transformar vários refluxos e redesenhos em um.

Quando você acessa as seguintes propriedades ou métodos, o navegador limpa imediatamente a fila:

clientWidth、clientHeight、clientTop、clientLeft


offsetWidth、offsetHeight、offsetTop、offsetLeft


scrollWidth、scrollHeight、scrollTop、scrollLeft


width、height


getComputedStyle()


getBoundingClientRect()

Como pode haver operações na fila que afetam os valores de retorno dessas propriedades ou métodos, mesmo que as informações que você deseja obter não estejam relacionadas às alterações causadas pelas operações na fila, o navegador forçará o esvaziamento da fila para garantir que o valor obtido seja o mais preciso .

Como evitar

CSS

  • Evite usar o layout da tabela.

  • Altere a classe no final da árvore DOM tanto quanto possível.

  • Evite definir vários estilos embutidos.

  • Aplique efeitos de animação a elementos cujo atributo de posição seja absoluto ou fixo.

  • Evite usar expressões CSS (por exemplo: calc ()).

Javascript

  • Para evitar a manipulação frequente de estilos, é melhor reescrever o atributo de estilo uma vez ou definir a lista de estilos como classe e alterar o atributo de classe uma vez.

  • Evite operar frequentemente o DOM, crie um documentFragment, aplique todas as operações DOM nele e, finalmente, adicione-o ao documento.

  • Você também pode definir display: none para o elemento primeiro e, em seguida, exibi-lo após o término da operação. Como as operações DOM realizadas em elementos cujo atributo de exibição é nenhum, não causarão refluxo e redesenho.

  • Evite ler frequentemente os atributos que causarão refluxo / redesenho. Se você realmente precisar usá-los várias vezes, use uma variável para armazená-los em cache.

  • Use o posicionamento absoluto para o elemento com animação complexa para torná-lo fora do fluxo do documento, caso contrário, ele causará refluxo frequente do elemento pai e dos elementos subsequentes.

Acho que você gosta

Origin blog.csdn.net/qq_29722281/article/details/105710839
Recomendado
Clasificación