
Neste tutorial, 
OffscreenCanvas como, usando o 
OffscreenCanvas consegui colocar todo o código para trabalhar com o WebGL e o 
Three.js em um thread de trabalho da Web separado. Isso acelerou o trabalho do site e, em dispositivos fracos, os frisos desapareceram durante o carregamento da página.
O artigo baseia-se na experiência pessoal, quando 
adicionei terra 3D em rotação ao 
meu site e foram necessários 5 pontos de produtividade no 
Google Lighthouse - demais para exibições fáceis. 
O problema
O Three.js oculta muitos problemas complexos de WebGL, mas tem um preço sério - a biblioteca adiciona 
563 KB à sua compilação JS para navegadores (e a arquitetura da biblioteca não permite que o trichashing funcione com eficiência).
Alguns podem dizer que as fotos costumam pesar os mesmos 500 KB - e estarão muito erradas. Cada KB do script tem desempenho muito mais poderoso que o KB da imagem. Para que um site seja rápido, você precisa pensar não apenas na largura do canal e no tempo de atraso - mas também no tempo de operação da CPU do computador para processar arquivos. Em telefones e laptops fracos, o processamento pode demorar mais do que carregar.
 O processamento de 170K JS leva 3,5 segundos contra 0,1 segundo para imagens de 170K - Eddie Osmani
O processamento de 170K JS leva 3,5 segundos contra 0,1 segundo para imagens de 170K - Eddie OsmaniEnquanto o navegador executará 500 KB Three.js, o fluxo da página principal será bloqueado e o usuário verá o friso da interface.
Trabalhadores da Web e telas fora da tela
Há muito tempo temos uma solução para não remover o friso durante o longo prazo dos trabalhadores da JS - web executando o código em um encadeamento separado.
Para que trabalhar com trabalhadores da Web não se torne um inferno de programação multithread, um trabalhador da Web não tem acesso ao DOM. Somente o encadeamento principal funciona com a página HTML. Mas como iniciar o Three.js sem acesso ao DOM, o que requer acesso direto ao 
<canvas> ?
Para fazer isso, existe o 
OffscreenCanvas - ele permite que você passe 
<canvas> para um trabalhador da Web. Para não abrir os portões do inferno multithread, após a transferência, o thread principal perde acesso a este 
<canvas> - apenas um thread funcionará com ele.
Parece que estamos perto do objetivo, mas acontece que apenas o Chrome suporta o 
OffscreenCanvas .
 Suporte para telas para abril de 2019, de acordo com Can I Use
Suporte para telas para abril de 2019, de acordo com Can I UseMas mesmo aqui, diante do principal inimigo do desenvolvedor da web, o suporte ao navegador, não devemos desistir. Nos reunimos e encontramos o último elemento do quebra-cabeça - este é um caso ideal para "melhoria progressiva". No Chrome e em navegadores futuros, removeremos o friso e outros navegadores funcionarão como antes.
Como resultado, precisaremos escrever um arquivo que possa funcionar em dois ambientes diferentes ao mesmo tempo - em um trabalhador da Web e em um fluxo JS principal comum.
Solução
Para ocultar os hacks sob uma camada de açúcar, 
criei uma pequena biblioteca JS de 
tela fora da tela de 400 bytes (!). Nos exemplos, o código o usará, mas vou lhe dizer como ele funciona "sob o capô".
Vamos começar instalando a biblioteca:
 npm install offscreen-canvas 
Precisamos de um arquivo JS separado para o trabalhador da Web - crie um arquivo de montagem separado no Webpack ou Parcel:
  entry: { 'app': './src/app.js', + 'webgl-worker': './src/webgl-worker.js' } 
Os coletores mudam constantemente o nome do arquivo durante a implantação devido aos busters de cache - precisamos escrever o nome em HTML usando a 
tag de pré - 
carregamento . Aqui o exemplo será abstrato, pois o código real dependerá muito dos recursos do seu assembly.
  <link type="preload" as="script" href="./webgl-worker.js"> </head> 
Agora precisamos obter o nó DOM para as 
<canvas> e o conteúdo da tag preload no arquivo JS principal.
 import createWorker from 'offscreen-canvas/create-worker' const workerUrl = document.querySelector('[rel=preload][as=script]').href const canvas = document.querySelector('canvas') const worker = createWorker(canvas, workerUrl) 
