
Dalam tutorial ini, saya akan 
OffscreenCanvas caranya, menggunakan 
OffscreenCanvas saya berhasil memasukkan semua kode untuk bekerja dengan WebGL dan 
Three.js ke utas pekerja web yang terpisah. Ini mempercepat kerja situs dan pada jalur perangkat yang lemah menghilang selama pemuatan halaman.
Artikel ini didasarkan pada pengalaman pribadi, ketika saya 
menambahkan bumi berputar 3D ke 
situs saya dan butuh 5 poin produktivitas di 
Google Lighthouse - terlalu banyak untuk pamer mudah. 
Masalah
Three.js menyembunyikan banyak masalah WebGL yang kompleks, tetapi memiliki harga yang serius - perpustakaan menambahkan 
563 KB ke JS build untuk browser Anda (dan arsitektur perpustakaan tidak memungkinkan trichashing berfungsi secara efisien).
Beberapa orang mungkin mengatakan bahwa gambar seringkali memiliki berat 500 KB yang sama - dan akan sangat salah. Setiap KB script jauh lebih kuat dalam kinerjanya daripada KB gambar. Agar suatu situs menjadi cepat, Anda tidak hanya perlu memikirkan lebar saluran dan waktu tunda - Anda juga perlu memikirkan waktu pengoperasian CPU komputer untuk memproses file. Pada ponsel dan laptop yang lemah, pemrosesan bisa memakan waktu lebih lama daripada memuat.
 Pemrosesan 170K JS membutuhkan 3,5 detik dibandingkan 0,1 detik untuk gambar 170K - Eddie Osmani
Pemrosesan 170K JS membutuhkan 3,5 detik dibandingkan 0,1 detik untuk gambar 170K - Eddie OsmaniSementara browser akan mengeksekusi 500 KB Three.js, aliran halaman utama akan diblokir dan pengguna akan melihat dekorasi antarmuka.
Pekerja Web dan Kanvas Offscreen
Kami telah lama memiliki solusi untuk tidak menghapus dekorasi selama JS - pekerja web menjalankan kode dalam utas terpisah.
Agar bekerja dengan pekerja web tidak berubah menjadi neraka pemrograman multithreaded, pekerja web tidak memiliki akses ke DOM. Hanya utas utama yang berfungsi dengan laman HTML. Tetapi bagaimana cara memulai Three.js tanpa akses ke DOM, yang membutuhkan akses langsung ke 
<canvas> ?
Untuk melakukan ini, ada 
OffscreenCanvas - memungkinkan Anda untuk mengirimkan 
<canvas> ke pekerja web. Agar tidak membuka gerbang neraka multithreaded, setelah transfer, utas utama kehilangan akses ke 
<canvas> - hanya satu utas yang akan bekerja dengannya.
Sepertinya kami dekat dengan tujuan, tetapi ternyata hanya Chrome yang mendukung 
OffscreenCanvas .
 Dukungan OffscreenCanvas untuk April 2019 sesuai dengan Can I Use
Dukungan OffscreenCanvas untuk April 2019 sesuai dengan Can I UseTetapi bahkan di sini, dalam menghadapi musuh utama pengembang web, dukungan browser, kita tidak boleh menyerah. Kami berkumpul dan menemukan elemen terakhir dari teka-teki - ini adalah kasus yang ideal untuk "peningkatan progresif." Di Chrome dan browser berikutnya, kami akan menghapus dekorasi, dan browser lain akan berfungsi seperti sebelumnya.
Akibatnya, kita perlu menulis satu file yang dapat bekerja di dua lingkungan yang berbeda sekaligus - dalam pekerja web dan dalam aliran JS utama reguler.
Solusi
Untuk menyembunyikan retasan di bawah lapisan gula, saya membuat pustaka JS kecil dengan 
layar 400 byte (!). Dalam contoh, kode akan menggunakannya, tetapi saya akan memberitahu Anda cara kerjanya "di bawah tenda."
Mari kita mulai dengan menginstal perpustakaan:
 npm install offscreen-canvas 
Kami memerlukan file JS terpisah untuk pekerja web - buat file rakitan terpisah di Webpack atau Paket:
  entry: { 'app': './src/app.js', + 'webgl-worker': './src/webgl-worker.js' } 
Para kolektor akan terus-menerus mengubah nama file selama penyebaran karena penghilang cache - kita perlu menulis nama dalam HTML menggunakan 
tag preload . Di sini contohnya akan abstrak, karena kode aktual akan sangat tergantung pada fitur-fitur perakitan Anda.
  <link type="preload" as="script" href="./webgl-worker.js"> </head> 
Sekarang kita perlu mendapatkan simpul DOM untuk 
<canvas> dan konten tag preload di file JS utama.
 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 jika ada 
canvas.transferControlToOffscreen memuat file JS ke dalam pekerja web. Dan dengan tidak adanya metode ini - seperti 
<script> .
Buat 
webgl-worker.js untuk pekerja:
 import insideWorker from 'offscreen-canvas/inside-worker' const worker = insideWorker(e => { if (e.data.canvas) {  
insideWorker memeriksa untuk melihat apakah telah dimuat di dalam pekerja web. Tergantung pada lingkungannya, ia akan meluncurkan sistem komunikasi yang berbeda dengan utas utama.
Perpustakaan akan menjalankan fungsi yang diteruskan ke 
insideWorker untuk setiap pesan baru dari utas utama. Segera setelah memuat, 
createWorker akan mengirim pesan pertama 
{ canvas, width, height } untuk menggambar bingkai pertama di 
<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() } }) 
Saat porting kode lama Anda untuk Three.js ke pekerja web, Anda mungkin melihat kesalahan, karena pekerja web tidak memiliki DOM API. Misalnya, tidak ada 
document.createElement untuk memuat tekstur SVG. Jadi, kadang-kadang kita akan membutuhkan loader yang berbeda dalam pekerja web dan di dalam skrip biasa. Untuk memeriksa jenis lingkungan, kami memiliki 
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() 
Kami menggambar bingkai pertama. Tetapi sebagian besar adegan WebGL harus menanggapi tindakan pengguna. Misalnya, putar kamera ketika kursor bergerak atau menggambar bingkai ketika jendela diubah ukurannya. Sayangnya, pekerja web tidak dapat mendengarkan acara DOM. Kita perlu mendengarkan mereka di arus utama dan mengirim pesan ke pekerja web.
  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  
Hasil
Dengan 
OffscreenCanvas saya mengalahkan 
OffscreenCanvas di situs saya dan mendapat 100% poin di Google Lighthouse. Dan WebGL bekerja di semua browser, bahkan tanpa dukungan 
OffscreenCanvas .
Anda dapat melihat 
situs langsung dan 
kode sumber utas atau 
pekerja utama .
 Dengan OffscreenCanvas, kacamata Google Lighthouse naik dari 95 menjadi 100
Dengan OffscreenCanvas, kacamata Google Lighthouse naik dari 95 menjadi 100