Pekerja perkhidmatan ialah teknologi yang hebat. Anda mungkin mengenalinya berhubung dengan istilah Aplikasi Web Progresif (PWA), supaya sesuatu yang biasanya kelihatan pada penyemak imbas boleh "dipasang" dalam OS, dan boleh dibuka seperti aplikasi asli, dan dinyahpasang seperti aplikasi asli, dan kelihatan seperti aplikasi asli di sekeliling. Tetapi pekerja perkhidmatan boleh melakukan lebih daripada itu.
Untuk kebolehaksesan dan penjelasan, lihat di sini.
Pekerja perkhidmatan pada asasnya ialah pekerja web yang dikongsi (yang wujud sebagai teknologi yang berasingan) dengan keupayaan istimewa untuk memintas semua permintaan http yang dibuat oleh penyemak imbas daripada URL dalam skop yang sama (laluan asal) pekerja telah berdaftar dengannya. Kemudian, ia boleh diarahkan untuk sama ada membalas dengan respons yang dibina atau dicache - sebenarnya menghalang penyemak imbas untuk memukul rangkaian dengan permintaan - atau menghantar permintaan kepada rangkaian seperti biasa atau dengan mengubah suai permintaan (menggunakan fetch).
Ini berkata, jelas sebab pekerja perkhidmatan sering dikaitkan dengan keupayaan untuk mengakses halaman web apabila luar talian: kali pertama anda boleh memuat turun dan menyimpan cache semua sumber statik (yang pada asasnya "memasang" halaman), maka pekerja perkhidmatan boleh membalas permintaan yang sama dengan versi cache, pada asasnya menyediakan "sumber aplikasi" seperti ia adalah apl asli. dev.to ialah contoh yang bagus untuk itu.
Ini sudah menjadi pemudahan, dan bercakap tentang penghapusan cache, kemas kini dan selebihnya adalah di luar skop untuk artikel ini, jadi saya tidak akan mengambil bahagian dalam perkara itu. Perkara yang akan saya bincangkan ialah keupayaan pekerja perkhidmatan untuk memberikan respons dibina.
Pasukan saya baru-baru ini ditugaskan untuk membina aplikasi "showcase", iaitu aplikasi web yang pada asasnya tiada apa-apa, tetapi berfungsi untuk menunjukkan cara menggunakan Kit UI Komponen Web kami, mengikut sistem reka bentuk dan garis panduan pengekodan.
Aplikasi itu bertujuan sebagai aplikasi bahagian hadapan semata-mata (bermaksud kami tidak sepatutnya membangunkan bahagian belakang juga), tetapi seharusnya kelihatan seperti satu daripada banyak aplikasi B2B yang diselenggara oleh pelanggan kami, dengan bahagian belakang dan semua . Itulah peranan seorang pekerja perkhidmatan yang berguna.
Kini, membalas dengan respons teks agak mudah. Malah JSON pada asasnya adalah teks, jadi pada akhirnya pekerja perkhidmatan kami boleh menjadi seperti ini:
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
Saya tidak akan bosan dengan anda tentang cara coretan ini boleh dipertingkatkan. Padanan URL boleh menggunakan URLPattern. Anda boleh memuatkan data statik dengan mengambil dan menyimpannya di IndexedDB. Anda boleh gila dengan itu.
Tetapi bagaimana pula dengan jenis tindak balas dinamik yang lain? Suka imej?
Cara paling mudah untuk menjana imej dinamik ialah mencipta SVG, yang pada asasnya adalah dokumen XML. Maksudnya, ia adalah teks. Ini adalah tugas yang boleh dilaksanakan sepenuhnya dan anda boleh menggunakan perpustakaan seperti D3.js untuk menjana elemen dan laluan SVG untuk anda: kilang seperti line() dan lain-lain mengembalikan fungsi yang mengembalikan apa yang anda perlukan untuk dimasukkan ke dalam atribut d bagi
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
Menjana SVG secara dinamik boleh menjadi bagus untuk mengeluarkan tugas daripada urutan utama - malah hasilnya boleh dicache. Ini bagus untuk carta dan maklumat grafik dan cukup "mudah" untuk dicapai.
Apa yang lebih rumit ialah menjana imej raster seperti PNG atau JPG. "Generasi" bermaksud menggunakan instrumen penyuntingan untuk mengubah gambar atau menciptanya dari awal. Perkara yang biasa kami lakukan dalam kes ini ialah menggunakan
Masalahnya ialah, pekerja perkhidmatan tidak mempunyai akses kepada elemen DOM. Jadi, adakah kita kurang beruntung?
Jangan risau, kawan-kawan saya! Kerana semua pekerja (termasuk pekerja perkhidmatan) boleh mencipta objek OffscreenCanvas. Berikan lebar dan ketinggian dalam piksel kepada conscructor dan begitulah, kanvas yang sangat halus (walaupun tidak kelihatan) dalam pekerja perkhidmatan:
import { pie, arc } from 'd3-shape'; const pieData = pie().sort(null)(data); const sectorArc = arc().outerRadius(35).innerRadius(20); const svg = '<svg viewBox="-40 -40 80 80" xmlns="http://www.w3.org/2000/svg">' + pieData.map((pie, index) => `<path d="${sectorArc(pie)}" fill="${colors[index]}"/>` ).join('') + '</svg>'; event.respondWith(new Response( svg, { headers: { 'Content-Type': 'image/svg+xml' }} ));
Bagi mereka yang tertanya-tanya: ya, anda boleh mendapatkan jenis konteks yang berbeza, walaupun tidak semuanya tersedia dalam setiap penyemak imbas. Anda boleh cuba menggunakan pustaka seperti three.js untuk menjana adegan 3D dalam pekerja perkhidmatan (saya rasa saya akan mencubanya kemudian).
Sekarang kita boleh lakukan... apa sahaja, pada asasnya. Lukis garisan, lengkok, laluan, dsb. Malah mengubah suai geometri kanvas kita. Itu semudah melukis pada konteks kanvas DOM, jadi saya tidak akan menikmati bahagian ini.
Kami memang boleh menulis teks juga. Ini penting kerana dalam persekitaran lain - iaitu, worklet Cat, kita tidak boleh melakukannya:
Nota: PaintRenderingContext2D melaksanakan subset API CanvasRenderingContext2D. Secara khususnya ia tidak melaksanakan CanvasImageData, CanvasUserInterface, CanvasText atau CanvasTextDrawingStyles API.
Tetapi dalam pekerja perkhidmatan, ini semua baik-baik saja. Ini bermakna kami mempunyai persekitaran yang lebih berkuasa (walaupun kurang berprestasi) untuk menjana imej latar belakang kami.
Melukis teks semudah ini:
const canvas = new OffscreenCanvas(800, 600); const context = canvas.getContext('2d');
Anda boleh menggunakan fon yang anda suka di sini, tetapi saya mendapati bahawa nilai standard biasa seperti sans-serif, monospace atau system-ui nampaknya tidak berfungsi, kerana semuanya kembali kepada fon serif lalai. Tetapi anda boleh menggunakan tindanan fon seperti biasa:
context.fillStyle = '#222'; context.font = '24px serif'; // (x, y) = (50, 90) will be the *bottom left* corner of the text context.fillText('Hello, world!', 50, 90);
Selain itu, anda boleh menggunakan API Pemuatan Fon untuk memuatkan fon daripada sumber luaran:
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
Menghantar semula respons adalah, sekali lagi, semudah memanggil kaedah convertToBlob yang mengembalikan janji - anda dapat meneka - Blob. Dan gumpalan boleh dihantar semula dengan mudah kepada pengirim.
import { pie, arc } from 'd3-shape'; const pieData = pie().sort(null)(data); const sectorArc = arc().outerRadius(35).innerRadius(20); const svg = '<svg viewBox="-40 -40 80 80" xmlns="http://www.w3.org/2000/svg">' + pieData.map((pie, index) => `<path d="${sectorArc(pie)}" fill="${colors[index]}"/>` ).join('') + '</svg>'; event.respondWith(new Response( svg, { headers: { 'Content-Type': 'image/svg+xml' }} ));
Kaedah ini mencipta imej PNG secara lalai, tetapi boleh diarahkan untuk mencipta fail JPG sebaliknya, seperti yang dilihat di atas. 'image/webp' ialah satu lagi format biasa, tetapi Safari tidak menyokongnya. Sejujurnya, pilihan di sini agak mengecewakan, kerana format imej yang baru tersedia dan lebih berkebolehan dekoder tidak ditunjukkan dalam pengekod yang sepadan. Tetapi itu sudah memadai untuk kebanyakan tujuan.
Fakta menyeronokkan: kaedah convertToBlob adalah khusus untuk kelas OffscreenCanvas. HTMLCanvasElements mempunyai toBlob, yang mengambil panggilan balik sebagai hujah pertama, dalam gaya biasa era pra-Janji pengendalian tugas tak segerak.
Kini, ini semua berfungsi jika kita mahu mencipta gambar dari awal. Tetapi bagaimana jika kita mahu bermula dari templat kosong?
Jika kami bekerja dalam utas utama, kami boleh menampar gambar dalam konteks menggunakan kaedah drawImage konteks 2D kami, mendapatkannya cth. daripada elemen.
Masalahnya, sekali lagi, kami tidak boleh mengakses DOM, jadi kami tidak boleh merujuk elemen. Perkara yang boleh lakukan, sebaliknya, ia mengambil gambar yang kami perlukan sebagai latar belakang, mendapatkan Blobnya dan kemudian menukarnya kepada sesuatu yang boleh dihadam oleh drawImage. Masukkan createImageBitmap, kaedah global yang juga tersedia dalam pekerja perkhidmatan. Ia mengembalikan janji untuk contoh ImageBitmap, salah satu daripada banyak kelas pembangunan web frontend yang kurang dikenali. Ia nampaknya lebih meluas digunakan dalam konteks WebGL, tetapi drawImage nampaknya menerimanya, jadi...
const canvas = new OffscreenCanvas(800, 600); const context = canvas.getContext('2d');
Mulai saat ini, kami boleh meneruskan melukis coretan dan teks kami di atasnya, mencipta imej sinamik yang memuaskan untuk dihantar semula kepada pengguna.
Nota: ini boleh diselesaikan dengan lebih mudah dengan SVG, kerana anda hanya boleh menggunakan
elemen untuk menyediakan gambar latar belakang. Tetapi itu bermakna penyemak imbas perlu memuatkan gambar selepas imej yang dijana telah dihantar, sedangkan dengan teknik ini ini dilakukan sebelum. Sesuatu yang serupa berlaku semasa memilih fon.
Dalam semua contoh ini, saya telah menggunakan modul pekerja perkhidmatan (iaitu saya telah menggunakan import daripada modul ES lain). Malangnya, pekerja perkhidmatan modul belum lagi disokong oleh Firefox, tetapi diharapkan mereka akan segera. Sementara itu, anda mungkin perlu melaraskan kod anda untuk menggunakan importScripts lama sebaliknya.
Apabila mengimport skrip lain ke dalam pekerja perkhidmatan, sama ada melalui import atau importScripts, ingat bahawa penyemak imbas akan tidak melancarkan acara kemas kini yang ditemui apabila fail yang diimport berubah: ia dicetuskan hanya apabila skrip kemasukan pekerja perkhidmatan berubah.
Dalam kes seperti kami, di mana pekerja perkhidmatan hanya diperlukan untuk mengejek kehadiran hujung belakang, kitaran hayatnya boleh dipintas dengan memanggil self.skipWaiting() betul-betul apabila acara pemasangan dicetuskan, dan kemudian panggil diri sendiri. clients.claim() pada acara aktifkan agar dapat membalas permintaan dengan segera (jika tidak, ia akan bermula hanya pada muat semula halaman seterusnya).
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
Dan ini pada asasnya semuanya, jadi... bergembiralah dengan pekerja perkhidmatan, kawan-kawan!
Atas ialah kandungan terperinci Penciptaan imej dinamik dengan pekerja perkhidmatan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!