{"id":18074,"date":"2023-09-08T15:54:30","date_gmt":"2023-09-08T20:54:30","guid":{"rendered":"http:\/\/carterembry.com\/?p=18074"},"modified":"2025-08-05T09:28:55","modified_gmt":"2025-08-05T14:28:55","slug":"web-workers-react-app-parcel-wo-eject","status":"publish","type":"post","link":"https:\/\/carterembry.com\/it\/2023\/web-workers-react-app-parcel-wo-eject\/%20","title":{"rendered":"Aggiungi i Web Worker a create-react-app usando Parcel (eject non \u00e8 necessario)"},"content":{"rendered":"<div id=\"pl-18074\"  class=\"panel-layout\" ><div id=\"pg-18074-0\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-18074-0-0\"  class=\"panel-grid-cell\" ><div id=\"panel-18074-0-0-0\" class=\"so-panel widget widget_sow-headline panel-first-child\" data-index=\"0\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-headline so-widget-sow-headline-default-d6db24c2db75-18074\"\n\t\t\t\n\t\t><div class=\"sow-headline-container\">\n\t\t\t\t\t\t\t<h2 class=\"sow-headline\">\n\t\t\t\t\t\tPanoramica\t\t\t\t\t\t<\/h2>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"decoration\">\n\t\t\t\t\t\t<div class=\"decoration-inside\"><\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n<\/div><\/div><div id=\"panel-18074-0-0-1\" class=\"so-panel widget widget_sow-editor\" data-index=\"1\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p style=\"padding-left: 40px;\">Ho lavorato su <a href=\"https:\/\/www.fidgetmap.com\/\">FidgetMap<\/a> da un po' di tempo a questa parte. Penso che potrei velocizzare molti processi (compreso il ciclo di rendering di base) aggiungendo <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_Workers_API\">Lavoratori web<\/a> per gestire molte delle attivit\u00e0 di rendering in modo asincrono. L'intero gioco viene disegnato scrivendo i dati RGB in array e componendoli poi su <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Canvas_API\">una tela<\/a>quindi \u00e8 davvero un candidato perfetto per <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_Workers_API\">Lavoratori web<\/a>. \u00c8 piuttosto assurdo pensare che sia arrivato a questo punto senza alcun filo conduttore, ma eccoci qui.<\/p>\n<p>Non mi dilungher\u00f2 in un'intera lezione di storia sui Web Worker, basti dire che mi permetteranno di passare molte operazioni di rendering a un thread in background (pi\u00f9 thread in background nel mio caso) per liberare il thread di esecuzione principale. L'intero gioco dovrebbe essere pi\u00f9 veloce con alcuni compiti compartimentati in background.<\/p>\n<p>La parte principale del gioco \u00e8 disegnata direttamente su un<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Canvas_API\"> tela<\/a>ma i controlli dell'interfaccia utente, come il menu e i pulsanti interattivi, sono scritti in React. Non ha senso reinventare la ruota per quanto riguarda il testo e il rendering in stile flex. Ho quindi realizzato il progetto utilizzando <a href=\"https:\/\/create-react-app.dev\/\">creare-react-app<\/a> (con Typescript). Voglio scrivere i miei Web Worker nello stesso stile, preferibilmente nello stesso codice sorgente. Ma non puoi semplicemente importare i moduli Web Worker e trasformarli in worker. Devi istanziare un worker con un URL a un file (o bundle) JavaScript separato.<\/p>\n<p>CRA \u00e8 fantastico. Nasconde qualsiasi configurazione e rende super facile l'avvio di un progetto React. Tuttavia, non \u00e8 cos\u00ec perfetto per le configurazioni avanzate come questa, in cui devi compilare la tua applicazione in un unico file JS e potenzialmente diversi Web Worker in file separati. Di solito si consiglia di <a href=\"https:\/\/create-react-app.dev\/docs\/available-scripts#npm-run-eject\">espulsione da create-react-app<\/a>. Questo ti permette di modificare la configurazione a seconda delle necessit\u00e0, ma da un grande potere derivano grandi responsabilit\u00e0. Una volta espulso, dovrai mantenere tu stesso la configurazione e gli script. Non \u00e8 pi\u00f9 possibile aggiornare facilmente un progetto ben collaudato.<\/p>\n<p>Quindi come possiamo creare diversi target di compilazione per diversi punti di ingresso? <a href=\"https:\/\/parceljs.org\/\">Parcella<\/a> in soccorso.\u00a0 <a href=\"https:\/\/parceljs.org\/\">ParcelJS<\/a> \u00e8 uno strumento di compilazione a configurazione zero che funziona con Typescript out-of-the-box. Basta dire \"hey Parcel, questo file di ingresso\" e lui fa la cosa\u2122. Basta parlare, lascia che ti mostri come ho fatto.<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-18074-0-0-2\" class=\"so-panel widget widget_sow-headline\" data-index=\"2\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-headline so-widget-sow-headline-default-d6db24c2db75-18074\"\n\t\t\t\n\t\t><div class=\"sow-headline-container\">\n\t\t\t\t\t\t\t<h2 class=\"sow-headline\">\n\t\t\t\t\t\tLa soluzione\t\t\t\t\t\t<\/h2>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"decoration\">\n\t\t\t\t\t\t<div class=\"decoration-inside\"><\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n<\/div><\/div><div id=\"panel-18074-0-0-3\" class=\"so-panel widget widget_sow-button\" data-index=\"3\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button so-widget-sow-button-atom-27e09ad139d7-18074\"\n\t\t\t\n\t\t><div class=\"ow-button-base ow-button-align-center\"\n>\n\t\t\t<a\n\t\t\t\t\thref=\"https:\/\/github.com\/slobaum\/cra-workers-parcel\"\n\t\t\t\t\tclass=\"sowb-button ow-icon-placement-left ow-button-hover\" target=\"_blank\" rel=\"noopener noreferrer\" \t>\n\t\t<span>\n\t\t\t<span class=\"sow-icon-fontawesome sow-far\" data-sow-icon=\"&#xf1c9;\"\n\t\tstyle=\"\" \n\t\taria-hidden=\"true\"><\/span>\n\t\t\tVisita la repo con questa demo completa\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div><\/div><div id=\"panel-18074-0-0-4\" class=\"so-panel widget widget_sow-editor panel-last-child\" data-index=\"4\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Partiamo dall'inizio. Utilizza <a href=\"https:\/\/create-react-app.dev\/docs\/adding-typescript\/\">create-react-app per iniziare un nuovo progetto con Typescript<\/a>. Vai alla directory del tuo progetto e crea una nuova applicazione in questo modo:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npx create-react-app workers-example --template typescript<\/pre>\n<p>Ora spostiamoci nella directory e installiamo Parcel<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">cd workers-example\nnpm i --save-dev parcel<\/pre>\n<p>Rad, ora stiamo tecnicamente lavorando con due sistemi di compilazione: quello integrato in create-react-app e Parcel che abbiamo appena aggiunto. Creiamo una nuova cartella nella root del progetto chiamata \"workers\". \u00c8 qui che metteremo il codice sorgente di workers. Si trova in una directory separata da src poich\u00e9 ognuno di essi sar\u00e0 un piccolo bundle a s\u00e9 stante. Vi inseriremo anche un worker di esempio.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">mkdir workers\ncd workers\ntoccare sampleWorker.ts<\/pre>\n<p>Inseriamo qualcosa nel nostro worker di esempio in modo da vedere che funziona<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">self.onmessage = (e: MessageEvent) =&gt; {\n    self.postMessage(\"hello, world from the worker\");\n};<\/pre>\n<p>Ora aggiungeremo un nuovo script al nostro package.json in modo da poter costruire i worker utilizzando npm. Costruiremo tutti i worker nella directory pubblica sotto una sottodirectory, in modo che la nostra applicazione possa utilizzarli facilmente da l\u00ec.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...il resto del file\n  \"scripts\": {\n    \/\/ ... il resto degli script\n    \"worker:build\": \"parcel build --dist-dir public\/workers --\"\n  }\n}<\/pre>\n<p>Ok, ora possiamo costruire i lavoratori! Esegui questo per vedere la magia:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npm run worker:build workers\/sampleWorker.ts<\/pre>\n<p>Parcel utilizzer\u00e0 sampleWorker.ts come punto di ingresso. Vedr\u00e0 che si tratta di Typescript e far\u00e0 la cosa giusta senza alcun plugin o configurazione aggiuntiva. Al termine del comando, dovresti trovare il tuo worker appena costruito in public\/workers insieme a una mappa dei sorgenti. Bello!<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-18074-1\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-18074-1-0\"  class=\"panel-grid-cell\" ><div id=\"panel-18074-1-0-0\" class=\"so-panel widget widget_sow-headline panel-first-child\" data-index=\"5\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-headline so-widget-sow-headline-default-d6db24c2db75-18074\"\n\t\t\t\n\t\t><div class=\"sow-headline-container\">\n\t\t\t\t\t\t\t<h2 class=\"sow-headline\">\n\t\t\t\t\t\tGuardalo in azione\t\t\t\t\t\t<\/h2>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"decoration\">\n\t\t\t\t\t\t<div class=\"decoration-inside\"><\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n<\/div><\/div><div id=\"panel-18074-1-0-1\" class=\"so-panel widget widget_siteorigin-panels-builder panel-last-child\" data-index=\"6\" ><div id=\"pl-w64fe020f727a5\"  class=\"panel-layout\" ><div id=\"pg-w64fe020f727a5-0\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-w64fe020f727a5-0-0\"  class=\"panel-grid-cell\" ><div id=\"panel-w64fe020f727a5-0-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"0\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Aggiorniamo il file dell'App automatica in modo da poter vedere il nostro nuovo worker in azione. Copia\/incolla questo file nel tuo App.tsx per interfacciarti con il lavoratore che abbiamo costruito.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nimport React, { FC, useCallback, useEffect, useRef } da &#039;react&#039;;\n\nconst App: FC = () =&amp;gt; {\n  const workerRef = useRef&lt;worker&gt;();\n\n  const sendMessageToWorker = useCallback(() =&amp;gt; {\n    workerRef.current?.postMessage({});\n  }, []);\n\n  useEffect(() =&amp;gt; {\n    workerRef.current = new Worker(&quot;\/workers\/sampleWorker.js&quot;);\n\n    workerRef.current?.addEventListener(&#039;message&#039;, (event) =&amp;gt; {\n      alert(&#039;messaggio ricevuto dal lavoratore: &#039; + JSON.stringify(event.data));\n    });\n\n    return () =&amp;gt; workerRef.current?.terminate();\n  }, []);\n\n  return (\n    &lt;div&gt;\n      &lt;button type=&quot;button&quot; onclick=&quot;{sendMessageToWorker}&quot;&gt;\n        Invia un messaggio al lavoratore\n      &lt;\/button&gt;\n    &lt;\/div&gt;\n  );\n}\n\nexport default App;\n<\/pre>\n<p><\/worker><\/p>\n<\/div>\n<\/div><\/div><\/div><div id=\"pgc-w64fe020f727a5-0-1\"  class=\"panel-grid-cell\" ><div id=\"panel-w64fe020f727a5-0-1-0\" class=\"so-panel widget widget_media_image panel-first-child panel-last-child\" data-index=\"1\" ><a href=\"\/wp-content\/uploads\/2023\/09\/Screenshot-from-2023-09-08-21-21-50.png\"><img loading=\"lazy\" decoding=\"async\" width=\"500\" height=\"500\" src=\"https:\/\/carterembry.com\/wp-content\/uploads\/2023\/09\/Screenshot-from-2023-09-08-21-21-50-500x500.png\" class=\"image wp-image-18095  attachment-medium size-medium\" alt=\"\" style=\"max-width: 100%; height: auto;\" srcset=\"https:\/\/carterembry.com\/wp-content\/uploads\/2023\/09\/Screenshot-from-2023-09-08-21-21-50-500x500.png 500w, https:\/\/carterembry.com\/wp-content\/uploads\/2023\/09\/Screenshot-from-2023-09-08-21-21-50-375x375.png 375w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/a><\/div><\/div><\/div><\/div><\/div><\/div><\/div><div id=\"pg-18074-2\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-18074-2-0\"  class=\"panel-grid-cell\" ><div id=\"panel-18074-2-0-0\" class=\"so-panel widget widget_sow-headline panel-first-child\" data-index=\"7\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-headline so-widget-sow-headline-default-d6db24c2db75-18074\"\n\t\t\t\n\t\t><div class=\"sow-headline-container\">\n\t\t\t\t\t\t\t<h2 class=\"sow-headline\">\n\t\t\t\t\t\tPortarlo pi\u00f9 lontano - costruire automaticamente\t\t\t\t\t\t<\/h2>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"decoration\">\n\t\t\t\t\t\t<div class=\"decoration-inside\"><\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t<h3 class=\"sow-sub-headline\">\n\t\t\t\t\t\tCostruisce i lavoratori quando l'applicazione viene creata\t\t\t\t\t\t<\/h3>\n\t\t\t\t\t\t<\/div>\n<\/div><\/div><div id=\"panel-18074-2-0-1\" class=\"so-panel widget widget_sow-editor\" data-index=\"8\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Ora possiamo compilare i worker nel modo pi\u00f9 elementare. Sarebbe meglio se non aggiungessimo i worker compilati al nostro sorgente, ma li costruissimo insieme al normale bundle. Possiamo farlo in modo abbastanza semplice.<\/p>\n<p>Per prima cosa, aggiungiamo la directory dei lavoratori pubblici (e la directory della cache di parcel) al nostro .gitignore in modo da non eseguire il commit.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/\/ in .gitignore\n.parcel-cache\npubblico\/lavoratori\n<\/pre>\n<p>Successivamente, aggiungeremo un comando per costruire tutti i lavoratori nella tua directory dei lavoratori. Torniamo al file package.json...<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...il resto del file\n  \"scripts\": {\n    \/\/ ... il resto degli script\n    \"worker:build\": \"parcel build --dist-dir public\/workers --\",\n    \"workers:build:all\": \"npm run worker:build .\/workers\/*\"\n  }\n}<\/pre>\n<p>Ancora una volta Parcel salva la situazione. Utilizzer\u00e0 ogni file nella directory dei lavoratori come punto di ingresso e creer\u00e0 un bundle separato per ognuno di essi. Questo ti permette di avere lavoratori completamente compartimentati. Ciascuno di essi pu\u00f2 importare da node_modules e queste dipendenze diventeranno parte del bundle finale di ogni worker.<\/p>\n<p>Ma non vogliamo dover eseguire un comando di compilazione separato. Vogliamo che lo faccia insieme al pi\u00f9 semplice npm run build. Possiamo ottenere questo risultato utilizzando un altro pacchetto chiamato npm-run-all. Questo ti permette di eseguire pi\u00f9 comandi npm contemporaneamente, in sequenza o in parallelo.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npm i --save-dev npm-run-all<\/pre>\n<p>Ora, nel file package.json, sposteremo il comando di compilazione esistente con un nome diverso, in modo da poter fare in modo che il normale comando di compilazione faccia diverse cose<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...il resto del file\n  \"scripts\": {\n    \/\/ ... il resto degli script\n    \"build\": \"npm-run-all --sequential workers:build:all rs:build\",\n    \"rs:build\": \"react-scripts build\",\n    \"worker:build\": \"parcel build --dist-dir public\/workers --\",\n    \"workers:build:all\": \"npm run worker:build .\/workers\/*\"\n  }\n}<\/pre>\n<p>Bene! Ora, ogni volta che costruisci, costruisci prima tutti i tuoi worker e poi esegui il normale build di react-scripts che crea il tuo pacchetto create-react-app.<\/p>\n<p>Ora puoi utilizzare i tuoi lavoratori completamente transpilati e raggruppati in qualsiasi punto del tuo pacchetto di app esistente:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n    const worker = new Worker(\"\/workers\/sampleWorker.js\");\n<\/pre>\n<\/div>\n<\/div><\/div><div id=\"panel-18074-2-0-2\" class=\"so-panel widget widget_sow-headline\" data-index=\"9\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-headline so-widget-sow-headline-default-2b1d44ffeaae-18074\"\n\t\t\t\n\t\t><div class=\"sow-headline-container\">\n\t\t\t\t\t\t\t<h3 class=\"sow-headline\">\n\t\t\t\t\t\tCostruisci i lavoratori nello sviluppo ogni volta che cambiano\t\t\t\t\t\t<\/h3>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"decoration\">\n\t\t\t\t\t\t<div class=\"decoration-inside\"><\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n<\/div><\/div><div id=\"panel-18074-2-0-3\" class=\"so-panel widget widget_sow-editor\" data-index=\"10\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>L'ultimo pezzo del puzzle \u00e8 la creazione dei worker quando esegui `npm start`. Parcel ha un comando \"watch\" integrato, ma presuppone che il file sia in esecuzione in un ambiente con una variabile `window`, cosa che i worker non fanno. Possiamo quindi implementare il nostro watcher in modo semplice utilizzando un pacchetto chiamato `node-watch`. Inizia ad installarlo:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npm i --save-dev node-watch<\/pre>\n<p>Ora crea un nuovo script chiamato `watchWorkers.js`. Io l'ho messo in una sottodirectory chiamata \"scripts\". Copia questo in watchWorkers.js<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">var watch = require('node-watch');\nvar { spawn } = require('child_process');\n\n\/\/ questo costruir\u00e0 i worker inizialmente quando lo script viene eseguito\nspawn(\n    'npm',\n    ['run', 'workers:build'],\n    { stdio: 'inherit' }\n);\nwatch('.\/workers\/', { ricorsivo: true }, function(evt, name) {\n    \/\/ questo costruir\u00e0 ogni singolo lavoratore man mano che viene aggiornato\n    spawn(\n        'npm',\n        ['run', 'worker:build', name],\n        { stdio: 'inherit' }\n    );\n});\n<\/pre>\n<p>Ora abbiamo uno script che ricostruisce ogni worker quando cambia. Ancora una volta, modifichiamo package.json per renderlo parte del nostro processo normale<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...il resto del file\n  \"scripts\": {\n    \/\/ ... il resto degli script\n    \"start\": \"npm-run-all --parallel workers:watch rs:start\",\n    \"rs:start\": \"react-scripts start\",\n    \"build\": \"npm-run-all --sequential workers:build:all rs:build\",\n    \"rs:build\": \"react-scripts build\",\n    \"worker:build\": \"parcel build --dist-dir public\/workers --\",\n    \"workers:build:all\": \"npm run worker:build .\/workers\/*\",\n    \"workers:watch\": \"node .\/scripts\/watchWorkers.js\"\n  }\n}<\/pre>\n<p>Ora, ogni volta che eseguirai `npm start`, eseguirai il normale avvio di react-scripts che verificher\u00e0 le modifiche al bundle dell'app, ma eseguirai anche il tuo worker watch che effettuer\u00e0 il retranspile di ogni worker man mano che viene aggiornato.<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-18074-2-0-4\" class=\"so-panel widget widget_sow-button panel-last-child\" data-index=\"11\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button so-widget-sow-button-atom-27e09ad139d7-18074\"\n\t\t\t\n\t\t><div class=\"ow-button-base ow-button-align-center\"\n>\n\t\t\t<a\n\t\t\t\t\thref=\"https:\/\/github.com\/slobaum\/cra-workers-parcel\"\n\t\t\t\t\tclass=\"sowb-button ow-icon-placement-left ow-button-hover\" target=\"_blank\" rel=\"noopener noreferrer\" \t>\n\t\t<span>\n\t\t\t<span class=\"sow-icon-fontawesome sow-far\" data-sow-icon=\"&#xf1c9;\"\n\t\tstyle=\"\" \n\t\taria-hidden=\"true\"><\/span>\n\t\t\tVisita la repo con questa demo completa\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>Sto lavorando a FidgetMap da un po' di tempo. Penso che potrei velocizzare molti processi (compreso il ciclo di rendering di base) aggiungendo dei Web Worker per gestire molte delle attivit\u00e0 di rendering in modo asincrono. L'intero gioco viene disegnato scrivendo dati RGB su array e componendoli su una tela, quindi [...]<\/p>","protected":false},"author":3,"featured_media":21450,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wprm-recipe-roundup-name":"","wprm-recipe-roundup-description":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"default","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[10,5,7],"tags":[665,841,175,840,838,839],"class_list":["post-18074","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-about-daniel","category-professional-opinions","category-technical-jargon","tag-665","tag-create-react-app","tag-javascript","tag-parcel","tag-typescript","tag-web-worker"],"_links":{"self":[{"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/posts\/18074","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/comments?post=18074"}],"version-history":[{"count":80,"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/posts\/18074\/revisions"}],"predecessor-version":[{"id":21451,"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/posts\/18074\/revisions\/21451"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/media\/21450"}],"wp:attachment":[{"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/media?parent=18074"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/categories?post=18074"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/carterembry.com\/it\/wp-json\/wp\/v2\/tags?post=18074"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}