{"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\/es\/2023\/web-workers-react-app-parcel-wo-eject\/%20","title":{"rendered":"A\u00f1ade Web Workers a create-react-app utilizando Parcel (no se requiere eject)"},"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\tVisi\u00f3n general\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;\">He estado trabajando en <a href=\"https:\/\/www.fidgetmap.com\/\">FidgetMap<\/a> desde hace un tiempo. Creo que podr\u00eda acelerar muchos procesos (incluido el bucle b\u00e1sico de renderizado) a\u00f1adiendo <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_Workers_API\">Trabajadores web<\/a> para manejar muchas de las tareas de renderizado de forma as\u00edncrona. Todo el juego se dibuja escribiendo datos RGB en matrices y luego componi\u00e9ndolos en <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Canvas_API\">un lienzo<\/a>por lo que es realmente un candidato perfecto para <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_Workers_API\">Trabajadores web<\/a>. Es bastante descabellado pensar que haya llegado tan lejos sin ning\u00fan hilo de fondo, pero aqu\u00ed estamos.<\/p>\n<p>No voy a entrar en toda una lecci\u00f3n de historia sobre los Web Workers, basta con decir que me permitir\u00e1n pasar gran parte de la renderizaci\u00f3n a un hilo en segundo plano (m\u00faltiples hilos en segundo plano en mi caso) para liberar el hilo de ejecuci\u00f3n principal. Todo el juego deber\u00eda ser m\u00e1s r\u00e1pido con ciertas tareas compartimentadas en segundo plano.<\/p>\n<p>La parte principal del juego se dirige directamente a un<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Canvas_API\"> lona<\/a>como se ha mencionado anteriormente, pero los controles de la interfaz de usuario, como el men\u00fa y los botones interactivos, est\u00e1n escritos en React. No tiene sentido reinventar la rueda en lo que respecta a la representaci\u00f3n de texto y estilo flex. As\u00ed que constru\u00ed el proyecto utilizando <a href=\"https:\/\/create-react-app.dev\/\">crear-react-app<\/a> (con Typescript). Quiero escribir mis Web Workers con el mismo estilo, preferiblemente en el mismo c\u00f3digo fuente. Pero no puedes limitarte a importar m\u00f3dulos Web Worker y luego convertirlos en trabajadores. Tienes que instanciar un trabajador con una URL a un archivo (o paquete) JavaScript independiente.<\/p>\n<p>CRA es genial. Oculta cualquier configuraci\u00f3n y hace que sea superf\u00e1cil iniciar un proyecto React. Pero no es tan bueno en configuraciones avanzadas como \u00e9sta, en la que necesitas compilar tu aplicaci\u00f3n en un archivo JS y potencialmente varios Web Workers en archivos separados. La recomendaci\u00f3n habitual es <a href=\"https:\/\/create-react-app.dev\/docs\/available-scripts#npm-run-eject\">expulsar de create-react-app<\/a>. Esto te permite juguetear con la configuraci\u00f3n seg\u00fan necesites, pero un gran poder conlleva una gran responsabilidad. Una vez expulsado, tienes que mantener t\u00fa mismo la configuraci\u00f3n y los scripts. Se acab\u00f3 la actualizaci\u00f3n f\u00e1cil de un proyecto bien probado.<\/p>\n<p>Entonces, \u00bfc\u00f3mo podemos hacer diferentes objetivos de compilaci\u00f3n para diferentes puntos de entrada? <a href=\"https:\/\/parceljs.org\/\">Parcela<\/a> al rescate.\u00a0 <a href=\"https:\/\/parceljs.org\/\">ParcelJS<\/a> es una herramienta de construcci\u00f3n sin configuraci\u00f3n que funciona con Typescript desde el primer momento. S\u00f3lo tienes que decir \"oye Parcela, este archivo de entrada\" y hace The Thing\u2122. Basta de charla, d\u00e9jame mostrarte c\u00f3mo lo hice.<\/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 soluci\u00f3n\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 el repo con esta 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>Empecemos por el principio. Utiliza <a href=\"https:\/\/create-react-app.dev\/docs\/adding-typescript\/\">create-react-app para iniciar un nuevo proyecto con Typescript<\/a>. Ve al directorio de tu proyecto y crea una nueva aplicaci\u00f3n como \u00e9sta:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npx create-react-app workers-ejemplo --template typescript<\/pre>\n<p>Ahora entra en el directorio e instalemos Parcela<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">cd trabajadores-ejemplo\nnpm i --save-dev paquete<\/pre>\n<p>Rad, ahora t\u00e9cnicamente estamos trabajando con dos sistemas de compilaci\u00f3n: el incorporado en create-react-app y Parcel que acabamos de a\u00f1adir. Vamos a crear un nuevo directorio en la ra\u00edz del proyecto llamado \"workers\". Ah\u00ed es donde pondremos el c\u00f3digo fuente de nuestros workers. Est\u00e1 en un directorio distinto de src, ya que cada uno ser\u00e1 su propio peque\u00f1o paquete. Tambi\u00e9n pondremos ah\u00ed una trabajadora de ejemplo.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">mkdir trabajadores\ncd trabajadores\ntoca ejemploTrabajador.ts<\/pre>\n<p>Pongamos algo en nuestro trabajador de muestra para ver que funciona<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">self.onmessage = (e: EventoMensaje) =&gt; {\n    self.postMessage(\"hola, mundo desde el trabajador\");\n};<\/pre>\n<p>Ahora a\u00f1adiremos un nuevo script a nuestro package.json para poder construir workers usando npm. Construiremos todos los workers en el directorio p\u00fablico bajo un subdirectorio para que nuestra aplicaci\u00f3n pueda utilizarlos f\u00e1cilmente desde all\u00ed.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...resto del archivo\n  \"scripts\": {\n    \/\/ ... resto de scripts\n    \"trabajador:construir\": \"parcel build --dist-dir public\/workers --\"\n  }\n}<\/pre>\n<p>Bien, \u00a1ahora podemos construir trabajadores! Ejecuta esto para ver la magia:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npm run worker:build workers\/sampleWorker.ts<\/pre>\n<p>Parcel utilizar\u00e1 sampleWorker.ts como punto de entrada. Ver\u00e1 que es Typescript y har\u00e1 lo correcto sin ning\u00fan plugin o configuraci\u00f3n adicional. Cuando el comando termine, deber\u00edas encontrar tu reci\u00e9n construido trabajador en public\/workers junto con un mapa de fuentes. \u00a1Qu\u00e9 bien!<\/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\tM\u00edralo en acci\u00f3n\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>Vamos a actualizar el archivo autom\u00e1tico de la App para que podamos ver a nuestro nuevo trabajador en acci\u00f3n. Copia\/pega esto en tu App.tsx para interactuar con el trabajador que hemos construido.<\/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 enviarMensajeAlTrabajador = useCallback(() =&amp;gt; {\n    workerRef.current?.postMessage({});\n  }, []);\n\n  useEfecto(() =&amp;gt; {\n    workerRef.current = nuevo Worker(&quot;\/workers\/sampleWorker.js&quot;);\n\n    workerRef.current?.addEventListener(&#039;mensaje&#039;, (evento) =&amp;gt; {\n      alert(&#039;mensaje recibido del trabajador: &#039; + JSON.stringify(evento.datos));\n    });\n\n    return () =&amp;gt; workerRef.current?.terminate();\n  }, []);\n\n  devolver (\n    &lt;div&gt;\n      &lt;button type=&quot;button&quot; onclick=&quot;{sendMessageToWorker}&quot;&gt;\n        Enviar mensaje al trabajador\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\tLlevarlo m\u00e1s lejos - construir autom\u00e1ticamente\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\tConstruye los trabajadores cuando se construya la app\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>Ahora podemos compilar obreras de la forma m\u00e1s b\u00e1sica. Ser\u00eda mejor que no a\u00f1adi\u00e9ramos los trabajadores compilados a nuestro c\u00f3digo fuente, sino que los construy\u00e9ramos junto con el paquete normal. Podemos conseguirlo con bastante facilidad.<\/p>\n<p>En primer lugar, vamos a a\u00f1adir el directorio p\u00fablico de trabajadores (y el directorio de cach\u00e9 de paquetes) a nuestro .gitignore para no confirmarlos.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/\/ en .gitignore\n.parcel-cache\np\u00fablico\/trabajadores\n<\/pre>\n<p>A continuaci\u00f3n, a\u00f1adiremos un comando para construir todos los trabajadores en tu directorio de trabajadores. De vuelta en package.json...<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...resto del archivo\n  \"scripts\": {\n    \/\/ ... resto de scripts\n    \"trabajador:construir\": \"parcel build --dist-dir public\/workers --\",\n    \"workers:build:all\": \"npm run worker:build .\/workers\/*\"\n  }\n}<\/pre>\n<p>Una vez m\u00e1s, Parcela salva el d\u00eda. Utilizar\u00e1 cada archivo del directorio de trabajadores como punto de entrada y crear\u00e1 un paquete distinto para cada uno. Esto te permite tener trabajadores totalmente compartimentados. Cada uno puede importar de node_modules y esas dependencias pasar\u00e1n a formar parte del paquete final de cada trabajador.<\/p>\n<p>Pero no queremos tener que ejecutar un comando de compilaci\u00f3n aparte. Queremos que lo haga junto con el m\u00e1s b\u00e1sico npm run build. Podemos conseguirlo utilizando otro paquete llamado npm-run-all. Esto te permite ejecutar varios comandos npm a la vez, en secuencia o en paralelo.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npm i --save-dev npm-run-all<\/pre>\n<p>Ahora, de vuelta en package.json, vamos a mover el comando de compilaci\u00f3n existente a un nombre diferente para que podamos hacer que el comando de compilaci\u00f3n normal haga varias cosas<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...resto del archivo\n  \"scripts\": {\n    \/\/ ... resto de scripts\n    \"construir\": \"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>Muy bien. Ahora, cada vez que construyas, construir\u00e1s primero todos tus trabajadores y luego ejecutar\u00e1s la construcci\u00f3n normal de react-scripts que crea tu paquete create-react-app.<\/p>\n<p>Ahora puedes utilizar tus trabajadores totalmente transpilados y empaquetados en cualquier parte de tu paquete de aplicaciones existente haciendo:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n    const worker = nuevo 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\tForma a los trabajadores en el desarrollo siempre que cambien\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>La \u00faltima pieza del puzzle es construir los workers cuando ejecutes `npm start`. Parcel tiene un comando \"watch\" incorporado, pero asume que tu archivo se est\u00e1 ejecutando en un entorno con una variable `window`, cosa que no hacen los workers. As\u00ed que podemos implementar nuestro propio vigilante f\u00e1cilmente utilizando un paquete llamado `node-watch`. Empieza por instalarlo:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">npm i --save-dev node-watch<\/pre>\n<p>Ahora crea un nuevo script llamado `watchWorkers.js`. Yo lo puse en un subdirectorio llamado \"scripts\". Copia esto en watchWorkers.js<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">var watch = require('nodo-watch');\nvar { spawn } = require('proceso_hijo');\n\n\/\/ esto crear\u00e1 los trabajadores inicialmente cuando se ejecute el script\ncrear(\n    npm',\n    ['run', 'workers:build'],\n    { stdio: 'heredar' }\n);\nwatch('.\/workers\/', { recursive: true }, function(evt, name) {\n    \/\/ esto construir\u00e1 cada trabajador individual a medida que se actualicen\n    crear(\n        'npm',\n        ['ejecutar', 'trabajador:construir', nombre],\n        { stdio: 'heredar' }\n    );\n});\n<\/pre>\n<p>Ahora tenemos un script que reconstruir\u00e1 cada trabajador a medida que cambie. Una vez m\u00e1s, modifiquemos package.json para que forme parte de nuestro proceso normal<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">{\n\/\/ ...resto del archivo\n  \"scripts\": {\n    \/\/ ... resto de scripts\n    \"inicio\": \"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>Ahora, cada vez que ejecutes `npm start`, ejecutar\u00e1s el react-scripts start normal que vigilar\u00e1 los cambios en el paquete de aplicaciones, pero tambi\u00e9n ejecutar\u00e1s tu propio worker watch que retranspilar\u00e1 cada worker a medida que se actualicen.<\/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 el repo con esta demo completa\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>Llevo un tiempo trabajando en FidgetMap. Creo que podr\u00eda acelerar muchos procesos (incluido el bucle b\u00e1sico de renderizado) a\u00f1adiendo Web Workers para manejar muchas de las tareas de renderizado de forma as\u00edncrona. Todo el juego se dibuja escribiendo datos RGB en matrices y luego componi\u00e9ndolos en un lienzo, as\u00ed que [...]<\/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\/es\/wp-json\/wp\/v2\/posts\/18074","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/comments?post=18074"}],"version-history":[{"count":80,"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/posts\/18074\/revisions"}],"predecessor-version":[{"id":21451,"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/posts\/18074\/revisions\/21451"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/media\/21450"}],"wp:attachment":[{"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/media?parent=18074"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/categories?post=18074"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/carterembry.com\/es\/wp-json\/wp\/v2\/tags?post=18074"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}