Bir Web Worker'ı npm paketine nasıl gömdük
· 4 dk okuma
İşi ana iş parçacığının dışına taşıyan her kütüphanenin aynı kurulum sorunu
vardır. Worker ayrı bir betiktir, tarayıcı onu bir URL'den yükler ve o URL
kullanıcının hangi bundler'ı kullandığına bağlıdır. Sonuç: README'de bir bölüm
büyür — Vite kullanıyorsanız şunu yapın. Next.js'te şöyle. Bu dosyayı
public/ içine kopyalayın.
Bizde de o bölüm vardı. İnsanların en sık yanlış yaptığı tek şeydi.
@kanunilabs/[email protected] ile gitti. Kopyalanacak dosya, bundler
reçetesi ve ayarlanacak workerUrl yok:
import { BasePivotGrid } from '@kanunilabs/pivotgrid-react';
<BasePivotGrid data={rows} initialFields={fields} />
Nasıl yaptığımız, neye mal olduğu ve nerede canınızı yakabileceği aşağıda.
Fikir
Bir Worker, Blob URL'inden oluşturulabilir:
const url = URL.createObjectURL(new Blob([source], { type: 'text/javascript' }));
const worker = new Worker(url, { type: 'module' });
Yani worker'ın kaynak kodu paketin içinde bir dizeyse, çalışma anında çözülecek hiçbir şey kalmaz. URL yok, bundler'ın işi yok, kopyalama yok.
İşin püf noktası kaynak kelimesinde. O dize eksiksiz ve kendi kendine yeten
bir program olmak zorunda. İçinde hâlâ import ifadesi bulunan bir worker
sorunsuz oluşturulur, sonra çalıştığı anda patlar — çünkü Blob URL'inin o
import'ları çözeceği bir modül grafiği yoktur.
Derleme
Üç adım, bu sırayla.
1. Worker'ları tek başlarına derleyin. İkinci bir bundler yapılandırması, her worker'ı kendi kendine yeten tek dosya olarak üretir — kod bölme kapalı, tüm workspace bağımlılıkları import olarak bırakılmak yerine içeri gömülü:
// tsup.worker.config.ts
export default defineConfig({
entry: {
'pivot.worker': 'src/worker/pivot.worker.ts',
'export.worker': 'src/worker/export.worker.ts',
'import.worker': 'src/worker/import.worker.ts',
},
splitting: false,
noExternal: ['@kanunilabs/foundation'],
clean: true,
});
2. Her derlenen dosyayı bir TypeScript modülüne çevirin. Küçük bir betik çıktıyı okur ve dize sabiti olarak geri yazar:
const source = readFileSync('dist/pivot.worker.js', 'utf8');
writeFileSync(
'src/worker/workerSource.generated.ts',
`export const EMBEDDED_PIVOT_WORKER_SOURCE = ${JSON.stringify(source)};\n`,
);
Burada JSON.stringify gerçek bir iş yapıyor: tırnakları, ters bölü işaretlerini
ve satır sonlarını kaçırıyor; böylece minifier ne üretmiş olursa olsun sonuç
geçerli bir dize sabiti oluyor.
Sessizce bozulduğu iki nokta
İkisi de sorunsuz kurulan, tip kontrolünden geçen ve yalnızca müşterinin makinesinde patlayan bir paket üretir. Açıkça korumaya değerler.
Ana derleme worker'ları siler. Çoğu bundler yapılandırması varsayılan olarak
çıktı klasörünü temizler. Önce worker derlemesini, sonra temizleme açık halde
ana derlemeyi çalıştırırsanız, üçüncü adım birinci adımın ürettiğini siler.
Paket yine yayınlanır — sadece geri düşme yolu için worker dosyası kalmamıştır.
clean yalnızca tek bir yapılandırmanın olmalı.
Gömülü kaynak kendi kendine yetmiyordur. Bir düzenleme, içeri gömülmüş bir
bağımlılığı tekrar dış import'a çevirirse, Blob worker çalışma anında hata
vermeye başlar ama bütün yerel testler geçmeye devam eder. Bu yüzden üretim
betiği üç kontrolden geçmeyen dosyayı yazmayı reddediyor: asgari bayt boyutu,
import/export ifadesi bulunmaması ve bir onmessage işleyicisinin varlığı.
Boyut eşiği worker başına, genel değil. Bizimkiler 16 KB, 10 KB ve 3 KB. Tek bir eşik ya bozuk 16 KB'lık worker'ı geçirir ya da sapasağlam 3 KB'lık olanı reddeder.
Bedeli ne
Üç worker'ı gömmek çekirdek paketi 30,1 KB — %17,4 büyüttü (173,2 KB → 203,3 KB; minified, gzip öncesi).
Bu bedava değil ve öyleymiş gibi davranmayacağız. Karşılığında, bütün iddiası yolunuzdan çekilmek olan bir kütüphanede koca bir kurulum adımı siliniyor. Tek worker için rakam çok daha küçüktü: DataGrid aynı değişiklik için %6,1 ödedi.
Bir kaçış kapısı bırakın
Blob worker'ı gerçekten çalıştıramayan tek bir ortam var: worker-src içinde
blob: izni vermeyen katı bir Content-Security-Policy. Orayı çıkmaz sokak
yapmayın.
export function startEmbeddedWorker(kind, resolveUrl) {
try {
return new Worker(embeddedWorkerUrl(kind), { type: 'module' });
} catch {
// CSP Blob'u engelledi. Worker'ı tamamen kaybetmek yerine gömme öncesi
// davranışa geri dön.
return new Worker(resolveUrl(), { type: 'module' });
}
}
Worker dosyaları pakette durmaya devam ediyor, yani geri düşme yolunun
yükleyeceği bir şey var. Açıkça verilen bir workerUrl ise hâlâ ikisini de
yener — o yol, worker'ı kendi CDN'inden sunanlar için var ve gömme onu elinden
almamalı.
Gerçekten çalıştığını doğrulamak
Buradaki hata sessiz olduğu için "iyi görünüyor" kanıt sayılmaz. İki kontrol otomatikleştirmeye değer:
Workeryapıcısını araya girin ve gerçek bir sayfada aldığı URL'inblob:ile başladığını doğrulayın. Bizimki başlıyor.- Yayınlanan tarball'da arayın. Derlenmiş her worker'dan uzun, tırnaksız
bir parça alıp
dist/index.jsiçinde arayın. Kaynak gerçekten gömülüyse birebir oradadır.
İkinci kontrolü, yerel derlemeyle değil, genel registry'den temiz bir klasöre
yapılan npm install üzerinde çalıştırdık — üç worker'dan alınan dokuz parçanın
dokuzu da bulundu. Bu iddianın söylenmeye değer tek biçimi bu.
Bu, PivotGrid 1.0.5 ile gelen değişikliklerden biri. Kaldırdığı kurulum adımı Kurulum sayfasında anlatılıyor.