{"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\/de\/2023\/web-workers-react-app-parcel-wo-eject\/%20","title":{"rendered":"Hinzuf\u00fcgen von Web Workern zu create-react-app mit Parcel (Auswerfen nicht erforderlich)"},"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\t\u00dcbersicht\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;\">Ich habe daran gearbeitet <a href=\"https:\/\/www.fidgetmap.com\/\">FidgetMap<\/a> schon eine Weile. Ich denke, ich k\u00f6nnte viele Prozesse (einschlie\u00dflich der grundlegenden Renderingschleife) beschleunigen, indem ich <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_Workers_API\">Web-ArbeiterInnen<\/a> um einen Gro\u00dfteil der Rendering-Aufgaben asynchron zu erledigen. Das gesamte Spiel wird gezeichnet, indem RGB-Daten in Arrays geschrieben und dann auf <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Canvas_API\">eine Leinwand<\/a>Es ist also ein perfekter Kandidat f\u00fcr <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_Workers_API\">Web-ArbeiterInnen<\/a>. Es ist ziemlich verr\u00fcckt zu denken, dass es so weit gekommen ist, ohne dass es einen Hintergrund-Thread gab, aber hier sind wir.<\/p>\n<p>Ich werde keine ganze Geschichtsstunde \u00fcber Web Worker halten, aber es reicht zu sagen, dass ich damit viele Rendering-Aufgaben an einen Hintergrund-Thread (in meinem Fall mehrere Hintergrund-Threads) \u00fcbergeben kann, um den Hauptausf\u00fchrungs-Thread zu entlasten. Das ganze Spiel sollte schneller sein, wenn bestimmte Aufgaben im Hintergrund ausgef\u00fchrt werden.<\/p>\n<p>Der Hauptteil des Spiels wird direkt auf eine<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Canvas_API\"> Leinwand<\/a>wie oben erw\u00e4hnt, aber die UI-Steuerelemente wie das Men\u00fc und die interaktiven Schaltfl\u00e4chen sind in React geschrieben. Es macht keinen Sinn, das Rad in Bezug auf Text und Flex-Style-Rendering neu zu erfinden. Also habe ich das Projekt mit <a href=\"https:\/\/create-react-app.dev\/\">create-react-app<\/a> (mit Typescript). Ich m\u00f6chte meine Web Worker im gleichen Stil schreiben, am besten im gleichen Quellcode. Aber du kannst nicht einfach Web-Worker-Module importieren und sie dann zu Workern machen. Du musst einen Worker mit einer URL zu einer separaten JavaScript-Datei (oder einem Bundle) instanziieren.<\/p>\n<p>CRA ist gro\u00dfartig. Es verbirgt jede Konfiguration und macht es super einfach, ein React-Projekt zu starten. Aber es ist nicht so toll f\u00fcr fortgeschrittene Konfigurationen wie diese, bei denen du deine App in eine JS-Datei und m\u00f6glicherweise mehrere Web Worker in separate Dateien kompilieren musst. Die \u00fcbliche Empfehlung lautet <a href=\"https:\/\/create-react-app.dev\/docs\/available-scripts#npm-run-eject\">Auswerfen von create-react-app<\/a>. So kannst du nach Bedarf an der Konfiguration herumschrauben, aber mit gro\u00dfer Macht kommt auch gro\u00dfe Verantwortung. Sobald du das Projekt ausgeworfen hast, musst du die Konfiguration und die Skripte selbst pflegen. Es ist nicht mehr so einfach, ein gut getestetes Projekt zu aktualisieren.<\/p>\n<p>Wie k\u00f6nnen wir also verschiedene Kompilierziele f\u00fcr verschiedene Einstiegspunkte festlegen? <a href=\"https:\/\/parceljs.org\/\">Parzelle<\/a> zur Rettung.\u00a0 <a href=\"https:\/\/parceljs.org\/\">ParcelJS<\/a> ist ein konfigurationsfreies Build-Tool, das sofort mit Typescript funktioniert. Du sagst einfach: \"Hey Parcel, diese Eingabedatei\", und schon erledigt es die Sache. Genug geredet, ich zeige dir jetzt, wie ich es gemacht habe.<\/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\tDie L\u00f6sung\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\tBesuche das Repo mit dieser vollst\u00e4ndigen Demo\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>Fangen wir ganz von vorne an. Verwende <a href=\"https:\/\/create-react-app.dev\/docs\/adding-typescript\/\">create-react-app, um ein neues Projekt mit Typescript zu starten<\/a>. Gehe in dein Projektverzeichnis und erstelle eine neue App wie folgt:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npx create-react-app workers-example --template typescript<\/pre>\n<p>Wechsle jetzt in das Verzeichnis und lass uns Parcel installieren<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">cd workers-example\nnpm i --save-dev parcel<\/pre>\n<p>Rad, jetzt arbeiten wir technisch gesehen mit zwei Build-Systemen: dem, das in create-react-app eingebaut ist, und Parcel, das wir gerade hinzugef\u00fcgt haben. Legen wir ein neues Verzeichnis mit dem Namen \"workers\" im Stammverzeichnis des Projekts an. Darin werden wir den Quellcode von workers ablegen. Er befindet sich in einem anderen Verzeichnis als src, da jedes dieser Verzeichnisse ein eigenes kleines Bundle sein wird. Wir werden dort auch einen Beispielworker ablegen.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">mkdir workers\ncd workers\ntouch sampleWorker.ts<\/pre>\n<p>F\u00fcgen wir etwas in unseren Beispielworker ein, damit wir sehen k\u00f6nnen, dass es funktioniert<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">self.onmessage = (e: MessageEvent) =&gt; {\n    self.postMessage(\"hallo, Welt vom Worker\");\n};<\/pre>\n<p>Jetzt f\u00fcgen wir ein neues Skript zu unserer package.json hinzu, damit wir Worker mit npm bauen k\u00f6nnen. Wir werden alle Worker im \u00f6ffentlichen Verzeichnis in einem Unterverzeichnis erstellen, damit unsere App sie von dort aus einfach nutzen kann.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...Rest der Datei\n  \"Skripte\": {\n    \/\/ ... Rest der Skripte\n    \"worker:build\": \"parcel build --dist-dir public\/workers --\"\n  }\n}<\/pre>\n<p>Okay, jetzt k\u00f6nnen wir Arbeiter bauen! F\u00fchre dies aus, um die Magie zu sehen:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npm run worker:build workers\/sampleWorker.ts<\/pre>\n<p>Parcel wird sampleWorker.ts als Einstiegspunkt verwenden. Es wird erkennen, dass es sich um Typescript handelt und das Richtige tun, ohne dass zus\u00e4tzliche Plugins oder Konfigurationen erforderlich sind. Wenn der Befehl beendet ist, solltest du deinen neu erstellten Worker in public\/workers zusammen mit einer Sourceemap finden. Super!<\/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\tSehen Sie es in Aktion\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>Aktualisieren wir die automatische App-Datei, damit wir unseren neuen Worker in Aktion sehen k\u00f6nnen. Kopiere diese Datei und f\u00fcge sie in deine App.tsx ein, um sie mit dem Worker, den wir gebaut haben, zu verbinden.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nimport React, { FC, useCallback, useEffect, useRef } from &#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;Nachricht vom Worker erhalten: &#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        Nachricht an Arbeiter senden\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\tTaking It Farther - automatisch bauen\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\tBaue die Arbeiter, wenn die App gebaut wird\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>Jetzt k\u00f6nnen wir die Worker auf die einfachste Weise bauen. Es w\u00e4re besser, wenn wir die kompilierten Worker nicht zu unserem Quellcode hinzuf\u00fcgen, sondern sie zusammen mit dem normalen Bundle bauen w\u00fcrden. Das k\u00f6nnen wir ganz einfach erreichen.<\/p>\n<p>Als Erstes f\u00fcgen wir das \u00f6ffentliche Worker-Verzeichnis (und das Cache-Verzeichnis von parcel) zu unserem .gitignore hinzu, damit wir sie nicht \u00fcbertragen.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/\/ in .gitignore\n.parcel-cache\npublic\/workers\n<\/pre>\n<p>Als N\u00e4chstes f\u00fcgen wir einen Befehl hinzu, um alle Worker in deinem Worker-Verzeichnis zu bauen. Zur\u00fcck in package.json...<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...Rest der Datei\n  \"Skripte\": {\n    \/\/ ... Rest der Skripte\n    \"worker:build\": \"parcel build --dist-dir public\/workers --\",\n    \"worker:build:all\": \"npm run worker:build .\/workers\/*\"\n  }\n}<\/pre>\n<p>Parcel rettet wieder einmal den Tag. Es verwendet jede Datei im Worker-Verzeichnis als Einstiegspunkt und erstellt f\u00fcr jede Datei ein eigenes Bundle. Auf diese Weise kannst du deine Worker vollst\u00e4ndig unterteilen. Jeder kann von node_modules importieren und diese Abh\u00e4ngigkeiten werden Teil des endg\u00fcltigen Bundles f\u00fcr jeden Worker.<\/p>\n<p>Aber wir wollen keinen separaten Build-Befehl ausf\u00fchren m\u00fcssen. Wir wollen, dass es zusammen mit dem einfachen npm run build ausgef\u00fchrt wird. Das k\u00f6nnen wir erreichen, indem wir ein weiteres Paket namens npm-run-all verwenden. Damit kannst du mehrere npm-Befehle auf einmal ausf\u00fchren, entweder nacheinander oder parallel.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npm i --save-dev npm-run-all<\/pre>\n<p>Zur\u00fcck in der package.json verschieben wir den bestehenden Build-Befehl in einen anderen Namen, damit wir den normalen Build-Befehl mehrere Dinge tun lassen k\u00f6nnen<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...Rest der Datei\n  \"Skripte\": {\n    \/\/ ... Rest der Skripte\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    \"worker:build:all\": \"npm run worker:build .\/workers\/*\"\n  }\n}<\/pre>\n<p>Alles klar! Wenn du jetzt baust, baust du zuerst alle deine Worker und f\u00fchrst dann den normalen react-scripts build aus, der dein create-react-app-Paket erstellt.<\/p>\n<p>Jetzt kannst du deine vollst\u00e4ndig transpilierten und geb\u00fcndelten Worker \u00fcberall in deinem bestehenden App-Paket verwenden, indem du das tust:<\/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\tBaue die Arbeiter in die Entwicklung ein, wenn sie sich ver\u00e4ndern\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>Das letzte Teil des Puzzles ist der Aufbau der Worker, wenn du `npm start` ausf\u00fchrst. Parcel hat einen eingebauten \"watch\"-Befehl, aber er setzt voraus, dass deine Datei in einer Umgebung mit einer \"Window\"-Variablen l\u00e4uft, was bei Workern nicht der Fall ist. Deshalb k\u00f6nnen wir unseren eigenen Watcher ganz einfach mit dem Paket `node-watch` implementieren. Installiere es zun\u00e4chst:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npm i --save-dev node-watch<\/pre>\n<p>Erstelle nun ein neues Skript namens \"watchWorkers.js\". Ich habe es in ein Unterverzeichnis namens \"scripts\" gelegt. Kopiere dies in watchWorkers.js<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">var watch = require('node-watch');\nvar { spawn } = require('child_process');\n\n\/\/ Damit werden die Worker erstellt, wenn das Skript gestartet wird\nspawn(\n    'npm',\n    ['run', 'workers:build'],\n    { stdio: 'inherit' }\n);\nwatch('.\/workers\/', { recursive: true }, function(evt, name) {\n    \/\/ Dies baut jeden einzelnen Worker auf, sobald er aktualisiert wird\n    spawn(\n        'npm',\n        ['run', 'worker:build', name],\n        { stdio: 'inherit' }\n    );\n});\n<\/pre>\n<p>Jetzt haben wir ein Skript, das jeden Worker neu aufbaut, wenn er sich \u00e4ndert. \u00c4ndern wir die package.json noch einmal, damit sie Teil unseres normalen Prozesses wird<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...Rest der Datei\n  \"Skripte\": {\n    \/\/ ... Rest der Skripte\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    \"worker:build:all\": \"npm run worker:build .\/workers\/*\",\n    \"workers:watch\": \"node .\/scripts\/watchWorkers.js\"\n  }\n}<\/pre>\n<p>Wenn du jetzt `npm start` ausf\u00fchrst, wird der normale React-Skript-Start ausgef\u00fchrt, der auf \u00c4nderungen im App-Bundle achtet, aber du f\u00fchrst auch deine eigene Worker-Watch aus, die jeden Worker bei jeder Aktualisierung neu \u00fcbertr\u00e4gt.<\/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\tBesuche das Repo mit dieser vollst\u00e4ndigen Demo\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>Ich arbeite schon seit einer Weile an FidgetMap. Ich glaube, ich k\u00f6nnte viele Prozesse (einschlie\u00dflich der grundlegenden Rendering-Schleife) beschleunigen, indem ich Web Worker hinzuf\u00fcge, die einen Gro\u00dfteil der Rendering-Aufgaben asynchron erledigen. Das ganze Spiel wird gezeichnet, indem RGB-Daten in Arrays geschrieben und dann auf einer Leinwand zusammengesetzt werden, also [...]<\/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\/de\/wp-json\/wp\/v2\/posts\/18074","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/comments?post=18074"}],"version-history":[{"count":80,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/posts\/18074\/revisions"}],"predecessor-version":[{"id":21451,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/posts\/18074\/revisions\/21451"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/media\/21450"}],"wp:attachment":[{"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/media?parent=18074"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/categories?post=18074"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/tags?post=18074"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}