createWorker se houver 
canvas.transferControlToOffscreen carregará o arquivo JS no trabalhador da web. E na ausência deste método - como um 
<script> regular.
Crie este 
webgl-worker.js para o trabalhador:
 import insideWorker from 'offscreen-canvas/inside-worker' const worker = insideWorker(e => { if (e.data.canvas) {  
insideWorker verifica se foi carregado dentro de um trabalhador da Web. Dependendo do ambiente, ele lançará diferentes sistemas de comunicação com o thread principal.
A biblioteca executará uma função passada para 
insideWorker para cada nova mensagem do encadeamento principal. Imediatamente após o carregamento, o 
createWorker enviará a primeira mensagem 
{ canvas, width, height } para desenhar o primeiro quadro em 
<canvas> .
 + import { + WebGLRenderer, Scene, PerspectiveCamera, AmbientLight, + Mesh, SphereGeometry, MeshPhongMaterial + } from 'three' import insideWorker from 'offscreen-canvas/inside-worker' + const scene = new Scene() + const camera = new PerspectiveCamera(45, 1, 0.01, 1000) + scene.add(new AmbientLight(0x909090)) + + let sphere = new Mesh( + new SphereGeometry(0.5, 64, 64), + new MeshPhongMaterial() + ) + scene.add(sphere) + + let renderer + function render () { + renderer.render(scene, camera) + } const worker = insideWorker(e => { if (e.data.canvas) { + // canvas  -    —    ,     Three.js + if (!canvas.style) canvas.style = { width, height } + renderer = new WebGLRenderer({ canvas, antialias: true }) + renderer.setPixelRatio(pixelRatio) + renderer.setSize(width, height) + + render() } }) 
Ao portar o código antigo do Three.js para um trabalhador da Web, você poderá ver erros, pois o trabalhador da Web não possui uma API DOM. Por exemplo, não há 
document.createElement para carregar texturas SVG. Portanto, às vezes precisaremos de carregadores diferentes em um trabalhador da Web e dentro de um script regular. Para verificar o tipo de ambiente, temos o 
worker.isWorker :
  renderer.setPixelRatio(pixelRatio) renderer.setSize(width, height) + const loader = worker.isWorker ? new ImageBitmapLoader() : new ImageLoader() + loader.load('/texture.png', mapImage => { + sphere.material.map = new CanvasTexture(mapImage) + render() + }) render() 
Nós desenhamos o primeiro quadro. Mas a maioria das cenas do WebGL deve responder às ações do usuário. Por exemplo, gire a câmera quando o cursor se mover ou desenhe um quadro quando a janela for redimensionada. Infelizmente, o trabalhador da Web não pode ouvir eventos DOM. Precisamos ouvi-los no fluxo principal e enviar mensagens para o web worker.
  import createWorker from 'offscreen-canvas/create-worker' const workerUrl = document.querySelector('[rel=preload][as=script]').href const canvas = document.querySelector('canvas') const worker = createWorker(canvas, workerUrl) + window.addEventListener('resize', () => { + worker.post({ + type: 'resize', width: canvas.clientWidth, height: canvas.clientHeight + }) + }) 
  const worker = insideWorker(e => { if (e.data.canvas) { if (!canvas.style) canvas.style = { width, height } renderer = new WebGLRenderer({ canvas, antialias: true }) renderer.setPixelRatio(pixelRatio) renderer.setSize(width, height) const loader = worker.isWorker ? new ImageBitmapLoader() : new ImageLoader() loader.load('/texture.png', mapImage => { sphere.material.map = new CanvasTexture(mapImage) render() }) render() - } + } else if (e.data.type  
Resultado
Com o 
OffscreenCanvas derrotei frisos no meu site e ganhei 100% de pontos no Google Lighthouse. E o WebGL funciona em todos os navegadores, mesmo sem o suporte do 
OffscreenCanvas .
Você pode dar uma olhada no 
site ativo e no 
código-fonte do thread ou 
trabalhador principal .
 Com o OffscreenCanvas, os óculos do Google Lighthouse aumentaram de 95 para 100
Com o OffscreenCanvas, os óculos do Google Lighthouse aumentaram de 95 para 100