<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feeds/rss-style.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Nevermind</title>
        <link>https://blog.m1ng.space/es</link>
        <description>El blog de m1ngsama</description>
        <lastBuildDate>Thu, 13 Aug 2026 08:51:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Astro-Theme-Retypeset with Feed for Node.js</generator>
        <language>es</language>
        <copyright>Copyright © 2026 m1ngsama</copyright>
        <atom:link href="https://blog.m1ng.space/es/rss.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Cómo construir tu propio sistema operativo]]></title>
            <link>https://blog.m1ng.space/es/posts/myarch/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/myarch/</guid>
            <pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Arch Linux sigue el principio del minimalismo, permitiendo a los usuarios construir cualquier funcionalidad que deseen. A continuación, se p...]]></description>
            <content:encoded><![CDATA[<p>Arch Linux sigue el principio del <a href="https://wiki.archlinux.org/title/Arch_Linux">minimalismo</a>, permitiendo a los usuarios construir cualquier funcionalidad que deseen. A continuación, se presenta una guía sencilla para configurar tu propio sistema Arch Linux en una máquina física.</p>
<h2>Preparación</h2>
<p>Necesitarás: una computadora, una unidad USB (o cualquier medio de almacenamiento extraíble), conexión a internet y habilidades básicas de búsqueda de información.</p>
<ul>
<li>Independientemente de la imagen de instalación que elijas, incluso para instalaciones sin conexión, se recomienda tener acceso a internet para garantizar la actualización del kernel y las herramientas. Si eres un usuario experimentado, puedes decidir por tu cuenta.</li>
<li>Si usas Wi-Fi, asegúrate de que el nombre de la red esté en inglés, ya que el entorno tty no puede mostrar caracteres no ASCII, que aparecerán como bloques ilegibles.</li>
<li>Si planeas configurar un arranque dual en el mismo disco, reserva suficiente espacio para Arch Linux: se recomienda al menos 100 GB para futuras instalaciones de software. Asegúrate de que la partición EFI tenga al menos 256 MB o <a href="https://wiki.archlinux.org/title/EFI_system_partition">crea un punto de montaje adicional</a>.</li>
<li>Verifica si la partición de Windows 10 utiliza cifrado BitLocker. Obtén la clave de recuperación con antelación y desactiva el "Inicio rápido" en las opciones de energía.</li>
</ul>
<blockquote>
<p>Antes de proceder, lee cuidadosamente y busca información sobre cualquier aspecto que no entiendas. Opera con precaución y realiza copias de seguridad regularmente: los datos son invaluables.</p>
</blockquote>
<h2>Creación del medio de instalación</h2>
<ol>
<li>Descarga la imagen de instalación únicamente desde la <a href="https://archlinux.org/download/">página oficial de descargas de Arch Linux</a>. Ten en cuenta que Arch Linux es una distribución de lanzamiento continuo.</li>
<li>Si deseas compilar tu propio kernel, consulta la guía de <a href="https://wiki.archlinux.org/title/Kernel/Traditional_compilation">Compilación tradicional del kernel</a>.</li>
<li>Para la imagen de instalación oficial, se recomienda usar <a href="https://www.ventoy.net/">Ventoy</a> para crear un USB booteable.</li>
</ol>
<h2>Instalación base</h2>
<h3>1. Arranque desde el medio de Arch Linux</h3>
<blockquote>
<p>Apaga la computadora, inserta la unidad USB y enciéndela. Ingresa al BIOS, selecciona el USB como dispositivo de arranque, elige la primera opción y presiona Enter para acceder al entorno de instalación de Arch Linux.</p>
</blockquote>
<h3>2. Verificación de UEFI</h3>
<pre><code>systemctl stop reflector.service
# Deshabilita la actualización automática de espejos, ya que las condiciones de red geográficas pueden causar problemas.
</code></pre>
<pre><code>ls /sys/firmware/efi/efivars
# Si se muestra una lista de variables EFI, el sistema está arrancado en modo UEFI. La mayoría de las máquinas en 2025 usan UEFI.
</code></pre>
<h3>3. Configuración de red</h3>
<blockquote>
<p>La instalación de Arch Linux requiere conexión a internet. La instalación sin conexión es más compleja; consulta la guía de <a href="https://wiki.archlinux.org/title/Offline_installation">Instalación sin conexión</a>.</p>
<p>Para conexiones por cable, conecta el cable Ethernet, verifica si el LED de la interfaz parpadea y espera unos segundos para que se establezca la conexión.</p>
<p>En redes de campus, puede ser necesaria la autenticación a través de un enrutador superior. Consulta el proyecto <a href="https://github.com/nbtca/nbtverify">nbtverify</a>.</p>
<p>Para Wi-Fi, utiliza <code>iwctl</code> para conectarte.</p>
</blockquote>
<pre><code>lspci -k | grep Network
# Verifica si el adaptador inalámbrico está funcionando. Si estás seguro de que está operativo, puedes omitir este paso.
</code></pre>
<blockquote>
<p>Comprueba si el kernel ha cargado el controlador de la red inalámbrica.</p>
<p>Deberías ver algo como: <code>00:14.3 Network controller: Intel Corporation Wi-Fi 6 AX201 (rev 20)</code>.</p>
<p>Si no aparece nada, verifica si la conexión inalámbrica está desactivada (blocked: yes).</p>
</blockquote>
<pre><code>rfkill list
# El adaptador inalámbrico suele llamarse wlan0.
</code></pre>
<pre><code>ip link set wlan0 up
# Si aparece un error como “Operation not possible due to RF-kill”, ejecuta:
rfkill unblock wifi
</code></pre>
<pre><code># Conéctate a Wi-Fi usando iwctl
iwctl # Ingresa al modo interactivo
device list # Lista los dispositivos inalámbricos, por ejemplo, wlan0
station wlan0 scan # Escanea las redes
station wlan0 get-networks # Lista las redes Wi-Fi disponibles
station wlan0 connect wifi-name # Conéctate a la red. Los nombres no ASCII no son compatibles. Ingresa la contraseña cuando se solicite.
exit # Salir tras conectar

ping www.google.com # Prueba la conectividad de red
</code></pre>
<blockquote>
<p>Para problemas de configuración de red, consulta <a href="https://wiki.archlinux.org/title/Network_configuration/Wireless">Configuración de red/Inalámbrica</a>.</p>
</blockquote>
<h3>5. Sincronización del reloj del sistema</h3>
<pre><code>timedatectl set-ntp true # Sincroniza la hora del sistema con la hora de la red
timedatectl status # Verifica el estado del servicio
</code></pre>
<h3>6. Actualización de la lista de espejos (opcional para usuarios en España)</h3>
<pre><code>vim /etc/pacman.d/mirrorlist # Edita la lista de espejos si es necesario
Server = https://mirror.librelabucm.org/archlinux/$repo/os/$arch # Espejo de la UCM
Server = https://ftp.rediris.es/mirror/archlinux/$repo/os/$arch # RedIRIS
Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch # Kernel.org
</code></pre>
<h3>7. Creación de particiones Btrfs</h3>
<h4>Verificación de la información del disco</h4>
<pre><code>lsblk
</code></pre>
<p>Muestra la estructura actual de particiones. <strong>Verifica cuidadosamente el disco de destino para la instalación de Arch Linux</strong>.</p>
<p>Convenciones de nomenclatura de discos:</p>
<ul>
<li><strong>Discos SATA</strong>: <code>sda</code>, <code>sdb</code>, <code>sdc</code> … Particiones: <code>sda1</code>, <code>sda2</code>, etc.</li>
<li><strong>Discos NVMe</strong>: <code>nvme0n1</code>, <code>nvme1n1</code> … Particiones: <code>nvme0n1p1</code>, <code>nvme0n1p2</code>, etc.</li>
</ul>
<blockquote>
<p>Este ejemplo usa un disco SATA. Reemplaza <code>/dev/sdx</code> con tu disco real.</p>
</blockquote>
<pre><code>cfdisk /dev/sdx
</code></pre>
<p>Deberías ver una interfaz TUI amigable para particionar. 😄</p>
<h4>Pasos para particionar</h4>
<h5>1. Crear partición Swap</h5>
<ul>
<li>Usa las teclas de flecha para seleccionar <strong>Free space</strong>.</li>
<li>Presiona <code>[New]</code>, presiona Enter e introduce el tamaño (recomendado: 60%–100% de la RAM).</li>
<li>Presiona <code>[Type]</code> y selecciona <strong>Linux swap</strong>.</li>
</ul>
<h5>2. Crear partición raíz (para Btrfs)</h5>
<ul>
<li>Selecciona el Free space restante, presiona <code>[New]</code> y Enter.</li>
<li>Introduce el tamaño (por defecto: usa todo el espacio restante).</li>
<li>Mantén el tipo como <strong>Linux filesystem</strong>.</li>
</ul>
<h5>3. Escribir la tabla de particiones</h5>
<ul>
<li>Selecciona <code>[Write]</code>, escribe <code>yes</code> y presiona Enter.
<blockquote>
<p>⚠️ <strong>Nota: Los cambios no se aplicarán si no se escriben.</strong></p>
</blockquote>
</li>
</ul>
<h4>Formatear particiones</h4>
<h5>Volver a verificar los discos</h5>
<pre><code>fdisk -l
</code></pre>
<h5>Formatear la partición EFI (si es nueva)</h5>
<pre><code>mkfs.fat -F32 /dev/sdxn
</code></pre>
<blockquote>
<p>💡 Para usuarios con arranque dual, puedes reutilizar la partición EFI de Windows sin formatearla, pero asegúrate de que tenga suficiente espacio. Consulta <a href="https://wiki.archlinux.org/title/Dual_boot_with_Windows">Arranque dual con Windows</a>.</p>
</blockquote>
<h5>Formatear la partición Swap</h5>
<pre><code>mkswap /dev/sdxn
</code></pre>
<h5>Formatear la partición Btrfs</h5>
<pre><code>mkfs.btrfs -L myArch /dev/sdxn
</code></pre>
<h4>Crear y montar subvolúmenes Btrfs</h4>
<pre><code>mount -t btrfs -o compress=zstd /dev/sdxn /mnt

# Crear subvolúmenes
btrfs subvolume create /mnt/@        # Subvolumen raíz
btrfs subvolume create /mnt/@home    # Subvolumen para /home

umount /mnt
</code></pre>
<h4>⚠️ Recordatorio final</h4>
<ul>
<li>¡Revisa nuevamente todos los comandos y operaciones!</li>
<li><strong>Los errores pueden causar pérdida de datos, especialmente al eliminar particiones de Windows 😥.</strong></li>
</ul>
<h3>8. Montar particiones, comenzando por la raíz</h3>
<pre><code>mount -t btrfs -o subvol=/@,compress=zstd /dev/sdxn /mnt # Montar el directorio /
mkdir /mnt/home # Crear el directorio /home
mount -t btrfs -o subvol=/@home,compress=zstd /dev/sdxn /mnt/home # Montar el directorio /home
mkdir -p /mnt/boot # Crear el directorio /boot
mount /dev/sdxn /mnt/boot # Montar el directorio /boot
swapon /dev/sdxn # Activar la partición de intercambio
</code></pre>
<pre><code>df -h # Verificar montajes
free -h # Verificar el montaje de la partición de intercambio
</code></pre>
<h3>9. Instalar el sistema</h3>
<pre><code>pacstrap /mnt base base-devel linux linux-firmware btrfs-progs
# Si usas el sistema de archivos Btrfs, instala el paquete btrfs-progs
</code></pre>
<pre><code>pacman -S archlinux-keyring
# Si aparece un error de clave GPG, puede deberse a una imagen desactualizada. Actualiza archlinux-keyring para resolverlo.
</code></pre>
<pre><code>pacstrap /mnt networkmanager vim sudo zsh zsh-completions
# Instala paquetes funcionales esenciales con pacstrap
</code></pre>
<h3>10. Generar el archivo fstab</h3>
<blockquote>
<p>Genera el archivo fstab para definir las particiones del disco basadas en los montajes actuales.</p>
</blockquote>
<pre><code>genfstab -U /mnt &gt; /mnt/etc/fstab
</code></pre>
<h3>11. Entrar al nuevo sistema</h3>
<pre><code>arch-chroot /mnt
# ¿Desapareció el resaltado de código? ¡No te preocupes, has cambiado al chroot con éxito!
</code></pre>
<h3>12. Configurar el nombre del host y la zona horaria</h3>
<pre><code>vim /etc/hostname
# Elige un nombre para el host (evita caracteres especiales o espacios para evitar problemas; no establecer un nombre de host puede causar fallos en algunas aplicaciones GUI).
</code></pre>
<pre><code>vim /etc/hosts
# Edita el archivo hosts
</code></pre>
<blockquote>
<p>Añade lo siguiente (reemplaza myarch con tu nombre de host, usa tabuladores para alinear):</p>
</blockquote>
<pre><code>127.0.0.1   localhost
::1         localhost
127.0.1.1   myarch.localdomain  myarch
</code></pre>
<pre><code>ln -sf /usr/share/zoneinfo/Europe/Madrid /etc/localtime
# Crea un enlace simbólico para la zona horaria de Madrid
</code></pre>
<pre><code>ls /usr/share/zoneinfo/
# Verifica las zonas horarias disponibles y actualiza el comando anterior si es necesario
</code></pre>
<h3>13. Configuración del reloj de hardware</h3>
<pre><code>hwclock --systohc
# Sincroniza la hora del sistema con el reloj de hardware
</code></pre>
<h3>14. Configuración de la localización</h3>
<pre><code>vim /etc/locale.gen
# Edita /etc/locale.gen, descomenta las líneas en_US.UTF-8 UTF-8 y es_ES.UTF-8 UTF-8
# Este paso define el idioma y el conjunto de caracteres para el software
</code></pre>
<pre><code>locale-gen
# Genera las localizaciones
</code></pre>
<pre><code>echo 'LANG=en_US.UTF-8' &gt; /etc/locale.conf
# Configura locale.conf. No se recomienda usar localizaciones en español en tty, ya que pueden causar problemas de codificación
</code></pre>
<h3>15. Establecer la contraseña de root</h3>
<pre><code>passwd root
# La entrada de la contraseña es invisible, ¡no es un problema del teclado! 😄
</code></pre>
<h3>16. Instalar microcódigo</h3>
<pre><code>pacman -S intel-ucode # Para procesadores Intel
pacman -S amd-ucode # Para procesadores AMD
</code></pre>
<h3>17. Instalar el cargador de arranque Grub</h3>
<pre><code>pacman -S grub efibootmgr os-prober
# grub es el cargador de arranque, efibootmgr escribe las entradas de arranque en NVRAM, os-prober permite detectar Windows 10
</code></pre>
<pre><code>grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=ARCH
# Instala grub en la partición EFI
</code></pre>
<pre><code>vim /etc/default/grub
# Edita los parámetros de arranque
</code></pre>
<pre><code># Cambia "loglevel=3 quiet" a "loglevel=5 nowatchdog"
# Añade al final del archivo: GRUB_DISABLE_OS_PROBER=false
</code></pre>
<ul>
<li>Elimina el parámetro <code>quiet</code> de la línea GRUB_CMDLINE_LINUX_DEFAULT.</li>
<li>Cambia <code>loglevel</code> de 3 a 5 para facilitar la depuración de errores.</li>
<li>Añade <code>nowatchdog</code> para mejorar la velocidad de encendido/apagado.</li>
<li>Habilita <code>os-prober</code> para detectar Windows 10.</li>
</ul>
<pre><code>grub-mkconfig -o /boot/grub/grub.cfg
# Genera el archivo de configuración de grub
# Si se detecta Windows 10, aparecerá una línea como: “Found Windows Boot Manager on /dev/nvme0n1p1@/EFI/Microsoft/Boot/bootmgfw.efi done”
# Si Windows está en otro disco, no aparecerá. Una vez en el sistema, vuelve a montar y ejecuta este comando nuevamente.
</code></pre>
<blockquote>
<p>Consulta todos los parámetros en <a href="https://wiki.archlinux.org/title/GRUB">Arch Wiki</a>.</p>
</blockquote>
<h3>18. Finalizar la instalación</h3>
<pre><code>exit # Regresa al entorno de instalación
umount -R /mnt # Desmonta las nuevas particiones
reboot # Reinicia
</code></pre>
<blockquote>
<p>Tras reiniciar, inicia sesión con la cuenta root.</p>
</blockquote>
<pre><code>systemctl enable --now NetworkManager # Habilita y ejecuta el servicio NetworkManager
ping www.google.com # Prueba la conectividad de red
</code></pre>
<blockquote>
<p>Para Wi-Fi:</p>
</blockquote>
<pre><code>nmcli dev wifi list # Lista las redes Wi-Fi cercanas
nmcli dev wifi connect "SSID de Wi-Fi" password "contraseña de red" # Conéctate a una red Wi-Fi específica
</code></pre>
<pre><code>nmtui
# Personalmente, prefiero nmtui, ¡es más amigable! 😄
</code></pre>
<pre><code>pacman -S fastfetch
fastfetch
# Instala fastfetch para verificar la información del sistema
# ¡Llega el momento clásico de neofetch! 😄
</code></pre>
<pre><code>shutdown 0
shutdown -h now
poweroff
# Los tres comandos apagan el sistema. 😄 Apaga correctamente, ya que las políticas de energía aún no están configuradas.
</code></pre>
<h2>¡Felicidades 🎉</h2>
<blockquote>
<p>¡Has instalado con éxito una versión mínima de Arch Linux sin interfaz gráfica!</p>
<p>La guía para la interfaz gráfica se incluirá en la próxima actualización, pero como siempre: ¡lee el manual!</p>
<p>Esta guía es solo un punto de partida, con la esperanza de inspirar a más entusiastas a unirse a la comunidad tecnológica.</p>
</blockquote>
<hr />
<blockquote>
<p>Enlaces relacionados: <a href="https://nbtca.space/">NBTCA</a></p>
</blockquote>
<ul>
<li>📧 Correo de NBTCA: <a href="mailto:contact@nbtca.com">contact@nbtca.com</a></li>
<li>🌐 GitHub de NBTCA: <a href="https://github.com/nbtca">https://github.com/nbtca</a></li>
</ul>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Hydration en profundidad: ¿un “mal necesario” de los frameworks frontend modernos?]]></title>
            <link>https://blog.m1ng.space/es/posts/frameworks/deep-dive-into-hydration/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/frameworks/deep-dive-into-hydration/</guid>
            <pubDate>Sat, 10 Aug 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[El renderizado del lado del servidor (SSR) acelera la carga inicial, pero también introduce Hydration y su coste de rendimiento. ¿Qué es? ¿Por qué causa un “retraso de interacción”? ¿Y qué alternativas explora la comunidad?]]></description>
            <content:encoded><![CDATA[<h2>1. Las ventajas y los problemas del SSR</h2>
<p>El renderizado del lado del servidor (SSR) mejora enormemente la “velocidad de carga de la primera pantalla” (FCP) de las aplicaciones web. El navegador recibe directamente el contenido HTML completo y lo renderiza de inmediato, por lo que el usuario puede ver la página muy pronto, algo muy favorable tanto para el SEO como para la percepción del usuario.</p>
<p>Sin embargo, en ese momento la página solo es una “carcasa estática”. Aunque parece haber terminado de cargar, los clics en botones y las interacciones con campos de entrada no producen ninguna respuesta. Para dar “vida” a esta página estática necesitamos un proceso llamado <strong>Hydration</strong>.</p>
<h2>2. ¿Qué es Hydration?</h2>
<p>Hydration es el proceso mediante el cual el framework JavaScript del cliente “toma el control” del HTML estático renderizado por el servidor.</p>
<p>El proceso es, a grandes rasgos, el siguiente:</p>
<ol>
<li>El navegador descarga y ejecuta los paquetes JavaScript que necesita la página, como el runtime de React o Vue y el código de la aplicación.</li>
<li>El framework reconstruye el árbol de componentes en memoria.</li>
<li>El framework recorre el DOM renderizado por el servidor y conecta listeners de eventos, como <code>onClick</code>, a los nodos DOM correspondientes.</li>
<li>El framework garantiza que el estado de los componentes en el cliente coincida con el estado usado durante el renderizado en el servidor.</li>
</ol>
<p>Solo entonces la página se vuelve realmente interactiva. Podemos imaginar Hydration así: recibes un modelo de LEGO ya montado (HTML), pero para hacer que una de sus piezas se mueva debes leer de principio a fin todo el manual de montaje (JS), comprobar la posición de cada bloque y, solo después, instalar una batería en esa pieza (conectar listeners de eventos).</p>
<h2>3. El problema de Hydration: “una interfaz interactiva no interactiva”</h2>
<p>Hydration resuelve la falta de interacción de las páginas SSR, pero también crea un nuevo cuello de botella de rendimiento, conocido como <strong>“valle inquietante” (Uncanny Valley)</strong> o <strong>“brecha de Hydration” (Hydration Gap)</strong>.</p>
<p>El problema es el intervalo entre el momento en que el usuario ve el contenido de la página (FCP) y el momento en que esta puede responder realmente a sus interacciones (TTI). Durante ese intervalo, la página parece utilizable, pero en realidad está “congelada”.</p>
<p>Las causas son:</p>
<ul>
<li><strong>Bloqueo por la descarga y ejecución de JS</strong>: El navegador debe descargar, analizar y ejecutar una gran cantidad de JavaScript antes de poder iniciar Hydration.</li>
<li><strong>Coste de arranque elevado</strong>: El framework debe realizar mucho trabajo en el cliente para reconstruir el árbol de componentes y conectar listeners, aunque el 90 % de la página sea contenido estático no interactivo.</li>
</ul>
<h2>4. Optimizaciones y alternativas a Hydration</h2>
<p>Para resolver los problemas de rendimiento de Hydration, la comunidad ha explorado varios patrones más avanzados.</p>
<h4>a. Hydration parcial (Partial Hydration)</h4>
<p>La <strong>arquitectura de islas (Islands Architecture)</strong> popularizada por <strong>Astro</strong> es una implementación típica de Hydration parcial. Su idea central es que, de forma predeterminada, todos los componentes producen únicamente HTML estático (cero JS). Los componentes que necesitan interacción se pueden marcar explícitamente como “islas”.</p>
<p>Durante la compilación, Astro solo empaqueta y envía JavaScript para esos componentes “isla”. Así, el navegador solo hidrata unas pocas partes aisladas de la página, en lugar de la página completa, y reduce enormemente la cantidad de JavaScript que debe cargar y ejecutar al arrancar.</p>
<h4>b. Hydration progresiva (Progressive Hydration)</h4>
<p>Es una estrategia de optimización más granular que hidrata los componentes siguiendo un orden de prioridad. Por ejemplo, puede hidratar primero los componentes dentro del viewport o aquellos con los que el usuario está a punto de interactuar, y retrasar los componentes no críticos situados al final de la página.</p>
<h4>c. Reanudabilidad (Resumability)</h4>
<p>El framework <strong>Qwik</strong> propone un concepto revolucionario, la <strong>reanudabilidad (Resumability)</strong>, cuyo objetivo es eliminar Hydration por completo.</p>
<p>Funciona de la siguiente manera:</p>
<ol>
<li><strong>Serialización</strong>: En el servidor, Qwik serializa todo el estado de la aplicación, las relaciones entre componentes, la información de los listeners de eventos y otros datos, y los incrusta en el HTML.</li>
<li><strong>Reanudación</strong>: En el cliente, el diminuto runtime de Qwik (aproximadamente 1KB) no necesita reconstruir el árbol de componentes ni conectar todos los eventos al arrancar. Mediante un listener global, puede saber con precisión qué pequeño fragmento de código debe descargar y ejecutar cuando el usuario pulsa un botón concreto.</li>
</ol>
<p>El objetivo de Qwik es lograr una interacción <strong>Instant-on</strong>. No “vuelve a ejecutar” el trabajo del servidor, sino que “reanuda” la ejecución desde el punto donde el servidor se detuvo.</p>
<h2>Conclusión</h2>
<p>Hydration es el puente entre el renderizado del servidor y la interacción del cliente, pero en las implementaciones tradicionales es un proceso costoso que puede perjudicar la experiencia del usuario. Los frameworks frontend modernos intentan dejar atrás este “mal necesario” mediante patrones innovadores como la Hydration parcial (Astro) y la reanudabilidad (Qwik), avanzando hacia un rendimiento extremo y una interacción más rápida.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Cómo WebAssembly (WASM) está cambiando el desarrollo frontend]]></title>
            <link>https://blog.m1ng.space/es/posts/wasm/wasm-is-changing-frontend/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/wasm/wasm-is-changing-frontend/</guid>
            <pubDate>Sun, 30 Jun 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[JavaScript ya no es el único «jugador» del navegador. Al ofrecer un rendimiento cercano al nativo, WebAssembly (WASM) está llevando a la Web aplicaciones complejas de escritorio como Photoshop y Figma.]]></description>
            <content:encoded><![CDATA[<h2>1. El «segundo lenguaje» del navegador</h2>
<p>Durante mucho tiempo, JavaScript fue prácticamente el único lenguaje del desarrollo frontend para la Web. Aunque ha tenido un enorme éxito, por ser un lenguaje dinámico e interpretado sigue encontrando límites de rendimiento al procesar tareas intensivas de CPU, como el renderizado 3D, la codificación y decodificación de vídeo o los cálculos complejos.</p>
<p>WebAssembly, abreviado WASM, nació precisamente para superar esta situación. No pretende sustituir a JavaScript, sino complementarlo con una herramienta potente que aporta a la plataforma Web un rendimiento y unas posibilidades sin precedentes.</p>
<h2>2. ¿Qué es WebAssembly?</h2>
<p>WebAssembly es un <strong>formato binario de instrucciones</strong> diseñado para una máquina virtual basada en pila. Es un lenguaje de bajo nivel parecido al ensamblador, pero no está pensado para que los desarrolladores lo escriban directamente.</p>
<p>En cambio, está diseñado como <strong>objetivo de compilación</strong> para lenguajes de alto nivel como C, C++, Rust y Go. Puedes escribir código en estos lenguajes de alto rendimiento, compilarlo en un archivo <code>.wasm</code> y ejecutarlo en el navegador a una velocidad cercana a la nativa.</p>
<p><strong>Puntos clave:</strong></p>
<ul>
<li><strong>No sustituye a JavaScript</strong>: WASM y JavaScript trabajan como socios.</li>
<li><strong>Es un objetivo de compilación</strong>: escribes C++ o Rust y lo compilas a WASM.</li>
<li><strong>Es rápido, eficiente y portable</strong>: el rendimiento forma parte de su diseño desde el principio.</li>
</ul>
<h2>3. Cómo colaboran JavaScript y WASM</h2>
<p>Un módulo WASM se ejecuta dentro de un entorno aislado. No puede acceder directamente al DOM, llamar a las API Web ni realizar solicitudes de red. Para todas esas operaciones necesita que JavaScript actúe como una capa intermediaria de «pegamento».</p>
<p>El modelo habitual de colaboración es el siguiente:</p>
<ol>
<li><strong>JavaScript se ocupa de la coordinación</strong>: el código JS gestiona la lógica general de la aplicación, procesa eventos del usuario y actualiza el DOM.</li>
<li><strong>WASM se ocupa del cálculo</strong>: ante una tarea intensiva de cálculo, JS llama a las funciones exportadas por el módulo <code>.wasm</code>.</li>
<li><strong>Intercambio de datos</strong>: JS y WASM pueden intercambiar datos con eficiencia, principalmente valores numéricos y bloques de memoria lineal.</li>
</ol>
<p>Podemos imaginar JavaScript como un «director» y WebAssembly como un «ingeniero experto». El director se encarga de la comunicación y la coordinación, mientras el ingeniero resuelve los problemas técnicos más difíciles.</p>
<h2>4. Casos de uso reales</h2>
<p>WASM ya no es una tecnología experimental. Muchas aplicaciones Web de primer nivel lo usan para impulsar sus funciones principales:</p>
<ul>
<li><strong>Figma</strong>: el motor de renderizado central de esta popular herramienta de diseño en línea está escrito en C++ y compilado a WASM, lo que permite una experiencia fluida de edición gráfica.</li>
<li><strong>Adobe Photoshop &amp; Lightroom</strong>: Adobe llevó con éxito a la Web, mediante WASM, el núcleo en C++ de sus principales aplicaciones de escritorio, permitiendo usar un Photoshop potente desde el navegador.</li>
<li><strong>Google Earth</strong>: la nueva versión de Google Earth se ejecuta por completo en el navegador y WASM impulsa su complejo renderizado 3D del planeta.</li>
<li><strong>AutoCAD Web App</strong>: Autodesk compiló a WASM su gran motor CAD en C++, haciendo posible ejecutar una experiencia completa de AutoCAD en el navegador.</li>
</ul>
<p>Estos casos demuestran que WASM ya puede llevar a la plataforma Web software complejo que antes se consideraba exclusivo de las aplicaciones de escritorio.</p>
<h2>5. El futuro: WASI y más allá del navegador</h2>
<p>La ambición de WASM no se limita al navegador. <strong>WASI (WebAssembly System Interface)</strong> es un estándar emergente que busca proporcionar a WASM un conjunto normalizado de API de sistema, como el acceso al sistema de archivos y a la red.</p>
<p>Esto significa que, en el futuro, los archivos <code>.wasm</code> podrían convertirse en un formato binario universal, multiplataforma y seguro, capaz de ejecutarse en cualquier lugar: desde servidores, donde podría competir con Docker, hasta nodos de computación perimetral y dispositivos del Internet de las cosas. Sería realmente «compilar una vez y ejecutar en cualquier lugar».</p>
<h2>Conclusión</h2>
<p>WebAssembly está transformando profundamente nuestra idea de los límites de una aplicación Web. Permite que JavaScript se concentre en lo que mejor hace —la interacción de la interfaz y la coordinación de la aplicación— y deja el techo de rendimiento en manos de módulos WASM compilados desde lenguajes de sistemas como Rust y C++. Para quienes desarrollan frontend, comprender las capacidades y los escenarios adecuados de WASM será clave para construir la próxima generación de aplicaciones Web de alto rendimiento.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Cómo crear transiciones de página fluidas con la View Transitions API]]></title>
            <link>https://blog.m1ng.space/es/posts/css/view-transitions-api/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/css/view-transitions-api/</guid>
            <pubDate>Mon, 22 Apr 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[El “destello blanco” al navegar entre páginas ha perjudicado durante mucho tiempo la experiencia de usuario. Ahora los navegadores ofrecen la View Transitions API nativa, con la que podemos crear transiciones cinematográficas usando solo unas líneas de código.]]></description>
            <content:encoded><![CDATA[<h2>1. La brecha de experiencia al navegar entre páginas</h2>
<p>En el desarrollo web llevamos mucho tiempo ante un dilema de experiencia de usuario:</p>
<ul>
<li><strong>Aplicaciones multipágina (MPA)</strong>: Su arquitectura es sencilla y estable, pero cada navegación realiza una carga completa, lo que provoca una “pantalla blanca” o un “destello” y rompe la continuidad de la experiencia.</li>
<li><strong>Aplicaciones de una sola página (SPA)</strong>: El enrutamiento frontend ofrece una experiencia fluida sin recargas, pero obliga a los desarrolladores a gestionar manualmente animaciones de transición complejas, el estado y la división del código, con un coste de desarrollo elevado.</li>
</ul>
<p>¿Existe una forma de conservar la sencillez de una MPA y obtener al mismo tiempo las transiciones fluidas de una SPA? La View Transitions API nació precisamente para eso.</p>
<h2>2. ¿Qué es la View Transitions API?</h2>
<p>La View Transitions API es una API nativa del navegador que ofrece un mecanismo para crear fácilmente transiciones animadas entre dos estados distintos del DOM.</p>
<p>Su flujo de trabajo principal es muy sencillo:</p>
<ol>
<li>Al iniciar una transición, la API toma una “captura” de la página actual (el estado anterior).</li>
<li>Después se actualiza el DOM al nuevo estado mediante JavaScript.</li>
<li>La API también toma una “captura” de la nueva página (el estado nuevo).</li>
<li>Por último, el navegador crea una transición predeterminada y fluida, normalmente un fundido cruzado, entre la “captura anterior” y la “captura nueva”.</li>
</ol>
<p>El navegador gestiona todo esto eficientemente a bajo nivel; nosotros solo tenemos que llamar a una función sencilla.</p>
<h2>3. ¿Cómo se utiliza?</h2>
<p>Usar View Transitions en una aplicación de una sola página es muy sencillo. Basta con envolver la lógica que actualiza el DOM en la función <code>document.startViewTransition</code>.</p>
<pre><code>// 假设你有一个更新页面内容的函数
async function updatePageContent(url) {
  const response = await fetch(url);
  const newHtml = await response.text();
  // 用新内容替换旧内容 (具体实现取决于你的架构)
  document.body.innerHTML = newHtml;
}

// 在导航链接的点击事件中调用
document.querySelector('a').addEventListener('click', (event) =&gt; {
  event.preventDefault();
  const url = event.target.href;

  // 检查浏览器是否支持
  if (!document.startViewTransition) {
    updatePageContent(url); // 不支持则直接更新
    return;
  }

  // 使用 View Transition
  document.startViewTransition(() =&gt; updatePageContent(url));
});
</code></pre>
<p>¡Solo con esto, la navegación ya tendrá un efecto de fundido cruzado predeterminado!</p>
<h2>4. Personalizar la animación de transición</h2>
<p>El fundido predeterminado es excelente, pero el verdadero poder de la API está en su capacidad de personalización. Cuando se ejecuta <code>startViewTransition</code>, el navegador crea una estructura DOM que contiene los siguientes pseudoelementos:</p>
<ul>
<li><code>::view-transition</code>: Elemento raíz que contiene toda la transición.</li>
<li><code>::view-transition-old(root)</code>: La “captura” de la vista anterior.</li>
<li><code>::view-transition-new(root)</code>: La “captura” de la vista nueva.</li>
</ul>
<p>Podemos sustituir el efecto predeterminado mediante una <code>animation</code> de CSS; por ejemplo, para crear una animación de entrada lateral:</p>
<pre><code>@keyframes slide-in {
  from { transform: translateX(100%); }
}

::view-transition-new(root) {
  animation: slide-in 0.5s ease-out;
}
</code></pre>
<h2>5. La “transformación” entre elementos: <code>view-transition-name</code></h2>
<p>La función más sorprendente de la API permite reconocer <strong>elementos diferentes</strong> de dos páginas como <strong>el mismo objeto</strong> y crear una animación de “transformación” fluida entre ambos.</p>
<p>Esto se consigue mediante la propiedad CSS <code>view-transition-name</code>.</p>
<p><strong>Página A (lista):</strong></p>
<pre><code>&lt;img src="thumbnail.jpg" style="view-transition-name: hero-image;" /&gt;
</code></pre>
<p><strong>Página B (detalle):</strong></p>
<pre><code>&lt;img src="full-size.jpg" style="view-transition-name: hero-image;" /&gt;
</code></pre>
<p>Al navegar de la página A a la página B, el navegador detecta que ambos elementos tienen el mismo <code>view-transition-name</code>. Entonces calcula automáticamente las diferencias de posición, tamaño y forma, y genera una transición fluida en lugar de un simple fundido. Esto funciona especialmente bien en transiciones de imágenes, tarjetas y elementos similares.</p>
<h2>Conclusión</h2>
<p>La View Transitions API aporta a la web la esperada capacidad nativa de crear transiciones. Simplifica enormemente efectos de interacción que antes requerían complejas bibliotecas de animación JavaScript y facilita más que nunca la creación de experiencias fluidas y cinematográficas. Con su integración en frameworks como Astro y Nuxt, y su futura adopción en aplicaciones multipágina, se convertirá sin duda en una habilidad estándar para los desarrolladores web modernos.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[React Server Components en profundidad]]></title>
            <link>https://blog.m1ng.space/es/posts/react/deep-dive-into-rsc/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/react/deep-dive-into-rsc/</guid>
            <pubDate>Fri, 15 Mar 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[React Server Components (RSC) representa uno de los cambios de paradigma más importantes de React en los últimos años. Este artículo analiza sus conceptos principales, los problemas que resuelve y cómo transforma nuestra forma de crear aplicaciones.]]></description>
            <content:encoded><![CDATA[<h2>1. ¿Qué son los React Server Components (RSC)?</h2>
<p>Los React Server Components (RSC) son un nuevo tipo de componente React que se renderiza <strong>durante la compilación o en el servidor</strong>. A diferencia de los componentes tradicionales renderizados en el navegador del cliente —ahora denominados «Client Components»—, el código de los RSC nunca se envía al cliente. El navegador solo recibe el HTML ya renderizado o un formato especial de transmisión en streaming.</p>
<p>Esto marca la evolución de React desde una biblioteca puramente del lado del cliente hasta un framework full-stack capaz de trabajar de forma fluida entre el servidor y el cliente.</p>
<h2>2. ¿Qué problemas resuelven los RSC?</h2>
<p>Los RSC surgieron principalmente para resolver los siguientes problemas fundamentales:</p>
<ul>
<li><strong>Paquetes de JavaScript enormes</strong>: Las aplicaciones React tradicionales empaquetan y envían al cliente el código JS de todos los componentes, incluso el de aquellos que no son directamente interactivos, lo que ralentiza la carga inicial. Los RSC permiten conservar en el servidor una gran cantidad de componentes —como textos, diseños y visualizaciones de datos—, logrando que tengan un <strong>peso de JS igual a cero</strong>.</li>
<li><strong>Cascadas de peticiones (Request Waterfalls)</strong>: El patrón típico para obtener datos en el cliente consiste en: renderizar un componente -&gt; <code>useEffect</code> -&gt; realizar una petición -&gt; esperar los datos -&gt; volver a renderizar. Si los componentes están profundamente anidados, se forma una cascada de peticiones que retrasa la presentación de la página. Los RSC pueden obtener los datos directamente en el servidor con <code>async/await</code> y transmitirlos junto con los componentes al cliente, resolviendo el problema de raíz.</li>
<li><strong>Acceso directo a recursos del backend</strong>: Los RSC se ejecutan en un entorno de servidor como Node.js, lo que significa que pueden acceder directa y seguramente a bases de datos, sistemas de archivos o APIs internas sin exponer endpoints adicionales al cliente.</li>
</ul>
<h2>3. Server Components frente a Client Components</h2>
<p>Comprender la diferencia entre ambos es la clave para dominar los RSC.</p>
<table>
<thead>
<tr>
<th>Característica</th>
<th>Server Components (componentes de servidor)</th>
<th>Client Components (componentes de cliente)</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Entorno de ejecución</strong></td>
<td>Servidor (Node.js, etc.)</td>
<td>Cliente (navegador)</td>
</tr>
<tr>
<td><strong>JS enviado al cliente</strong></td>
<td>No</td>
<td>Sí</td>
</tr>
<tr>
<td><strong>Estado (State)</strong></td>
<td>No compatible (p. ej., <code>useState</code>)</td>
<td>Compatible</td>
</tr>
<tr>
<td><strong>Ciclo de vida/Hooks</strong></td>
<td>No compatible (p. ej., <code>useEffect</code>)</td>
<td>Compatible</td>
</tr>
<tr>
<td><strong>Interacción (eventos)</strong></td>
<td>No compatible (p. ej., <code>onClick</code>)</td>
<td>Compatible</td>
</tr>
<tr>
<td><strong>Obtención de datos</strong></td>
<td>Compatible con <code>async/await</code></td>
<td>Mediante <code>useEffect</code> o una biblioteca de obtención de datos</td>
</tr>
<tr>
<td><strong>Reglas de importación</strong></td>
<td>No puede importar Client Components</td>
<td>Puede importar Server Components (como <code>children</code> o <code>prop</code>)</td>
</tr>
</tbody>
</table>
<p><strong>Regla práctica</strong>: Considera todos los componentes como Server Components de forma predeterminada. Solo cuando el componente necesite <code>useState</code>, <code>useEffect</code> o gestionar eventos del usuario como <code>onClick</code>, añade la directiva <code>"use client";</code> al principio del archivo para marcarlo como Client Component.</p>
<h2>4. ¿Cómo trabajan juntos?</h2>
<p>Una aplicación React moderna será una combinación de ambos. Por ejemplo, en la página de un artículo de blog:</p>
<ul>
<li><code>PageLayout</code> (Server Component): Se encarga del diseño general.</li>
<li><code>ArticleContent</code> (Server Component): Obtiene los datos del artículo desde una base de datos o un archivo Markdown y los renderiza.</li>
<li><code>LikeButton</code> (Client Component): Contiene <code>useState</code> y un evento <code>onClick</code> para gestionar la interacción de «Me gusta».</li>
<li><code>Comments</code> (Client Component): Obtiene y muestra los comentarios e incluye interacciones como el envío de formularios.</li>
</ul>
<p>Los Server Components pueden reservar en el servidor los «huecos» que ocuparán los Client Components y, finalmente, el navegador ensambla ambos de forma fluida.</p>
<h2>Conclusión</h2>
<p>React Server Components representa un profundo cambio de paradigma que amplía las capacidades de React desde el navegador hasta el servidor y aporta ventajas considerables de rendimiento y una mejor experiencia de desarrollo. Aunque introduce un nuevo modelo mental, su uso práctico mediante frameworks como el App Router de Next.js está convirtiendo rápidamente a RSC en un nuevo estándar para crear aplicaciones Web escalables y de alto rendimiento.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Adiós a process.env.UNDEFINED: variables de entorno con seguridad de tipos]]></title>
            <link>https://blog.m1ng.space/es/posts/typescript/typesafe-environment-variables/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/typescript/typesafe-environment-variables/</guid>
            <pubDate>Sun, 25 Feb 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Las variables de entorno ausentes o con un formato incorrecto son una fuente habitual de errores en tiempo de ejecución. Con una biblioteca de validación como Zod podemos comprobar al arrancar que todas existen y tienen el tipo correcto, eliminando este tipo de problema.]]></description>
            <content:encoded><![CDATA[<h2>1. La «trampa» de <code>process.env</code></h2>
<p>En una aplicación Node.js accedemos a las variables de entorno mediante <code>process.env</code>. Es un mecanismo sencillo y eficaz, pero tiene un problema inherente: <strong>no ofrece seguridad de tipos</strong>.</p>
<p>Los valores del objeto <code>process.env</code> son <code>string</code> o <code>undefined</code>. Esto provoca varios problemas habituales:</p>
<ul>
<li><strong>Un <code>undefined</code> inesperado</strong>: si olvidas definir una variable en el archivo <code>.env</code> o en el servidor, <code>process.env.MY_VAR</code> será <code>undefined</code> en tiempo de ejecución. Esto puede causar un <code>TypeError</code> en una parte profunda de la lógica.</li>
<li><strong>Tipos que no coinciden</strong>: quizá esperes que un número de puerto sea de tipo <code>number</code>, pero <code>process.env.PORT</code> siempre es una cadena y tienes que llamar manualmente a <code>parseInt</code> en cada lugar donde lo utilizas.</li>
<li><strong>Lógica de validación dispersa</strong>: solemos escribir código defensivo en distintos rincones, como <code>const port = process.env.PORT || 3000;</code>, lo que desordena la gestión de las variables de entorno.</li>
</ul>
<h2>2. Principio fundamental: fallar cuanto antes</h2>
<p>Para una configuración crítica como las variables de entorno, una buena práctica es <strong>fallar cuanto antes (Fail-Fast)</strong>.</p>
<p>Esto significa que la aplicación debe comprobar al comienzo del arranque si se han proporcionado todas las variables obligatorias y si su formato es correcto. Ante cualquier problema, debe lanzar inmediatamente un error y detenerse, en lugar de fallar más adelante, en un momento imprevisible, debido a una configuración incorrecta.</p>
<p>Así descubrimos los problemas de configuración de inmediato durante el despliegue o el desarrollo, y no cuando una persona accede a la aplicación.</p>
<h2>3. Validación con seguridad de tipos mediante Zod</h2>
<p>Zod es una biblioteca de declaración y validación de esquemas diseñada primero para TypeScript. Resulta especialmente adecuada para resolver el problema de seguridad de tipos de las variables de entorno.</p>
<p>Nuestra estrategia es:</p>
<ol>
<li>Definir un esquema Zod para todas las variables de entorno.</li>
<li>Analizar <code>process.env</code> con ese esquema al arrancar la aplicación.</li>
<li>Si el análisis falla —es decir, si no pasa la validación—, Zod lanza un error y el arranque de la aplicación falla.</li>
<li>Si el análisis tiene éxito, obtenemos un objeto completamente tipado que utilizamos en toda la aplicación.</li>
</ol>
<h4>Ejemplo de implementación</h4>
<p>Primero, instala Zod: <code>pnpm add zod</code></p>
<p>Después, crea un archivo dedicado a las variables de entorno, por ejemplo <code>src/env.ts</code>:</p>
<pre><code>// src/env.ts
import { z } from 'zod';

// 1. 定义 Schema
const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  DATABASE_URL: z.string().min(1, 'DATABASE_URL is required.'),
  PORT: z.coerce.number().int().positive().default(3000),
  // z.coerce 会尝试将字符串转换为数字
});

// 2. 解析和导出
// .parse 会在验证失败时抛出错误，实现 "Fail-Fast"
export const env = envSchema.parse(process.env);
</code></pre>
<p>Ahora puedes importar desde <code>src/env</code> el objeto <code>env</code> en cualquier otra parte de la aplicación.</p>
<pre><code>// src/server.ts
import { env } from './env'; // 导入经过验证和类型化的 env 对象

// env.PORT 的类型是 `number`，而不是 `string | undefined`
const port = env.PORT;

// env.NODE_ENV 的类型是 'development' | 'production' | 'test'
if (env.NODE_ENV === 'development') {
  console.log('Running in development mode');
}

// 如果 DATABASE_URL 未设置，应用在启动时就已经崩溃了，
// 所以在这里我们可以放心地认为它是存在的，并且类型是 `string`。
connectToDatabase(env.DATABASE_URL);
</code></pre>
<h2>4. Ventajas</h2>
<p>Este patrón aporta beneficios inmediatos:</p>
<ul>
<li><strong>Seguridad de tipos completa</strong>: cuando accedes a <code>env.PORT</code> en el código, TypeScript sabe que es un <code>number</code>.</li>
<li><strong>Documentación y validación centralizadas</strong>: el propio archivo <code>env.ts</code> se convierte en la documentación oficial de las variables de entorno y reúne toda la lógica de validación.</li>
<li><strong>Ejecución fiable</strong>: evita errores en tiempo de ejecución provocados por variables de entorno mal escritas, ausentes o con formato incorrecto.</li>
<li><strong>Fallo temprano</strong>: cualquier problema de configuración se descubre inmediatamente durante el despliegue o el desarrollo.</li>
</ul>
<h2>Conclusión</h2>
<p>Adelantar la validación de las variables de entorno al arranque de la aplicación y utilizar una herramienta como Zod para garantizar la seguridad de tipos es una práctica de ingeniería con una excelente relación entre esfuerzo y beneficio. Mejora notablemente la robustez de la aplicación y la confianza de quienes la desarrollan, y constituye una parte imprescindible de un proyecto TypeScript moderno.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Actualización de Vue 3.4: una sintaxis v-bind más sencilla y mejor rendimiento]]></title>
            <link>https://blog.m1ng.space/es/posts/vue/vue-3-4-v-bind-updates/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/vue/vue-3-4-v-bind-updates/</guid>
            <pubDate>Mon, 15 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Vue 3.4 «🏀 Slam Dunk» ya está disponible con una reescritura más eficiente del sistema reactivo y una API defineModel estable, que mejora enormemente la experiencia al desarrollar componentes con enlace bidireccional.]]></description>
            <content:encoded><![CDATA[<h2>1. Introducción a Vue 3.4 «Slam Dunk»</h2>
<p>A comienzos de 2024, el equipo de Vue publicó la versión 3.4, cuyo nombre en clave es «Slam Dunk». Aunque no es una versión tan revolucionaria como la 3.0, incorpora mejoras importantes en dos áreas clave: la <strong>optimización interna del rendimiento</strong> y la <strong>mejora de la experiencia de desarrollo</strong>.</p>
<p>Los cambios más destacados incluyen la reescritura del sistema reactivo, la estabilización de la API <code>defineModel</code> y una sintaxis de <code>v-bind</code> más concisa.</p>
<h2>2. Reescritura del sistema reactivo</h2>
<p>Vue 3.4 incluye una importante reescritura de su sistema reactivo central. El objetivo principal de este trabajo es mejorar la eficiencia de las propiedades calculadas con <code>computed</code>.</p>
<p>En versiones anteriores, una propiedad calculada podía volver a evaluarse innecesariamente aunque sus dependencias no hubieran cambiado realmente. Gracias a un seguimiento de dependencias más inteligente, la nueva versión garantiza que el cálculo solo se active cuando sea verdaderamente necesario, reduciendo los rerenderizados innecesarios de componentes.</p>
<p>Para la mayoría de quienes desarrollan, esto es una «comida gratis»: no hay que modificar el código y basta con actualizar a la versión 3.4 para obtener automáticamente la mejora de rendimiento.</p>
<h2>3. <code>defineModel</code>: enlace bidireccional elegante para componentes</h2>
<p>Antes de la versión 3.4, implementar en un componente un enlace bidireccional similar a <code>v-model</code> exigía bastante código repetitivo:</p>
<p><strong>Antes 👎:</strong></p>
<pre><code>&lt;script setup&gt;
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

// 通常需要一个计算属性来包装
const value = computed({
  get: () =&gt; props.modelValue,
  set: (val) =&gt; emit('update:modelValue', val)
})
&lt;/script&gt;

&lt;template&gt;
  &lt;input v-model="value" /&gt;
&lt;/template&gt;
</code></pre>
<p>Vue 3.4 lleva la API <code>defineModel</code> de la fase experimental a una versión estable. Ahora, el mismo comportamiento solo requiere una línea de código:</p>
<p><strong>Ahora (Vue 3.4) 👍:</strong></p>
<pre><code>&lt;script setup&gt;
const model = defineModel()
&lt;/script&gt;

&lt;template&gt;
  &lt;input v-model="model" /&gt;
&lt;/template&gt;
</code></pre>
<p>La macro <code>defineModel</code> registra automáticamente la prop <code>modelValue</code> y el evento <code>update:modelValue</code>, y devuelve un <code>ref</code> que se puede leer y escribir directamente. Esto simplifica enormemente el desarrollo de componentes compatibles con <code>v-model</code>.</p>
<h2>4. Forma abreviada de <code>v-bind</code> para nombres iguales</h2>
<p>Otra mejora sintáctica de la experiencia de desarrollo es la forma abreviada de <code>v-bind</code> cuando los nombres coinciden. Si la prop que se pasa a un componente tiene el mismo nombre que una variable definida en <code>&lt;script setup&gt;</code>, ahora se puede omitir el valor del atributo.</p>
<p><strong>Antes 👎:</strong></p>
<pre><code>&lt;script setup&gt;
const id = 'my-id'
const title = 'Hello Vue'
&lt;/script&gt;

&lt;template&gt;
  &lt;MyComponent :id="id" :title="title" /&gt;
&lt;/template&gt;
</code></pre>
<p><strong>Ahora (Vue 3.4) 👍:</strong></p>
<pre><code>&lt;script setup&gt;
const id = 'my-id'
const title = 'Hello Vue'
&lt;/script&gt;

&lt;template&gt;
  &lt;MyComponent :id :title /&gt;
&lt;/template&gt;
</code></pre>
<p>Este pequeño cambio hace que el código de las plantillas sea más limpio y fácil de leer.</p>
<h2>Conclusión</h2>
<p>Vue 3.4 es una iteración sólida. Mediante optimizaciones de rendimiento en las capas internas y la simplificación de las API de alto nivel, refuerza la filosofía de Vue como framework «progresivo»: ofrece sólidas garantías de rendimiento para aplicaciones grandes y, al mismo tiempo, ventajas prácticas para el trabajo diario. Destaca especialmente la estabilización de <code>defineModel</code>, que resuelve un problema persistente al encapsular componentes y es el aspecto más elogiable de esta actualización.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Adiós Rome, hola Biome: una nueva cadena de herramientas frontend todo en uno]]></title>
            <link>https://blog.m1ng.space/es/posts/build-tools/intro-to-biome/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/build-tools/intro-to-biome/</guid>
            <pubDate>Fri, 10 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[La visión de Rome entusiasmó a innumerables desarrolladores. Tras fracasar su comercialización, la comunidad continuó su legado bajo el nombre Biome. ¿Podrá esta cadena de herramientas todo en uno escrita en Rust convertirse en el próximo miembro de nuestro repertorio?]]></description>
            <content:encoded><![CDATA[<h2>1. La «fatiga de herramientas» en el desarrollo frontend</h2>
<p>En el desarrollo frontend moderno, un proyecto suele necesitar una combinación compleja de herramientas para funcionar con fluidez:</p>
<ul>
<li><strong>Linter</strong>: ESLint comprueba la calidad del código JavaScript/TypeScript.</li>
<li><strong>Formateador</strong>: Prettier unifica el estilo del código.</li>
<li><strong>Compilador</strong>: Babel o TypeScript (tsc) transforman el código.</li>
<li><strong>Empaquetador</strong>: Webpack, Rollup o Vite empaquetan la aplicación.</li>
</ul>
<p>Gestionar la configuración, los plugins, las versiones y las interacciones de estas herramientas ya supone por sí solo una cantidad considerable de trabajo. Es lo que se conoce como «fatiga de herramientas». Durante años, la comunidad ha explorado la posibilidad de una cadena de herramientas todo en uno, con la esperanza de resolver todos los problemas con una sola herramienta.</p>
<h2>2. La visión de Rome y el nacimiento de Biome</h2>
<p>Rome fue un proyecto ambicioso iniciado por Sebastian McKenzie, antiguo empleado de Facebook y autor de Babel, con el objetivo de reescribir en TypeScript toda la cadena de herramientas frontend. Su visión era ofrecer una herramienta unificada, de alto rendimiento y sin configuración. Sin embargo, tras años de desarrollo e intentos de comercialización, Rome Labs anunció en 2023 el cese de sus operaciones.</p>
<p>Por suerte, el código central de Rome era de código abierto. La comunidad actuó con rapidez, creó un fork llamado <strong>Biome</strong> y confió su mantenimiento a una organización comunitaria dedicada a heredar y materializar la visión original de Rome.</p>
<h2>3. ¿Qué es Biome?</h2>
<p>Biome es una cadena de herramientas frontend de alto rendimiento reescrita en Rust. Busca ofrecer una experiencia de desarrollo unificada y extremadamente rápida que sustituya a una serie de herramientas independientes como ESLint y Prettier.</p>
<p>A finales de 2023, las funciones principales de Biome se concentraban sobre todo en el <strong>Linter</strong> y el <strong>Formateador</strong>, donde ya demostraba una capacidad impresionante.</p>
<h4>Principales ventajas:</h4>
<ul>
<li><strong>Rendimiento excepcional</strong>: Gracias a Rust, Biome funciona mucho más rápido que ESLint y Prettier, escritos en JavaScript. En bases de código grandes, la mejora puede alcanzar un orden de magnitud.</li>
<li><strong>Configuración unificada</strong>: Un único archivo <code>biome.json</code> permite gestionar el comportamiento de todas las herramientas y sustituir archivos dispersos como <code>.eslintrc</code>, <code>.prettierrc</code> y <code>.editorconfig</code>.</li>
<li><strong>Diagnósticos detallados</strong>: El Linter de Biome no solo señala los errores, sino que también ofrece abundante contexto y sugerencias de corrección para ayudar a los desarrolladores a entender la raíz del problema.</li>
<li><strong>Compatibilidad con Prettier</strong>: El formateador de Biome busca ser compatible con más del 95% de las reglas de Prettier, por lo que el coste de migrar desde Prettier es muy bajo.</li>
</ul>
<h2>4. Primeros pasos</h2>
<p>Puedes probar rápidamente Biome en tu proyecto mediante <code>npx</code>:</p>
<pre><code># 检查当前项目中的代码问题
npx @biomejs/biome check ./src

# 自动修复可修复的 lint 问题
npx @biomejs/biome check --apply ./src

# 格式化代码
npx @biomejs/biome format --write ./src
</code></pre>
<p>Crea un archivo <code>biome.json</code> para personalizar las reglas y la configuración:</p>
<pre><code>{
  "$schema": "https://biomejs.dev/schemas/1.4.1/schema.json",
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  }
}
</code></pre>
<h2>5. Perspectivas de futuro</h2>
<p>La hoja de ruta de Biome es muy clara: sobre la base de estabilizar el Linter y el Formateador, implementará gradualmente un Compilador, Empaquetador, Ejecutor de pruebas y otras funciones, hasta convertirse en una verdadera cadena de herramientas todo en uno.</p>
<p>Aunque todavía es joven, su excelente rendimiento, su modelo abierto impulsado por la comunidad y su atención a la experiencia de desarrollo ya han convertido a Biome en una nueva fuerza imposible de ignorar entre las herramientas frontend. Para los equipos que buscan simplificar su conjunto de herramientas y mejorar la eficiencia del desarrollo, Biome ofrece una opción de futuro muy atractiva.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Bun 1.0 ya está aquí: el runtime todo en uno que desafía a Node.js]]></title>
            <link>https://blog.m1ng.space/es/posts/build-tools/bun-1-0-release/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/build-tools/bun-1-0-release/</guid>
            <pubDate>Sun, 10 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[En septiembre de 2023, el lanzamiento de Bun 1.0 sacudió a la comunidad JavaScript. Creado desde cero, este kit de herramientas desafió oficialmente el dominio de Node.js con su asombrosa velocidad y su diseño todo en uno.]]></description>
            <content:encoded><![CDATA[<h2>1. Un nuevo competidor entre los runtimes de JavaScript</h2>
<p>Durante más de una década, Node.js ha sido sinónimo de JavaScript en el servidor. Aunque Deno propuso ideas nuevas sobre seguridad y API modernas, la posición de Node.js nunca pareció verse realmente amenazada. Sin embargo, en septiembre de 2023, el lanzamiento oficial de Bun 1.0 indicó que esto podría cambiar.</p>
<p>Bun no es simplemente otro runtime de JavaScript, sino un kit de herramientas todo en uno, diseñado desde cero y centrado en el rendimiento, que busca cubrir todo el ciclo de vida desde el desarrollo hasta el despliegue.</p>
<h2>2. El núcleo de Bun: velocidad, velocidad y más velocidad</h2>
<p>El principal objetivo de diseño de Bun es el rendimiento. Para lograrlo, tomó varias decisiones técnicas particulares:</p>
<ul>
<li><strong>El lenguaje Zig</strong>: Bun está implementado en Zig, un lenguaje moderno de programación de sistemas centrado en el rendimiento y el control de la memoria, lo que permite optimizar al máximo los detalles de bajo nivel.</li>
<li><strong>El motor JavaScriptCore</strong>: A diferencia de Node.js y Deno, que utilizan el motor V8 de Google, Bun eligió JavaScriptCore (JSC) de Apple. JSC es conocido por arrancar más rápido y consumir generalmente menos memoria.</li>
</ul>
<p>Estas decisiones permiten que Bun muestre cifras de rendimiento sorprendentes al iniciar scripts, en la velocidad de <code>bun install</code> y en la eficiencia de ejecución de sus API integradas.</p>
<h2>3. Mucho más que un runtime</h2>
<p>El diseño todo en uno de Bun es otro de sus rasgos distintivos. No es solo un sustituto del comando <code>node</code>; también incorpora:</p>
<ul>
<li><strong>Gestor de paquetes</strong>: <code>bun install</code> es varias o incluso decenas de veces más rápido que <code>npm install</code>. Utiliza una caché global de módulos y algoritmos eficientes de resolución de dependencias para reducir considerablemente el tiempo de instalación.</li>
<li><strong>Herramienta de compilación/empaquetador</strong>: Bun incluye un empaquetador de alto rendimiento capaz de convertir directamente un proyecto en un ejecutable o en código para el navegador, con un rendimiento comparable al de esbuild.</li>
<li><strong>Transpilador nativo</strong>: Puedes ejecutar directamente archivos TypeScript (<code>.ts</code>) y JSX (<code>.jsx</code>, <code>.tsx</code>) sin configurar antes <code>tsc</code> o <code>babel</code>. Bun los transforma a gran velocidad durante la ejecución.</li>
<li><strong>Ejecutor de pruebas</strong>: <code>bun test</code> ofrece un entorno de pruebas muy compatible con Jest, pero con una velocidad de ejecución mucho mayor.</li>
</ul>
<p>Esto significa que, para un proyecto nuevo, quizá solo necesites instalar la herramienta <code>bun</code> para completar todo el proceso de desarrollo, pruebas y empaquetado.</p>
<h2>4. Compatibilidad con Node.js</h2>
<p>Para facilitar la migración, el equipo de Bun ha dedicado un gran esfuerzo a la compatibilidad con las API de Node.js. Incluye soporte para la resolución de <code>node_modules</code>, módulos CommonJS (<code>require</code>) y ESM (<code>import</code>), así como numerosos módulos centrales de Node.js, como <code>fs</code>, <code>path</code> y <code>http</code>.</p>
<p>En teoría, muchos proyectos existentes de Node.js pueden ejecutarse directamente con <code>bun</code> y beneficiarse de inmediato de mejoras de rendimiento.</p>
<h2>5. Desafíos y futuro</h2>
<p>La aparición de Bun desafía la filosofía, arraigada desde hace tiempo en el mundo frontend, de «combinar pequeñas herramientas especializadas». Su modelo amplio e integrado ofrece una comodidad incomparable y un gran rendimiento desde el primer momento, aunque también puede sacrificar parte de la flexibilidad.</p>
<p>Node.js posee un ecosistema enorme y maduro que Bun difícilmente podrá igualar a corto plazo. Sin embargo, el lanzamiento de Bun 1.0 marcó que ya estaba preparado para producción. Su asombroso rendimiento y su experiencia de desarrollo integrada resultan irresistibles para proyectos nuevos y equipos que buscan la máxima eficiencia.</p>
<p>Independientemente de que Bun llegue o no a destronar a Node.js, su aparición ya ha dado un nuevo impulso a la evolución de la cadena de herramientas JavaScript y ha establecido un nuevo referente de rendimiento.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[¡El anidamiento nativo de CSS por fin ha llegado!]]></title>
            <link>https://blog.m1ng.space/es/posts/css/native-css-nesting/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/css/native-css-nesting/</guid>
            <pubDate>Fri, 01 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[¡Esperamos diez años! El anidamiento de selectores, una función esencial de Sass/Less, por fin cuenta con soporte nativo en los principales navegadores. Despídete de los preprocesadores y da la bienvenida a una forma más limpia e intuitiva de escribir CSS.]]></description>
            <content:encoded><![CDATA[<h2>1. La función «no oficial» que usamos durante una década</h2>
<p>Si escribiste código frontend hace algunos años, seguro que conoces Sass o Less. Estos preprocesadores CSS ofrecen una función que encanta a casi todos los desarrolladores: el <strong>anidamiento de selectores</strong>. Permite escribir las reglas de los selectores hijos dentro del selector padre, como al escribir HTML, lo que mejora enormemente la legibilidad y organización del código.</p>
<pre><code>// Sass 语法
nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li { display: inline-block; }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}
</code></pre>
<p>Durante años dependimos de herramientas de compilación para convertir esta sintaxis en CSS normal que los navegadores pudieran entender. Pero ahora la situación ha cambiado.</p>
<h2>2. Llega el anidamiento nativo de CSS</h2>
<p>Desde principios de 2023, los principales navegadores —Chrome, Safari y Firefox— anunciaron sucesivamente su compatibilidad con el módulo nativo CSS Nesting. Esto significa que podemos escribir reglas anidadas directamente en archivos <code>.css</code>, sin ningún preprocesamiento.</p>
<p>El ejemplo anterior se puede escribir con CSS nativo así:</p>
<pre><code>/* 原生 CSS 语法 */
nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li { display: inline-block; }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}
</code></pre>
<p>Sí, ¡parece exactamente Sass!</p>
<h2>3. El importante papel de <code>&amp;</code></h2>
<p>Al igual que en los preprocesadores, el símbolo <code>&amp;</code> también desempeña un papel fundamental en el anidamiento nativo: representa al selector padre. Resulta especialmente útil al trabajar con pseudoclases, pseudoelementos o selectores combinados.</p>
<pre><code>.card {
  background: white;
  border-radius: 8px;

  /* &amp; 代表 .card */
  &amp;:hover {
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  }

  /* &amp; 代表 .card，组合成 .card.dark-mode */
  &amp;.dark-mode {
    background: #333;
  }

  /* &amp; 代表 .card，组合成 .card &gt; .header */
  &gt; .header {
    font-weight: bold;
  }
}
</code></pre>
<h2>4. Una diferencia pequeña pero importante respecto a Sass</h2>
<p>En Sass puedes anidar directamente un selector de clase, por ejemplo <code>.card { .title { ... } }</code>. Esto no estaba permitido en las primeras versiones de la especificación nativa CSS Nesting: toda regla anidada tenía que empezar con un símbolo como <code>&amp;</code>, <code>&gt;</code>, <code>+</code> o <code>~</code>.</p>
<p>Aunque la especificación más reciente ha relajado esta restricción y permite anidar directamente selectores de tipo, como <code>article { h1 { ... } }</code>, al anidar selectores de clase sigue siendo una buena práctica utilizar <code>&amp;</code> para ser explícitos y evitar ambigüedades.</p>
<pre><code>/* 推荐写法 */
.article {
  &amp; .author-name {
    font-style: italic;
  }
}

/* 不推荐的写法 (在某些早期实现或严格解析器中可能无效) */
.article {
  .author-name {
    font-style: italic;
  }
}
</code></pre>
<p>El uso de <code>&amp;</code> expresa claramente que <code>.author-name</code> es descendiente de <code>.article</code>.</p>
<h2>5. Compatibilidad de navegadores y futuro</h2>
<p>En septiembre de 2023, todos los navegadores modernos principales —Chrome 112+, Safari 16.5+ y Firefox 117+— ya eran compatibles con CSS Nesting. Esto significa que podemos empezar a adoptarlo gradualmente en producción.</p>
<p>La llegada del anidamiento nativo de CSS es otro hito importante en la evolución de la propia plataforma web. Reduce nuestra dependencia de las herramientas de compilación, vuelve CSS más potente y expresivo, y permite a quienes comienzan en el desarrollo frontend escribir y entender los estilos de una forma más intuitiva.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[El auge del CSS Utility-First: Tailwind CSS como ejemplo]]></title>
            <link>https://blog.m1ng.space/es/posts/css/the-rise-of-utility-first/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/css/the-rise-of-utility-first/</guid>
            <pubDate>Sun, 20 Aug 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[¿Adiós a los problemas de BEM y CSS-in-JS? La filosofía Utility-First CSS y su framework más representativo, Tailwind CSS, han conquistado la comunidad frontend en los últimos años. ¿Qué los hace tan atractivos?]]></description>
            <content:encoded><![CDATA[<h2>1. Las dificultades de las metodologías CSS tradicionales</h2>
<p>Antes de la aparición de Utility-First, contábamos con muchas metodologías CSS excelentes para organizar los estilos, por ejemplo:</p>
<ul>
<li><strong>BEM (Block, Element, Modifier)</strong>: Utiliza la convención de nombres <code>block__element--modifier</code> para mantener los estilos independientes y fáciles de mantener.</li>
<li><strong>OOCSS (Object-Oriented CSS)</strong>: Promueve la separación entre estructura y apariencia, y entre contenedor y contenido.</li>
<li><strong>CSS-in-JS</strong>: Escribe CSS directamente dentro de componentes JavaScript para encapsular los estilos a nivel de componente.</li>
</ul>
<p>Estos métodos resolvieron hasta cierto punto la contaminación global de CSS y la organización del código, pero también suelen traer problemas nuevos: el esfuerzo mental de poner nombre a innumerables clases, la molestia de cambiar entre archivos y el posible coste en tiempo de ejecución de CSS-in-JS.</p>
<h2>2. ¿Qué es Utility-First?</h2>
<p>Utility-First es una filosofía CSS que propone ofrecer una serie de <strong>clases utilitarias de propósito único y nombre estable</strong>, y construir interfaces complejas combinando esas clases.</p>
<p>Por ejemplo, en lugar de crear una clase <code>.card</code> y definir su <code>display</code>, <code>padding</code> y <code>box-shadow</code> en un archivo CSS,
se escribe directamente lo siguiente en HTML:</p>
<pre><code>&lt;div class="block p-6 rounded-lg shadow-lg bg-white"&gt;
  
&lt;/div&gt;
</code></pre>
<p>Aquí, <code>block</code>, <code>p-6</code>, <code>rounded-lg</code> y las demás clases utilitarias se ocupan cada una de una sola tarea pequeña.</p>
<h2>3. Tailwind CSS: el modelo de Utility-First</h2>
<p>Tailwind CSS es actualmente el framework CSS Utility-First más popular. Ofrece un conjunto extremadamente completo de clases utilitarias que cubre casi todas las propiedades CSS de uso habitual.</p>
<p><strong>Ventajas principales:</strong></p>
<ul>
<li><strong>Desarrollo extremadamente rápido</strong>: Casi nunca hay que salir del archivo HTML. Al combinar clases atómicas se puede construir rápidamente prácticamente cualquier diseño, lo que aumenta enormemente la eficiencia del prototipado y el desarrollo.</li>
<li><strong>Un sistema de diseño obligatorio</strong>: Como todos los estilos —colores, espaciado y tamaños de fuente— proceden de una configuración predefinida (theme), el equipo puede mantener fácilmente la coherencia de la interfaz y evitar los “números mágicos”.</li>
<li><strong>Sin preocupaciones por los nombres</strong>: “Los dos grandes problemas de la informática son la invalidación de caché y poner nombres.” Tailwind permite olvidarse casi por completo de nombrar clases CSS.</li>
<li><strong>Rendimiento excepcional</strong>: Durante la compilación de producción, Tailwind analiza los archivos y elimina todas las clases CSS que no se utilizan mediante PurgeCSS (o su motor JIT integrado), por lo que el archivo CSS final suele ser muy pequeño.</li>
</ul>
<h2>4. ¿“Infierno de nombres de clase”? — Otra perspectiva sobre las desventajas</h2>
<p>La crítica más habitual a Tailwind es que hace que el HTML sea muy voluminoso y difícil de leer, como si se regresara a la época de los “estilos inline”.</p>
<pre><code>&lt;button class="py-2 px-4 font-semibold rounded-lg shadow-md text-white bg-blue-500 hover:bg-blue-700"&gt;
  Click me
&lt;/button&gt;
</code></pre>
<p>Sin duda requiere un periodo de adaptación. Sin embargo, sus defensores sostienen que:</p>
<ol>
<li>Los estilos y la estructura ya están estrechamente acoplados, por lo que colocarlos juntos puede facilitar el mantenimiento.</li>
<li>Al extraer componentes reutilizables, como componentes de React o Vue, esta aparente “confusión” se puede encapsular.</li>
</ol>
<p>Para las combinaciones de estilos que deben reutilizarse, Tailwind ofrece la directiva <code>@apply</code>:</p>
<pre><code>/* in your css file */
.btn-primary {
  @apply py-2 px-4 font-semibold rounded-lg shadow-md text-white bg-blue-500 hover:bg-blue-700;
}
</code></pre>
<p>Así se puede utilizar <code>.btn-primary</code> en HTML. No obstante, la recomendación oficial de Tailwind es resolver la reutilización mediante componentes.</p>
<h2>Conclusión</h2>
<p>El éxito de Utility-First y Tailwind CSS marca un cambio en la idea de la “separación de responsabilidades” en el desarrollo frontend: se pasa de la separación de lenguajes (HTML/CSS/JS) a la separación de componentes. Mediante un enfoque aparentemente “primitivo”, resuelve muchos problemas prácticos del desarrollo web moderno y ofrece a los desarrolladores un atajo hacia interfaces eficientes, coherentes y de alto rendimiento.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[La próxima generación de pruebas end-to-end: primeros pasos con Playwright]]></title>
            <link>https://blog.m1ng.space/es/posts/testing/intro-to-playwright/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/testing/intro-to-playwright/</guid>
            <pubDate>Thu, 20 Jul 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[¿Cansado de las pruebas end-to-end inestables? Gracias a su capacidad mult navegador, las esperas automáticas y sus potentes herramientas de depuración, Playwright de Microsoft se está convirtiendo en un nuevo referente de la automatización de pruebas.]]></description>
            <content:encoded><![CDATA[<h2>1. Los desafíos de las pruebas end-to-end</h2>
<p>Las pruebas end-to-end (E2E) son la última línea de defensa para garantizar la calidad de una aplicación. Simulan acciones reales del usuario —clics, escritura y navegación— para comprobar que el flujo completo funciona correctamente. Sin embargo, las pruebas E2E también tienen fama de ser «frágiles» e «inestables»:</p>
<ul>
<li><strong>Comportamiento asíncrono</strong>: cuando se ejecuta el script, es posible que los elementos del DOM todavía no se hayan cargado y la prueba falle. Quienes desarrollan tienen que añadir muchos <code>wait</code> o <code>sleep</code> manuales, lo que vuelve las pruebas poco fiables.</li>
<li><strong>Compatibilidad entre navegadores</strong>: conseguir que las pruebas sean estables en todos los navegadores principales —Chrome, Firefox y Safari— supone un gran reto.</li>
<li><strong>Depuración difícil</strong>: cuando una prueba falla en un entorno CI/CD, reproducir y localizar el problema es complicado por la falta de instantáneas del momento y registros de red.</li>
</ul>
<p>Para resolver estos problemas surgió una nueva generación de herramientas de pruebas, y Playwright es una de las más destacadas.</p>
<h2>2. ¿Qué es Playwright?</h2>
<p>Playwright es una biblioteca de Node.js desarrollada por Microsoft para automatizar los navegadores Chromium (Chrome y Edge), Firefox y WebKit (Safari). Fue creada por miembros clave del equipo que desarrolló originalmente Google Puppeteer. Puede considerarse su sucesora espiritual, pero con un diseño más moderno y potente.</p>
<h2>3. Principales ventajas de Playwright</h2>
<h4>a. Pruebas realmente mult navegador</h4>
<p>Esta es la ventaja más notable de Playwright. Con una única API unificada puedes escribir pruebas que se ejecuten en los tres motores principales. Así resulta sencillo descubrir y corregir errores que solo aparecen en un navegador concreto, como Safari.</p>
<h4>b. Esperas automáticas (Auto-Waits)</h4>
<p>Playwright resuelve de raíz los problemas de asincronía. Antes de ejecutar una acción, como <code>page.click()</code>, realiza automáticamente una serie de comprobaciones de operabilidad, por ejemplo:</p>
<ul>
<li>Esperar a que el elemento aparezca en el DOM.</li>
<li>Esperar a que el elemento sea visible.</li>
<li>Esperar a que una animación deje de cubrir el elemento.</li>
<li>Esperar a que el elemento pueda recibir eventos.</li>
</ul>
<p>Esto significa que casi nunca es necesario escribir esperas manuales, lo que mejora sustancialmente la estabilidad de las pruebas.</p>
<h4>c. Potentes herramientas complementarias</h4>
<ul>
<li>
<p><strong>Codegen (generador de código)</strong>: es una función revolucionaria. Puedes ejecutar <code>pnpm exec playwright codegen example.com</code> y Playwright abrirá una ventana del navegador. Todas las acciones que realices se grabarán automáticamente y se convertirán en código de prueba. Esto reduce enormemente la barrera de entrada para escribir pruebas E2E.</p>
</li>
<li>
<p><strong>Trace Viewer (visor de trazas)</strong>: es la «máquina del tiempo» de Playwright. Cuando una prueba falla, puedes generar un archivo con la traza completa. Al abrirlo con Trace Viewer puedes:</p>
<ul>
<li>Consultar una instantánea del DOM en cada paso de la prueba.</li>
<li>Consultar el registro completo de solicitudes de red.</li>
<li>Consultar los mensajes y errores de la consola.</li>
<li>Moverte en ambos sentidos por la línea temporal para ver de forma intuitiva los cambios de la página antes y después de cada acción.</li>
</ul>
</li>
</ul>
<p>Esto hace que depurar fallos de CI sea más sencillo que nunca.</p>
<h2>4. Un ejemplo de prueba sencillo</h2>
<p>La API de Playwright tiene un diseño muy intuitivo.</p>
<pre><code>import { test, expect } from '@playwright/test';

test('页面应有正确的标题', async ({ page }) =&gt; {
  await page.goto('https://playwright.dev/');

  // 断言页面的 title 包含 "Playwright"
  await expect(page).toHaveTitle(/Playwright/);
});

test('点击 "Get started" 链接应跳转到介绍页', async ({ page }) =&gt; {
  await page.goto('https://playwright.dev/');

  // 通过角色和名称定位并点击链接
  await page.getByRole('link', { name: 'Get started' }).click();

  // 断言 URL 包含 "intro"
  await expect(page).toHaveURL(/.*intro/);
});
</code></pre>
<h2>Conclusión</h2>
<p>Gracias a su excelente compatibilidad entre navegadores, sus fiables esperas automáticas y unas herramientas de depuración inigualables —especialmente Trace Viewer—, Playwright ha establecido un nuevo estándar para las pruebas E2E de aplicaciones Web modernas. No solo mejora la estabilidad y la fiabilidad de las pruebas, sino también la experiencia al escribirlas mediante herramientas como Codegen. Si buscas para tu proyecto una solución de automatización moderna y potente, Playwright es sin duda una de las mejores opciones.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Signals: la nueva ola de reactividad en el frontend]]></title>
            <link>https://blog.m1ng.space/es/posts/frameworks/the-rise-of-signals/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/frameworks/the-rise-of-signals/</guid>
            <pubDate>Sat, 20 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Desde SolidJS y Qwik hasta Preact y Svelte 5, los Signals se están convirtiendo en un paradigma central para las actualizaciones reactivas de alto rendimiento en los frameworks frontend modernos. Este artículo analiza en profundidad cómo funcionan y su impacto en el ecosistema.]]></description>
            <content:encoded><![CDATA[<h2>1. La evolución de los modelos reactivos</h2>
<p>La esencia del desarrollo frontend consiste en «convertir el estado en una interfaz de usuario». A lo largo de los años hemos presenciado la evolución continua de los modelos reactivos: desde la manipulación manual del DOM, pasando por los patrones MVC/MVVM, hasta el DOM virtual (Virtual DOM) popularizado por React. Al procesar por lotes los cambios de estado y realizar el Diffing a nivel de componente, el VDOM simplificó enormemente el desarrollo de interfaces.</p>
<p>Sin embargo, el VDOM no es una solución universal. El coste de volver a renderizar y hacer Diffing a nivel de componente puede convertirse en un cuello de botella en ciertos escenarios muy dinámicos. En los últimos años, un paradigma más antiguo pero optimizado mediante compiladores modernos —la reactividad de granularidad fina (Fine-Grained Reactivity)— ha vuelto al centro del escenario, y su principal vehículo son los <strong>Signals</strong>.</p>
<h2>2. ¿Qué son los Signals?</h2>
<p>Un Signal es una primitiva reactiva (Reactive Primitive) que encapsula un valor y puede notificar automáticamente a todas sus dependencias cuando dicho valor cambia. A diferencia de los frameworks VDOM, que rastrean los cambios a nivel de componente, un Signal crea automáticamente un grafo de dependencias en el punto donde se leen los datos.</p>
<p>Cuando se actualiza el valor de un Signal, solo se vuelven a ejecutar los cálculos (<code>Memo</code>) o efectos secundarios (<code>Effect</code>) que dependen directa o indirectamente de él. Así se consiguen actualizaciones precisas y «quirúrgicas» del DOM, sin necesidad de realizar Diffing sobre un VDOM.</p>
<p>Su núcleo suele estar formado por tres primitivas:</p>
<ul>
<li><strong><code>createSignal(value)</code></strong>: Crea una unidad de estado reactiva.</li>
<li><strong><code>createEffect(() =&gt; {})</code></strong>: Crea un efecto secundario que rastrea automáticamente sus dependencias y responde a los cambios.</li>
<li><strong><code>createMemo(() =&gt; {})</code></strong>: Crea un cálculo reactivo derivado y almacenado en caché.</li>
</ul>
<pre><code>// SolidJS 语法示例
const [count, setCount] = createSignal(0);

const doubleCount = createMemo(() =&gt; count() * 2);

createEffect(() =&gt; {
  console.log(`The double count is: ${doubleCount()}`);
});

setCount(1); // 这将触发 memo 重新计算，并触发 effect 重新执行
</code></pre>
<p>Un modelo mental fundamental es que, en un framework basado en Signals como SolidJS, la propia función del componente solo se ejecuta una vez durante la inicialización. Las actualizaciones posteriores están completamente impulsadas por el sistema reactivo, no por una nueva renderización del componente.</p>
<h2>3. Adopción generalizada en el ecosistema</h2>
<p>Los Signals no son un concepto totalmente nuevo; sus ideas se remontan a frameworks tempranos como Knockout.js. Sin embargo, gracias a los compiladores modernos de JavaScript, han cobrado nueva vida y han sido adoptados ampliamente por los principales frameworks:</p>
<ul>
<li><strong>SolidJS &amp; Qwik</strong>: Están construidos por completo sobre Signals y son implementaciones de referencia de este paradigma.</li>
<li><strong>Preact</strong>: Incorporó soporte oficial mediante el paquete <code>@preact/signals</code>, que puede integrarse en proyectos existentes de Preact/React.</li>
<li><strong>Vue</strong>: <code>ref</code> y <code>computed</code> de su Composition API son, en esencia, una implementación de Signals.</li>
<li><strong>Svelte 5</strong>: La próxima actualización «Runes» supone una reestructuración del modelo reactivo de Svelte, inspirada y basada en el paradigma de Signals.</li>
<li><strong>Angular</strong>: Sus versiones recientes también han incorporado Signals como nuevas primitivas reactivas.</li>
</ul>
<p>Esta convergencia entre frameworks demuestra el valor de los Signals como modelo reactivo eficiente y predecible.</p>
<h2>4. Ventajas y concesiones</h2>
<p><strong>Ventajas:</strong></p>
<ol>
<li><strong>Rendimiento excelente</strong>: Al evitar el VDOM y las nuevas renderizaciones a nivel de componente, suele ofrecer resultados sobresalientes en las pruebas de rendimiento.</li>
<li><strong>Previsibilidad</strong>: El flujo de las actualizaciones de estado es claro y fácil de comprender y depurar.</li>
<li><strong>Bajo uso de memoria</strong>: No es necesario mantener una representación virtual de todo el árbol de componentes.</li>
</ol>
<p><strong>Concesiones:</strong></p>
<ol>
<li><strong>Cambio de modelo mental</strong>: Los desarrolladores acostumbrados al ciclo de renderización de React deben adaptarse al nuevo modelo en el que «el componente solo se ejecuta una vez».</li>
<li><strong>Integración con el ecosistema</strong>: Aunque los Signals son potentes por sí mismos, su integración profunda con el enorme ecosistema VDOM de React —por ejemplo, con ciertas bibliotecas de UI— puede requerir trabajo adicional de adaptación.</li>
</ol>
<h2>Conclusión</h2>
<p>El resurgimiento de los Signals marca una evolución importante en los paradigmas de gestión de estado del frontend. Devuelve la atención de los desarrolladores desde «cómo se renderiza un componente» hacia la cuestión más esencial de «cómo fluye el estado». Gracias a su profunda integración con los compiladores, los Signals ofrecen una excelente experiencia de desarrollo y, al mismo tiempo, abren nuevas posibilidades en los límites de rendimiento de las aplicaciones Web.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Adiós a la documentación de API: aplicaciones con seguridad de tipos de extremo a extremo mediante tRPC]]></title>
            <link>https://blog.m1ng.space/es/posts/typescript/typesafe-apis-with-trpc/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/typescript/typesafe-apis-with-trpc/</guid>
            <pubDate>Wed, 15 Mar 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[¿Cómo mantener sincronizado el contrato de datos entre frontend y backend en un proyecto TypeScript full-stack? Sin generación de código ni esquemas intermedios, tRPC ofrece auténtica seguridad de tipos de extremo a extremo.]]></description>
            <content:encoded><![CDATA[<h2>1. El problema del «contrato» entre frontend y backend</h2>
<p>En el desarrollo tradicional con frontend y backend separados, la API es el «contrato» entre ambos. Normalmente mantenemos ese contrato de estas maneras:</p>
<ul>
<li><strong>API RESTful</strong>: mediante herramientas como OpenAPI (Swagger), que generan y mantienen documentación detallada de la API.</li>
<li><strong>GraphQL</strong>: mediante un estricto Schema Definition Language (SDL), que define las estructuras de datos y las operaciones.</li>
</ul>
<p>Estas soluciones funcionan, pero comparten un problema: <strong>el contrato y la implementación están separados</strong>. Cuando alguien del frontend llama a una API, confía en que devolverá la estructura descrita en la documentación o el esquema. Si cambia la implementación del backend —por ejemplo, se renombra un campo— y la documentación o el esquema no se actualizan a tiempo, la discrepancia solo aparece en tiempo de ejecución y provoca un error.</p>
<p>¿Existe una forma de mantener este «contrato» sincronizado automáticamente con la implementación e incluso descubrir discrepancias durante la compilación?</p>
<h2>2. La idea central de tRPC: compartir tipos, no esquemas</h2>
<p>tRPC (TypeScript Remote Procedure Call) propone una solución radical pero muy sencilla: <strong>si tanto el frontend como el backend usan TypeScript, ¿por qué no compartir directamente los tipos?</strong></p>
<p>tRPC permite escribir funciones TypeScript normales como API del backend y llamarlas directamente desde el frontend, con inferencia de tipos y autocompletado completos, como si fueran funciones de un módulo local.</p>
<p>No depende de ningún esquema ni de generación de código. El único «contrato» son los propios tipos de TypeScript.</p>
<h2>3. ¿Cómo funciona?</h2>
<p>La magia de tRPC procede de la inferencia de tipos y de una pequeña capa de encapsulación ingeniosa.</p>
<h4>a. Backend: definir el router de la API</h4>
<p>En el backend —normalmente un servicio Node.js— se utilizan las funciones de tRPC para crear uno o varios «routers». Cada router reúne un conjunto de «procedures» invocables, es decir, los endpoints de la API.</p>
<pre><code>// server/router.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod'; // 使用 Zod 进行运行时校验

const t = initTRPC.create();

export const appRouter = t.router({
  // 定义一个名为 `getUser` 的查询 procedure
  getUser: t.procedure
    .input(z.object({ userId: z.string() }))
    .query(({ input }) =&gt; {
      // 在这里查询数据库或执行其他逻辑
      const user = { id: input.userId, name: 'Alex' };
      return user;
    }),

  // 定义一个名为 `createUser` 的变更 procedure
  createUser: t.procedure
    .input(z.object({ name: z.string() }))
    .mutation(({ input }) =&gt; {
      const user = { id: `${Math.random()}`, name: input.name };
      return user;
    }),
});

// 导出 router 的类型定义
export type AppRouter = typeof appRouter;
</code></pre>
<h4>b. Frontend: crear el cliente y llamar a la API</h4>
<p>En el frontend solo hay que importar del backend <code>AppRouter</code> como <strong>tipo</strong>. Es importante observar que solo se importa un tipo, no código del servidor.</p>
<pre><code>// client/trpc.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../server/router'; // 只导入类型

export const trpc = createTRPCReact&lt;AppRouter&gt;();
</code></pre>
<p>Ahora puedes llamar a la API desde un componente React como si fuera una función local, con seguridad de tipos y autocompletado completos.</p>
<pre><code>// client/components/UserInfo.tsx
import { trpc } from '../trpc';

function UserInfo({ userId }: { userId: string }) {
  // `useQuery` 的第一个参数是 procedure 的路径
  // 你输入 `trpc.` 时，IDE 会自动提示 `getUser` 和 `createUser`
  const userQuery = trpc.getUser.useQuery({ userId });

  if (userQuery.isLoading) {
    return &lt;div&gt;Loading...&lt;/div&gt;;
  }

  // `userQuery.data` 的类型被自动推断为 { id: string; name: string }
  return &lt;div&gt;User: {userQuery.data?.name}&lt;/div&gt;;
}
</code></pre>
<p>Si ahora quien desarrolla el backend cambia el campo que devuelve <code>getUser</code> de <code>name</code> a <code>fullName</code>, <code>userQuery.data?.name</code> producirá inmediatamente un error de compilación de TypeScript en el frontend, en lugar de descubrirse en tiempo de ejecución.</p>
<h2>4. ¿Por qué elegir tRPC?</h2>
<ul>
<li><strong>Seguridad de tipos absoluta de extremo a extremo</strong>: es su principal valor. Elimina toda una clase de errores causados por contratos de API que no coinciden.</li>
<li><strong>Excelente experiencia de desarrollo</strong>: el autocompletado del IDE evita consultar documentación o adivinar la estructura de la API. Refactorizar se vuelve extraordinariamente sencillo y seguro.</li>
<li><strong>Sin generación de código</strong>: no hay pasos adicionales de compilación, por lo que el ciclo de respuesta es muy rápido.</li>
<li><strong>Ligero y flexible</strong>: tRPC es muy pequeño y puede integrarse con cualquier framework frontend y servicio backend.</li>
</ul>
<h2>Conclusión</h2>
<p>tRPC aporta una fluidez sin precedentes al desarrollo TypeScript full-stack. Al eliminar la dependencia de la documentación de API y los esquemas, hace que la colaboración entre frontend y backend sea directa y extremadamente segura. Sus ventajas son aún mayores en una arquitectura monorepo. Si estás construyendo una aplicación TypeScript full-stack, tRPC es una herramienta revolucionaria a la que merece la pena dedicar tiempo.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Explorando Astro: creado para sitios web basados en contenido]]></title>
            <link>https://blog.m1ng.space/es/posts/astro/exploring-astro-for-content-sites/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/astro/exploring-astro-for-content-sites/</guid>
            <pubDate>Tue, 10 Jan 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[En una época dominada por las aplicaciones de una sola página (SPA), Astro toma otro camino y se centra en el máximo rendimiento de los sitios de contenido. ¿Qué hace tan poderosa a su arquitectura de islas?]]></description>
            <content:encoded><![CDATA[<h2>1. ¿De verdad necesitamos tanto JavaScript?</h2>
<p>Los frameworks frontend modernos como React, Vue y Svelte han mejorado enormemente la experiencia de desarrollo al crear aplicaciones web complejas. Pero cuando los usamos para crear blogs, portafolios, documentación o sitios de marketing, surge una pregunta: ¿de verdad necesitan estos sitios, centrados principalmente en presentar contenido, cargar toda la página como una aplicación de una sola página (SPA)?</p>
<p>La respuesta suele ser «no». En este tipo de sitios, la necesidad principal del usuario es acceder rápidamente al contenido. Un exceso de JavaScript, en cambio, retrasa el tiempo hasta la interactividad (TTI) y perjudica tanto la experiencia de usuario como el SEO.</p>
<p>Astro nació precisamente para resolver este problema.</p>
<h2>2. La idea central de Astro: contenido primero y cero JS por defecto</h2>
<p>Astro es un generador de sitios estáticos (SSG) moderno cuya idea central es <strong>priorizar el contenido</strong>. Su objetivo es realizar tanto trabajo como sea posible durante la compilación y enviar al navegador la menor cantidad posible de JavaScript.</p>
<p>Para lograrlo, Astro propuso dos grandes innovaciones:</p>
<ul>
<li><strong>Cero JavaScript por defecto (Zero-JS by Default)</strong>: Astro renderiza durante la compilación todos tus componentes de interfaz, ya estén escritos en React, Vue o Svelte, como HTML puro. Así, el navegador recibe una página estática y no necesita cargar el runtime JS de ningún framework, por lo que la carga es extremadamente rápida.</li>
<li><strong>Arquitectura de islas (Islands Architecture)</strong>: Esta es la esencia de Astro. En un «océano» de HTML estático, cualquier componente que necesite interacción en el cliente puede marcarse como una «isla». Astro empaqueta y carga de forma independiente el JavaScript que necesitan esas islas, mientras el resto de la página permanece completamente estático.</li>
</ul>
<h2>3. ¿Cómo funcionan las «islas»?</h2>
<p>En Astro puedes marcar un componente como una isla interactiva añadiendo una directiva <code>client:*</code>.</p>
<p>Por ejemplo, supongamos que tenemos un componente contador escrito en React llamado <code>Counter.jsx</code>. Podemos usarlo en una página de Astro de esta manera:</p>
<pre><code>---
// src/pages/index.astro
import Counter from '../components/Counter.jsx';
---
&lt;html&gt;
  &lt;body&gt;
    &lt;h1&gt;Astro 示例&lt;/h1&gt;
    &lt;p&gt;这是一个静态的段落，它不会加载任何 JS。&lt;/p&gt;

    {/* 这个组件是一个交互式岛屿 */}
    &lt;Counter client:load /&gt;

    &lt;p&gt;这是另一个静态段落。&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>La directiva <code>client:load</code> le indica a Astro:</p>
<ol>
<li>Renderizar en el servidor el HTML inicial del componente <code>Counter</code>.</li>
<li>Crear un paquete JS independiente para el componente <code>Counter</code>.</li>
<li>Cargar automáticamente ese paquete JS al cargar la página y «activar» (hydrate) el componente para que sea interactivo.</li>
</ol>
<p>Astro también ofrece directivas de carga más precisas, como <code>client:idle</code> (cargar cuando el navegador esté inactivo) y <code>client:visible</code> (cargar cuando el componente entre en el viewport), llevando la optimización del rendimiento aún más lejos.</p>
<h2>4. Independencia de frameworks</h2>
<p>Otro gran atractivo de Astro es su apertura a los frameworks de interfaz. En un mismo proyecto de Astro puedes usar al mismo tiempo componentes de distintos frameworks como React, Vue, Svelte, SolidJS y Lit. Esto proporciona a los equipos una enorme flexibilidad para colaborar y elegir tecnologías.</p>
<h2>Conclusión</h2>
<p>Astro no pretende sustituir a React o Vue, sino ofrecer una solución mejor para escenarios concretos. Si tu proyecto es un sitio centrado en el contenido y con requisitos muy exigentes de rendimiento y SEO —por ejemplo, este mismo blog está creado con Astro—, Astro es sin duda una opción excelente que merece explorarse en profundidad. Combina a la perfección las ventajas de rendimiento de los sitios estáticos con la experiencia de desarrollo de los frameworks modernos.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Lanzamiento oficial de SvelteKit 1.0: una nueva forma de crear aplicaciones Web]]></title>
            <link>https://blog.m1ng.space/es/posts/svelte/sveltekit-1-0-release/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/svelte/sveltekit-1-0-release/</guid>
            <pubDate>Thu, 01 Dec 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Tras más de dos años de desarrollo, SvelteKit 1.0 se publicó finalmente en diciembre de 2022. Considerado el futuro de la creación de aplicaciones Web de cualquier escala, ofrece funciones clave y una filosofía de diseño propias.]]></description>
            <content:encoded><![CDATA[<h2>¡SvelteKit 1.0 ya está aquí!</h2>
<p>En diciembre de 2022, el equipo de Svelte anunció oficialmente el lanzamiento de SvelteKit 1.0. Fue un hito tanto para la comunidad de Svelte como para todo el ámbito del frontend. SvelteKit ya no es simplemente «el Next.js o Nuxt.js de Svelte»: incorpora una filosofía de diseño propia y distintiva, y pretende ofrecer una forma más sencilla, flexible y eficiente de crear aplicaciones Web.</p>
<h2>¿Qué es SvelteKit?</h2>
<p>Si conoces Svelte, probablemente sepas que Svelte en sí es un <strong>framework de componentes</strong>. Mediante un compilador radical, transforma tus archivos <code>.svelte</code> en código JavaScript imperativo y eficiente durante la compilación, logrando un rendimiento extraordinario y una sobrecarga en tiempo de ejecución extremadamente pequeña.</p>
<p>SvelteKit, por su parte, es un <strong>framework de aplicaciones</strong> construido sobre Svelte. Se ocupa de todo lo necesario para crear una aplicación completa: enrutamiento, renderización del lado del servidor (SSR), carga de datos, adaptadores de despliegue y mucho más, para que puedas centrarte en desarrollar la lógica de negocio.</p>
<h2>Resumen de sus características principales</h2>
<h4>1. Enrutamiento basado en el sistema de archivos</h4>
<p>Al igual que Next.js, SvelteKit genera automáticamente las rutas a partir de la estructura de tu sistema de archivos. La estructura de archivos del directorio <code>src/routes</code> se corresponde directamente con las URLs de la aplicación.</p>
<ul>
<li><code>src/routes/+page.svelte</code> -&gt; <code>/</code></li>
<li><code>src/routes/about/+page.svelte</code> -&gt; <code>/about</code></li>
<li><code>src/routes/blog/[slug]/+page.svelte</code> -&gt; <code>/blog/some-post</code></li>
</ul>
<p>Este enfoque de convención sobre configuración simplifica enormemente la gestión de rutas.</p>
<h4>2. Modos de renderización flexibles</h4>
<p>SvelteKit permite controlar con precisión el modo de renderización a nivel de página. La renderización del lado del servidor (SSR), la generación de sitios estáticos (SSG) o una combinación de ambas se pueden implementar en la misma aplicación. Incluso puedes desactivar SSR para una sola página y convertirla en una aplicación de página única (SPA) puramente del lado del cliente.</p>
<h4>3. La función <code>load</code> universal</h4>
<p>SvelteKit unifica el modelo de carga de datos. Junto a cada página o diseño, puedes crear un archivo <code>+page.js</code> o <code>+page.server.js</code> y exportar una función <code>load</code>.</p>
<ul>
<li>En <code>+page.js</code>, la función <code>load</code> se ejecuta tanto en el servidor como en el cliente.</li>
<li>En <code>+page.server.js</code>, la función <code>load</code> se ejecuta <strong>únicamente en el servidor</strong>, por lo que puedes acceder de forma segura a una base de datos o API privada.</li>
</ul>
<p>Los datos devueltos por la función <code>load</code> se pasan automáticamente al componente <code>+page.svelte</code> correspondiente.</p>
<pre><code>// src/routes/blog/[slug]/+page.server.js
import * as db from '$lib/server/database';

export async function load({ params }) {
  const post = await db.getPost(params.slug);
  return { post };
}
</code></pre>
<h4>4. Adaptadores</h4>
<p>«Escribe una vez, despliega en cualquier lugar» es una de las promesas fundamentales de SvelteKit. Mediante los <strong>adaptadores</strong>, SvelteKit puede empaquetar tu aplicación en el formato adecuado para cualquier plataforma de destino.</p>
<ul>
<li><code>@sveltejs/adapter-node</code>: Para desplegar en un servidor Node.js tradicional.</li>
<li><code>@sveltejs/adapter-vercel</code>: Adapta la aplicación a la plataforma Vercel.</li>
<li><code>@sveltejs/adapter-static</code>: Prerenderiza toda la aplicación como archivos estáticos, adecuados para cualquier alojamiento estático.</li>
<li>También existen más adaptadores oficiales y de la comunidad para plataformas como Netlify y Cloudflare Workers.</li>
</ul>
<h2>Conclusión</h2>
<p>El lanzamiento de SvelteKit 1.0 marcó la madurez del ecosistema Svelte. Combina el rendimiento extremo del compilador de Svelte con un framework de aplicaciones cuidadosamente diseñado, y ofrece a los desarrolladores una experiencia potente y agradable. Si buscas un framework versátil capaz de crear desde páginas estáticas sencillas hasta aplicaciones dinámicas complejas, sin duda merece la pena probar SvelteKit.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Evolución de la gestión de estado en React: de Props Drilling a Zustand]]></title>
            <link>https://blog.m1ng.space/es/posts/react/state-management-evolution/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/react/state-management-evolution/</guid>
            <pubDate>Tue, 15 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[La gestión de estado en React ha evolucionado desde el “props drilling” inicial, pasando por el enfoque unificado de Redux, hasta el actual auge de bibliotecas ligeras como Zustand. Este artículo repasa esa fascinante historia.]]></description>
            <content:encoded><![CDATA[<h2>Introducción</h2>
<p>«¿Cómo se puede gestionar el estado con elegancia?» Esta es una cuestión fundamental que todo desarrollador de React debe afrontar a medida que crece un proyecto. Desde el estado sencillo dentro de un componente hasta el estado global complejo compartido entre componentes, la comunidad de React ha explorado numerosas soluciones. Su evolución también refleja una comprensión cada vez más profunda del desarrollo basado en componentes.</p>
<h2>Primera etapa: una época sencilla — <code>useState</code> y Props Drilling</h2>
<p>Al principio solo teníamos <code>useState</code> (o <code>this.state</code> en los componentes de clase). Cuando varios componentes necesitaban compartir un estado, la recomendación oficial de React era <strong>«elevar el estado» (Lifting State Up)</strong>. El estado se trasladaba al ancestro común más cercano de esos componentes y después se transmitía, junto con su función de actualización, mediante props.</p>
<p>Cuando la jerarquía de componentes se hacía profunda, este patrón provocaba <strong>Props Drilling</strong>: algunos componentes intermedios recibían props únicamente para pasarlas a descendientes más profundos, sin utilizarlas ellos mismos. Esto aumentaba el acoplamiento entre componentes y dificultaba la refactorización y el mantenimiento.</p>
<h2>Segunda etapa: la respuesta oficial — Context API</h2>
<p>Para resolver el props drilling, React proporcionó oficialmente la Context API. Esta permite crear un «contexto», proporcionar (Provide) un valor en la parte superior del árbol de componentes y consumirlo (Consume) directamente desde un componente descendiente a cualquier profundidad, sin transmitirlo manualmente nivel por nivel.</p>
<pre><code>// 1. 创建 Context
const ThemeContext = React.createContext('light');

// 2. 在顶层提供值
&lt;ThemeContext.Provider value="dark"&gt;
  &lt;App /&gt;
&lt;/ThemeContext.Provider&gt;

// 3. 在子组件中消费
const theme = useContext(ThemeContext); // 'dark'
</code></pre>
<p>Sin embargo, la Context API también tiene una «trampa»: <strong>el rendimiento</strong>. Cada vez que cambia el <code>Provider</code> y su <code>value</code>, <strong>todos</strong> los componentes que consumen ese Context vuelven a renderizarse, incluso si solo les interesa una pequeña parte del objeto <code>value</code>. Por ello, la Context API no resulta adecuada para gestionar estados globales complejos que cambian con frecuencia.</p>
<h2>Tercera etapa: la era de la unificación — Redux</h2>
<p>Antes de que la Context API madurara, Redux irrumpió en escena y se convirtió rápidamente en el estándar de facto para gestionar el estado de aplicaciones grandes y complejas. Inspirándose en la arquitectura Flux y en la programación funcional, aportó:</p>
<ul>
<li><strong>Una única fuente de verdad (Single Source of Truth)</strong>: El state de toda la aplicación se almacena en un único store.</li>
<li><strong>El State es de solo lectura</strong>: La única forma de cambiar el state consiste en hacer dispatch de una action.</li>
<li><strong>Las modificaciones se realizan con funciones puras</strong>: Un reducer recibe el state anterior y una action, y devuelve el nuevo state.</li>
</ul>
<p>Gracias a su previsibilidad, sus potentes herramientas de depuración (viaje en el tiempo) y su abundante ecosistema de middleware, Redux resolvió los problemas de gestión de estado en aplicaciones grandes. Pero su defecto era igualmente evidente: una gran cantidad de <strong>código repetitivo (Boilerplate)</strong>. Para implementar una función sencilla, los desarrolladores tenían que escribir Actions, Reducers y Dispatchers, con una carga mental considerable.</p>
<h2>Cuarta etapa: el renacimiento — soluciones ligeras y Hooks-First</h2>
<p>Con la popularización de React Hooks y la reflexión sobre la complejidad de Redux, la comunidad comenzó a crear una nueva generación de bibliotecas de gestión de estado más ligeras. Todas compartían APIs concisas, modelos mentales sencillos y un uso completo de las capacidades de Hooks.</p>
<p><strong>Zustand</strong> es un ejemplo destacado. Proporciona una función <code>create</code> extremadamente sencilla para crear un store y, además:</p>
<ul>
<li><strong>No requiere Provider</strong>: El Store existe fuera del árbol de componentes de React y se puede importar desde cualquier lugar.</li>
<li><strong>API mínima</strong>: Se accede al estado y se actualiza mediante un único Hook.</li>
<li><strong>Suscripciones selectivas y mejor rendimiento</strong>: Cada componente puede suscribirse únicamente a la parte del state que necesita, evitando el problema de rendimiento de la Context API.</li>
</ul>
<pre><code>const useStore = create(set =&gt; ({
  count: 0,
  inc: () =&gt; set(state =&gt; ({ count: state.count + 1 })),
}));

function Counter() {
  // 只订阅 count 的变化
  const count = useStore(state =&gt; state.count);
  return &lt;h1&gt;{count}&lt;/h1&gt;;
}
</code></pre>
<h2>Conclusión: no hay una solución universal, elige según tus necesidades</h2>
<p>La evolución de la gestión de estado en React nos enseña que no existe una «mejor solución» válida para siempre, sino la solución más adecuada para cada situación.</p>
<ul>
<li><strong><code>useState</code></strong>: Siempre es la primera opción para el estado local de un componente.</li>
<li><strong>Context API</strong>: Adecuada para datos globales que no cambian con frecuencia, como el tema o la información de autenticación del usuario.</li>
<li><strong>Zustand / Jotai</strong>: Para la mayoría de las aplicaciones que necesitan estado global del lado del cliente, ofrecen un equilibrio excelente entre rendimiento y experiencia de desarrollo.</li>
<li><strong>Redux (Redux Toolkit)</strong>: Sigue siendo una opción fiable para aplicaciones enormes que requieren normas estrictas de flujo de datos, middleware complejo y potentes funciones de depuración.</li>
</ul>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[De Webpack a Vite: un viaje de migración sin sobresaltos]]></title>
            <link>https://blog.m1ng.space/es/posts/build-tools/migrating-from-webpack-to-vite/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/build-tools/migrating-from-webpack-to-vite/</guid>
            <pubDate>Mon, 05 Sep 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[¿Tu proyecto Webpack tarda un minuto en arrancar y más de diez segundos en actualizarse? Es hora de adoptar la nueva generación de herramientas de compilación frontend. Este artículo recoge el proceso y las lecciones de una migración de Webpack a Vite.]]></description>
            <content:encoded><![CDATA[<h2>1. ¿Por qué migrar? Los puntos débiles de Webpack</h2>
<p>Webpack es un empaquetador de módulos extremadamente potente y configurable que durante años ha sido la base de los proyectos frontend. Sin embargo, a medida que los proyectos crecen, su idea central basada en el empaquetado también genera cuellos de botella de rendimiento:</p>
<ul>
<li><strong>Arranque en frío lento</strong>: Cada vez que se inicia el servidor de desarrollo (<code>dev-server</code>), Webpack debe recorrer todo el grafo de dependencias y empaquetar todos los módulos en memoria. En proyectos grandes, este proceso puede tardar varios minutos.</li>
<li><strong>Actualizaciones en caliente (HMR) lentas</strong>: Cuando modificas un archivo, Webpack debe volver a calcular y sustituir los módulos relacionados. Aunque esto es más rápido que actualizar toda la página, en proyectos grandes la demora puede alcanzar varios segundos o incluso más de diez, interrumpiendo el flujo de desarrollo.</li>
</ul>
<h2>2. ¿Cómo resuelve Vite estos problemas?</h2>
<p>Vite, cuyo nombre significa «rápido» en francés, toma otro camino. Aprovecha el soporte de los navegadores modernos para módulos ES nativos (ESM) y divide el proceso de compilación en dos partes:</p>
<ul>
<li><strong>Durante el desarrollo</strong>: Vite inicia un servidor sin empaquetar previamente todos los módulos. En su lugar, intercepta las peticiones de módulos del navegador y transforma y sirve el código fuente bajo demanda. Por ejemplo, el navegador solicita <code>main.js</code> y Vite lo entrega; <code>main.js</code> importa <code>Button.vue</code>, el navegador vuelve a enviar una petición y Vite entrega el <code>Button.vue</code> transformado. Así, el servidor de desarrollo arranca casi al instante.</li>
<li><strong>En producción</strong>: Vite utiliza Rollup, otro empaquetador eficiente, para generar recursos estáticos muy optimizados.</li>
</ul>
<p>Este modelo transforma por completo la experiencia de desarrollo, con un arranque fulminante y actualizaciones en caliente medidas en milisegundos.</p>
<h2>3. Pasos prácticos para la migración</h2>
<p>Migrar un proyecto existente de Webpack a Vite suele implicar los siguientes pasos:</p>
<h4>a. Instalar dependencias y crear el archivo de configuración</h4>
<p>Primero, instala Vite:</p>
<pre><code>pnpm add -D vite @vitejs/plugin-react # 或 @vitejs/plugin-vue
</code></pre>
<p>Después, crea <code>vite.config.js</code> en la raíz del proyecto:</p>
<pre><code>import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [react()],
})
</code></pre>
<h4>b. Mover <code>index.html</code></h4>
<p>Webpack suele colocar <code>index.html</code> en el directorio <code>public</code> e inyecta automáticamente el JS empaquetado. Vite considera <code>index.html</code> como el punto de entrada de la aplicación. Debes moverlo a la raíz del proyecto y añadir manualmente la referencia al script:</p>
<pre><code>
&lt;body&gt;
  &lt;div id="root"&gt;&lt;/div&gt;
  &lt;script type="module" src="/src/main.jsx"&gt;&lt;/script&gt;
&lt;/body&gt;
</code></pre>
<h4>c. Sustituir los plugins de Webpack</h4>
<p>Debes encontrar plugins de Vite equivalentes a los Loaders y Plugins de Webpack. El ecosistema de la comunidad es muy amplio y existen soluciones para la mayoría de las necesidades.</p>
<table>
<thead>
<tr>
<th>Webpack</th>
<th>Vite</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>babel-loader</code></td>
<td><code>@vitejs/plugin-react</code> (Babel integrado)</td>
</tr>
<tr>
<td><code>vue-loader</code></td>
<td><code>@vitejs/plugin-vue</code></td>
</tr>
<tr>
<td><code>file-loader</code></td>
<td>Integrado en Vite</td>
</tr>
<tr>
<td><code>webpack-dev-server</code></td>
<td>Servidor de desarrollo integrado en Vite</td>
</tr>
</tbody>
</table>
<h4>d. Gestionar las variables de entorno</h4>
<p>En Webpack estamos acostumbrados a utilizar <code>process.env.NODE_ENV</code>. En Vite, las variables de entorno se consultan mediante <code>import.meta.env</code>, por ejemplo <code>import.meta.env.VITE_API_URL</code>.</p>
<h4>e. Configurar alias de rutas</h4>
<p>Configurar alias de rutas en <code>vite.config.js</code> es muy sencillo:</p>
<pre><code>import path from 'node:path'
// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  // ...
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
})
</code></pre>
<h2>4. Resultados tras la migración</h2>
<p>Después de migrar a Vite, la impresión más inmediata es la <strong>velocidad</strong>.</p>
<ul>
<li>El servidor de desarrollo pasó de tardar <strong>50 segundos</strong> a <strong>2 segundos</strong> en arrancar.</li>
<li>Las actualizaciones en caliente pasaron de una <strong>media de 3–5 segundos</strong> a <strong>unas decenas de milisegundos casi imperceptibles</strong>.</li>
<li>El archivo de configuración (<code>vite.config.js</code>) quedó mucho más sencillo que <code>webpack.config.js</code>.</li>
</ul>
<h2>Conclusión</h2>
<p>Aunque durante la migración quizá haya que resolver algunos problemas de configuración propios del proyecto, pasar de Webpack a Vite mejora enormemente la experiencia de desarrollo. Si todavía soportas un proceso de compilación lento, este es el mejor momento para adoptar Vite.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[CSS-in-JS sin runtime: experiencia de desarrollo y rendimiento excepcional]]></title>
            <link>https://blog.m1ng.space/es/posts/css/zero-runtime-css-in-js/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/css/zero-runtime-css-in-js/</guid>
            <pubDate>Fri, 05 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[CSS-in-JS ofrece una experiencia de desarrollo excelente, pero su coste de rendimiento en tiempo de ejecución siempre ha sido polémico. El CSS-in-JS sin runtime extrae los estilos a archivos CSS estáticos durante la compilación y reúne lo mejor de ambos mundos.]]></description>
            <content:encoded><![CDATA[<h2>1. La “doble cara” de CSS-in-JS</h2>
<p>Las soluciones CSS-in-JS, como <code>styled-components</code> y <code>Emotion</code>, transformaron la gestión de estilos en el desarrollo basado en componentes al permitir escribir CSS dentro de archivos JavaScript o TypeScript. Aportan numerosas ventajas:</p>
<ul>
<li><strong>Ámbito de componente</strong>: Los estilos quedan asociados al componente de forma predeterminada, lo que resuelve los conflictos globales de nombres CSS.</li>
<li><strong>Estilos dinámicos</strong>: Se pueden crear fácilmente estilos dinámicos a partir de las props o el state del componente.</li>
<li><strong>Código conjunto</strong>: Los estilos y la lógica del componente viven en el mismo archivo, lo que facilita su mantenimiento y organización.</li>
</ul>
<p>Sin embargo, estas comodidades no son gratuitas. El núcleo de las bibliotecas CSS-in-JS tradicionales es el <strong>runtime</strong>. Cuando un componente se monta en el navegador, el runtime JavaScript de la biblioteca CSS-in-JS:</p>
<ol>
<li>Analiza el CSS de los template strings o los objetos.</li>
<li>Genera nombres de clase únicos.</li>
<li>Inyecta dinámicamente los estilos en el <code>&lt;head&gt;</code> del documento, dentro de etiquetas <code>&lt;style&gt;</code>.</li>
</ol>
<p>Este proceso aumenta el tamaño del paquete JavaScript y añade cierto coste computacional durante el arranque y el renderizado de la aplicación, lo que afecta al rendimiento.</p>
<h2>2. La salida del “runtime cero”</h2>
<p>Para resolver el problema del rendimiento en tiempo de ejecución, la comunidad propuso un enfoque nuevo: <strong>CSS-in-JS sin runtime (Zero-Runtime)</strong>.</p>
<p>La idea central es conservar la experiencia de desarrollo de CSS-in-JS, pero completar todo el trabajo durante la <strong>compilación (build time)</strong>. Una herramienta de compilación, normalmente un plugin de Babel o Vite, analiza el código, encuentra todos los usos de CSS-in-JS y entonces:</p>
<ol>
<li>Extrae el texto CSS.</li>
<li>Genera archivos <code>.css</code> estáticos.</li>
<li>Sustituye el código CSS-in-JS original por nombres de clase únicos generados.</li>
</ol>
<p>Como resultado, el paquete JavaScript entregado al navegador <strong>no contiene ninguna biblioteca CSS-in-JS en tiempo de ejecución</strong>, y el resultado equivale a archivos CSS estáticos escritos a mano y altamente optimizados.</p>
<h2>3. Principales soluciones sin runtime</h2>
<h4>a. Linaria</h4>
<p>Linaria es una de las pioneras del CSS-in-JS sin runtime. Permite utilizar la conocida sintaxis de template literals etiquetados con <code>styled</code>.</p>
<pre><code>// YourComponent.js
import { styled } from '@linaria/react';

const Title = styled.h1`
  font-size: 2rem;
  color: tomato;
`;

// 构建时，这会被转换为：
// &lt;h1 class="Title_a1b2c3d"&gt;...&lt;/h1&gt;
//
// 并在一个静态 .css 文件中生成：
// .Title_a1b2c3d {
//   font-size: 2rem;
//   color: tomato;
// }
</code></pre>
<h4>b. vanilla-extract</h4>
<p>Desarrollado por el equipo de SEEK, vanilla-extract va un paso más allá y utiliza TypeScript para crear estilos completamente seguros en cuanto a tipos. Todas las definiciones de estilo son variables exportadas desde archivos <code>.css.ts</code>, con una potente inferencia de tipos y autocompletado.</p>
<pre><code>// styles.css.ts
import { style } from '@vanilla-extract/css';

export const title = style({
  fontSize: '2rem',
  color: 'tomato',
});
</code></pre>
<h4>c. Panda CSS</h4>
<p>Panda CSS es un competidor más reciente. Inspirado en Tailwind CSS, ofrece una experiencia de desarrollo basada en “Style Props” y, al mismo tiempo, garantiza una salida sin runtime.</p>
<pre><code>import { css } from '../styled-system/css';

function MyComponent() {
  return &lt;div className={css({ fontSize: '2rem', color: 'tomato' })} /&gt;;
}
</code></pre>
<h2>4. Ventajas y compromisos</h2>
<p><strong>Ventajas</strong>:</p>
<ul>
<li><strong>Rendimiento excepcional</strong>: El resultado final es CSS estático, sin coste de un runtime JavaScript.</li>
<li><strong>Paquetes más pequeños</strong>: No es necesario cargar una biblioteca CSS-in-JS en el cliente.</li>
<li><strong>Potente experiencia de desarrollo</strong>: Se mantienen las ventajas del ámbito por componente, la seguridad de tipos de TypeScript y la colocación conjunta del código.</li>
</ul>
<p><strong>Compromisos</strong>:</p>
<ul>
<li><strong>Dependencia durante la compilación</strong>: Debe integrarse en el proceso de build, por lo que la configuración resulta algo más compleja.</li>
<li><strong>Dinamismo limitado</strong>: No puede generar estilos completamente nuevos a partir de valores que solo existen en tiempo de ejecución, como datos procedentes de una API. No obstante, técnicas como las variables CSS pueden cubrir la mayoría de los casos dinámicos.</li>
</ul>
<h2>Conclusión</h2>
<p>El CSS-in-JS sin runtime es una corrección del rumbo del paradigma CSS-in-JS tradicional. Separa de forma inteligente la “experiencia durante el desarrollo” del “rendimiento en tiempo de ejecución”, para que ya no tengamos que elegir entre ambos. Para las aplicaciones web modernas que buscan un rendimiento extremo y paquetes más pequeños, ofrece una solución casi perfecta.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Novedades de ES2022: Top-Level Await, .at() y más]]></title>
            <link>https://blog.m1ng.space/es/posts/es/es2022-features-overview/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/es/es2022-features-overview/</guid>
            <pubDate>Fri, 15 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[ECMAScript 2022 (ES2022) se ha publicado oficialmente con varias funciones nuevas y prácticas. Este artículo repasa rápidamente las actualizaciones más destacadas, como Top-Level await y el método .at().]]></description>
            <content:encoded><![CDATA[<h2>Introducción</h2>
<p>Gracias al proceso anual de publicación del comité TC39, el lenguaje JavaScript sigue evolucionando cada año. ECMAScript 2022 (ES2022) incorpora varias funciones nuevas destinadas a mejorar la experiencia de desarrollo y la legibilidad del código. Veamos algunas de las más destacadas.</p>
<h2>1. <code>await</code> en el nivel superior</h2>
<p>Esta es una de las funciones más esperadas de ES2022. Antes, la palabra clave <code>await</code> solo podía utilizarse dentro de una función <code>async</code>. Esto resultaba muy incómodo al gestionar operaciones asíncronas en el nivel superior de un módulo, y normalmente había que envolver el código asíncrono en una IIFE (expresión de función invocada inmediatamente).</p>
<p><strong>Antes 👎:</strong></p>
<pre><code>// data.js
import { fetchData } from './api.js';

let data;
(async () =&gt; {
  data = await fetchData();
  // ... 只能在这里使用 data
})();

export { data }; // 导出时 data 还是 undefined
</code></pre>
<p><strong>Ahora (ES2022) 👍:</strong></p>
<pre><code>// data.js
import { fetchData } from './api.js';

const data = await fetchData();

export { data }; // 模块会等待 await 完成后再被其他模块评估
</code></pre>
<p>Top-level <code>await</code> simplifica enormemente situaciones como la carga dinámica de módulos o la inicialización de dependencias, y hace que el código asíncrono resulte más intuitivo.</p>
<h2>2. El método de indexación <code>.at()</code> para arrays y cadenas</h2>
<p>En JavaScript, para obtener el último elemento de un array normalmente hay que escribir <code>arr[arr.length - 1]</code>, una expresión larga y propensa a errores. ES2022 incorpora el método <code>.at()</code>, que unifica el uso de índices hacia delante y hacia atrás.</p>
<p>El método <code>.at()</code> recibe un entero. Si es positivo, devuelve el elemento del índice correspondiente; si es negativo, cuenta desde el final.</p>
<p><strong>Antes 👎:</strong></p>
<pre><code>const arr = [1, 2, 3, 4, 5];
const lastElement = arr[arr.length - 1]; // 5
const secondToLast = arr[arr.length - 2]; // 4
</code></pre>
<p><strong>Ahora (ES2022) 👍:</strong></p>
<pre><code>const arr = [1, 2, 3, 4, 5];
const lastElement = arr.at(-1); // 5
const secondToLast = arr.at(-2); // 4

// 同样适用于字符串
const str = 'hello';
console.log(str.at(-1)); // 'o'
</code></pre>
<h2>3. <code>Object.hasOwn(obj, prop)</code></h2>
<p>Para comprobar si un objeto posee una propiedad propia, en lugar de una heredada, solemos usar <code>Object.prototype.hasOwnProperty.call(obj, prop)</code>. Esta forma es muy engorrosa y puede fallar en algunos casos, como con objetos creados mediante <code>Object.create(null)</code>.</p>
<p><code>Object.hasOwn()</code> ofrece un método estático más corto y fiable.</p>
<p><strong>Antes 👎:</strong></p>
<pre><code>const obj = { a: 1 };
console.log(Object.prototype.hasOwnProperty.call(obj, 'a')); // true
console.log(Object.prototype.hasOwnProperty.call(obj, 'toString')); // false
</code></pre>
<p><strong>Ahora (ES2022) 👍:</strong></p>
<pre><code>const obj = { a: 1 };
console.log(Object.hasOwn(obj, 'a')); // true
console.log(Object.hasOwn(obj, 'toString')); // false
</code></pre>
<h2>4. Otras funciones destacables</h2>
<ul>
<li><strong>Error Cause</strong>: El constructor <code>Error</code> ahora puede recibir un segundo argumento para indicar la “causa” del error, lo que facilita la creación de cadenas de errores más claras.<pre><code>try {
  // ...
} catch (err) {
  throw new Error('New error message', { cause: err });
}
</code></pre>
</li>
<li><strong>Índices de coincidencia de RegExp (bandera <code>/d</code>)</strong>: Al usar la bandera <code>/d</code>, el resultado de una expresión regular también proporciona los índices inicial y final de cada grupo de captura.</li>
</ul>
<h2>Conclusión</h2>
<p>Aunque las novedades de ES2022 no suponen una revolución, refinan JavaScript en los detalles, resuelven muchos problemas que los desarrolladores arrastraban desde hacía tiempo y hacen que el código sea más conciso y robusto.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[¿Adiós al boilerplate de Redux? Guía detallada del gestor de estado ligero Zustand]]></title>
            <link>https://blog.m1ng.space/es/posts/react/zustand-vs-redux/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/react/zustand-vs-redux/</guid>
            <pubDate>Sun, 10 Apr 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[¿La complejidad de Redux te echa para atrás? Zustand ofrece una solución minimalista de gestión de estado para React basada en Hooks. Este artículo explica su uso principal y su filosofía de diseño.]]></description>
            <content:encoded><![CDATA[<h2>1. Los «problemas» de Redux</h2>
<p>Redux es, sin duda, la biblioteca de gestión de estado más conocida y potente del ecosistema React. Funciones como su flujo de datos unidireccional y la depuración con viaje en el tiempo la convierten en una opción fiable para aplicaciones grandes y complejas. Sin embargo, en muchos proyectos pequeños y medianos, el «código repetitivo» (Boilerplate) de Redux suele causar dolores de cabeza:</p>
<ul>
<li><strong>Actions &amp; Action Creators</strong>: Definir numerosos tipos de action y funciones creadoras.</li>
<li><strong>Reducers</strong>: Escribir enormes sentencias <code>switch</code> para gestionar distintas actions.</li>
<li><strong>Dispatch &amp; Selectors</strong>: Hacer dispatch de actions mediante <code>dispatch</code> y suscribirse a cambios de estado con <code>useSelector</code> dentro de los componentes.</li>
<li><strong>Context Provider</strong>: Envolver la raíz de la aplicación en un <code>&lt;Provider&gt;</code>.</li>
</ul>
<p>Todo esto hace que añadir incluso un estado sencillo exija modificar varios archivos y convierte el proceso en algo engorroso.</p>
<h2>2. Zustand: un soplo de aire fresco</h2>
<p>Zustand («estado» en alemán) es una biblioteca ligera de gestión de estado desarrollada por el equipo de Poimandres, creadores de <code>react-three-fiber</code>. Su filosofía de diseño es el <strong>minimalismo</strong> y la <strong>no intrusión</strong>.</p>
<p><strong>Características principales:</strong></p>
<ul>
<li><strong>Muy poco código</strong>: Para implementar una función, Zustand suele requerir solo una fracción del código necesario con Redux.</li>
<li><strong>Basado en Hooks</strong>: Todo gira en torno a un Hook personalizado, de acuerdo con las prácticas del desarrollo moderno con React.</li>
<li><strong>No requiere Context Provider</strong>: No necesitas envolver la parte superior de la aplicación en ningún Provider. El Store es independiente del árbol de componentes y se puede importar y utilizar desde cualquier lugar.</li>
<li><strong>Fácil de aprender</strong>: La API es extremadamente sencilla y su uso principal se aprende en pocos minutos.</li>
</ul>
<h2>3. Uso principal</h2>
<p>El uso de Zustand se divide en dos pasos: <strong>crear el Store</strong> y <strong>utilizarlo en un componente</strong>.</p>
<h4>a. Crear el Store</h4>
<p>Puedes definir el store en cualquier archivo <code>.js</code> o <code>.ts</code>.</p>
<pre><code>// src/store.js
import { create } from 'zustand';

const useBearStore = create((set) =&gt; ({
  bears: 0,
  increasePopulation: () =&gt; set((state) =&gt; ({ bears: state.bears + 1 })),
  removeAllBears: () =&gt; set({ bears: 0 }),
}));

export default useBearStore;
</code></pre>
<p>La función <code>create</code> recibe un callback cuyo argumento es la función <code>set</code>, similar al <code>setState</code> de React. Aquí defines el estado y los métodos que lo actualizan.</p>
<h4>b. Utilizarlo en un componente</h4>
<p>Úsalo en cualquier componente como si fuera un Hook normal.</p>
<pre><code>// src/components/BearCounter.jsx
import useBearStore from '../store';

function BearCounter() {
  const bears = useBearStore((state) =&gt; state.bears);
  return &lt;h1&gt;{bears} around here ...&lt;/h1&gt;;
}
</code></pre>
<p>Observa que nos suscribimos mediante la función selectora <code>(state) =&gt; state.bears</code> al estado <code>bears</code>. Esto es importante porque garantiza que el componente solo vuelva a renderizarse cuando cambie <code>bears</code>, evitando costes de rendimiento innecesarios.</p>
<pre><code>// src/components/Controls.jsx
import useBearStore from '../store';

function Controls() {
  const increasePopulation = useBearStore((state) =&gt; state.increasePopulation);
  return &lt;button onClick={increasePopulation}&gt;one up&lt;/button&gt;;
}
</code></pre>
<p>Obtener una action es igual de sencillo.</p>
<h2>4. Actions asíncronas</h2>
<p>Zustand también gestiona las operaciones asíncronas de forma muy natural. No necesitas ningún middleware como <code>redux-thunk</code> o <code>redux-saga</code>.</p>
<pre><code>const useAsyncStore = create((set) =&gt; ({
  data: null,
  fetchData: async (url) =&gt; {
    const response = await fetch(url);
    const data = await response.json();
    set({ data });
  },
}));
</code></pre>
<h2>Conclusión</h2>
<p>Zustand no pretende sustituir completamente a Redux. Redux conserva sus ventajas en cuanto a normas estrictas, trazabilidad y un enorme ecosistema.</p>
<p>Sin embargo, para la gran mayoría de las aplicaciones React, Zustand ofrece una opción más sencilla y rápida, con menor carga mental. Si estás cansado de las formalidades de Redux o tu próximo proyecto necesita un gestor de estado ágil y flexible, Zustand es sin duda una excelente alternativa que merece la pena probar.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Gestiona tu Monorepo con Turborepo]]></title>
            <link>https://blog.m1ng.space/es/posts/build-tools/intro-to-turborepo/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/build-tools/intro-to-turborepo/</guid>
            <pubDate>Sun, 20 Feb 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[A medida que los proyectos se vuelven más complejos, Monorepo se ha convertido en una forma popular de organizar el código. Pero ¿cómo se resuelven los problemas de rendimiento de compilación que trae consigo? Turborepo es un sistema de compilación de alto rendimiento creado para ello.]]></description>
            <content:encoded><![CDATA[<h2>1. ¿Por qué elegir un Monorepo?</h2>
<p>Un Monorepo, o repositorio de código único, es una estrategia que almacena varios proyectos o paquetes independientes en un mismo repositorio. Frente al modelo Polyrepo, donde cada proyecto tiene su propio repositorio, un Monorepo ofrece varias ventajas destacadas:</p>
<ul>
<li><strong>Código compartido</strong>: Compartir componentes, funciones auxiliares o definiciones de tipos entre distintos proyectos resulta muy sencillo.</li>
<li><strong>Commits atómicos</strong>: Si el cambio de una función afecta a varios paquetes, puede completarse en un solo commit, lo que garantiza la coherencia de las versiones.</li>
<li><strong>Gestión de dependencias simplificada</strong>: Todos los proyectos comparten un mismo <code>node_modules</code>, o lo optimizan mediante workspaces de pnpm/yarn, reduciendo conflictos de dependencias y diferencias entre versiones.</li>
</ul>
<p>Sin embargo, cuando el repositorio crece, el Monorepo también presenta un desafío: el <strong>rendimiento de compilación</strong>.</p>
<h2>2. Los puntos débiles de un Monorepo</h2>
<p>Imagina que tu repositorio contiene <code>docs</code>, <code>webapp</code> y una biblioteca compartida de componentes <code>ui</code>. Si solo corriges un error tipográfico en <code>docs</code>, lo último que quieres es que todos los proyectos del repositorio, incluidos <code>webapp</code> y <code>ui</code>, vuelvan a compilarse y probarse.</p>
<p>Las herramientas tradicionales de gestión de Monorepos, como Lerna, presentan limitaciones en la coordinación de tareas y la caché de compilación, lo que provoca:</p>
<ul>
<li><strong>Trabajo repetido</strong>: Cada ejecución de CI/CD compila y prueba todo desde cero, aunque la mayor parte del código no haya cambiado.</li>
<li><strong>Tiempos de compilación largos</strong>: No se pueden aprovechar eficazmente varios núcleos de CPU para ejecutar tareas en paralelo.</li>
<li><strong>Scripts complejos</strong>: Es necesario escribir scripts complicados en <code>package.json</code> para controlar manualmente el orden de ejecución de las tareas.</li>
</ul>
<h2>3. Turborepo: creado para la velocidad</h2>
<p>Turborepo es un sistema de compilación de alto rendimiento diseñado para Monorepos JavaScript/TypeScript y adquirido posteriormente por Vercel. Resuelve los problemas anteriores mediante dos tecnologías fundamentales: las <strong>compilaciones incrementales</strong> y la <strong>caché remota</strong>.</p>
<h4>a. Compilaciones incrementales y pipelines de tareas</h4>
<p>Turborepo permite definir las dependencias entre tareas en un archivo <code>turbo.json</code> situado en la raíz.</p>
<pre><code>// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": []
    },
    "lint": {
      "outputs": []
    },
    "dev": {
      "cache": false
    }
  }
}
</code></pre>
<ul>
<li><strong><code>dependsOn: ["^build"]</code></strong>: El símbolo <code>^</code> indica que la tarea <code>build</code> de un paquete depende de las tareas <code>build</code> de todos los paquetes de los que depende. Turborepo ejecuta las tareas en el orden paralelo más eficiente de acuerdo con este grafo.</li>
<li><strong><code>outputs</code></strong>: Indica a Turborepo qué archivos de salida produce la tarea.</li>
</ul>
<p>Cuando ejecutas <code>pnpm turbo build</code>, Turborepo calcula qué archivos han cambiado y solo vuelve a compilar los paquetes afectados. Si el código fuente y las dependencias de un paquete no han cambiado, Turborepo utiliza directamente el resultado de la compilación anterior, un proceso instantáneo.</p>
<h4>b. Caché remota</h4>
<p>Esta es la «función estrella» de Turborepo. No solo almacena en caché los resultados de compilación en tu equipo local, sino que también puede subirlos a un servidor remoto compartido, como Vercel o tu propio bucket de S3.</p>
<p>Esto significa que:</p>
<ul>
<li><strong>Tu compañero</strong> puede descargar la caché después de obtener el código más reciente si tú ya has compilado la parte que necesita, sin tener que volver a compilarla localmente.</li>
<li><strong>El servidor de CI/CD</strong> también puede conectarse a esta caché remota. Después de que un PR se compile y almacene en caché dentro de CI, otros desarrolladores y tareas posteriores de CI podrán aprovechar el resultado de inmediato.</li>
</ul>
<p>Esto puede reducir el tiempo de compilación de todo el equipo de decenas de minutos a unos pocos minutos o incluso decenas de segundos.</p>
<h2>Conclusión</h2>
<p>Turborepo no inventó el Monorepo, pero al introducir sistemas avanzados de caché y planificación de tareas mejora enormemente la experiencia de desarrollo en un Monorepo y resuelve su principal cuello de botella de rendimiento. Si utilizas o piensas adoptar una arquitectura Monorepo, Turborepo es sin duda una herramienta potente que puede ahorrar muchísimo tiempo a ti y a tu equipo.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[SolidJS: un framework JavaScript verdaderamente reactivo]]></title>
            <link>https://blog.m1ng.space/es/posts/frameworks/introduction-to-solidjs/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/frameworks/introduction-to-solidjs/</guid>
            <pubDate>Fri, 05 Nov 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Parece React, pero no tiene DOM virtual. Gracias a su innovador sistema de reactividad granular, SolidJS alcanza un nuevo nivel de rendimiento. Veamos cómo funciona.]]></description>
            <content:encoded><![CDATA[<h2>1. El “santo grial” de los frameworks frontend: rendimiento y experiencia</h2>
<p>Desde que React popularizó el DOM virtual (Virtual DOM), el VDOM parece haberse convertido en un elemento estándar de los frameworks frontend modernos. Al mantener en memoria una representación virtual de la interfaz y utilizar un algoritmo de diff para calcular el mínimo de actualizaciones del DOM, mejora tanto la experiencia de desarrollo como el rendimiento en la mayoría de los casos.</p>
<p>Pero el VDOM no es gratuito. Ocupa memoria y el proceso de diff también tiene un coste computacional. De ahí surge una pregunta: ¿podemos evitar el VDOM y aplicar los cambios de estado directamente y con precisión al DOM, sin renunciar a una experiencia de desarrollo declarativa similar a React?</p>
<p>SolidJS ofrece su respuesta.</p>
<h2>2. El núcleo de SolidJS: reactividad granular (Fine-Grained Reactivity)</h2>
<p>SolidJS es un framework JavaScript declarativo y reactivo. Aunque utiliza JSX y se parece mucho a React, sus principios internos son completamente diferentes.</p>
<p>El compilador de SolidJS transforma el código JSX en operaciones nativas del DOM óptimas. No utiliza un VDOM, sino que crea un grafo de dependencias formado por “señales” (Signals) reactivas. Cuando cambia el valor de una señal, solo vuelven a ejecutarse los “efectos” (Effects) o cálculos (Memos) suscritos a ella.</p>
<p><strong>Un modelo mental revolucionario: ¡los componentes solo se ejecutan una vez!</strong></p>
<p>En React, cuando cambian el state o las props, toda la función del componente vuelve a ejecutarse. En SolidJS, en cambio, la función del componente <strong>solo se ejecuta una vez de principio a fin</strong>.</p>
<pre><code>import { createSignal } from 'solid-js';

function Counter() {
  console.log('Component function runs!'); // 这句话只会在组件挂载时打印一次

  const [count, setCount] = createSignal(0);

  const increment = () =&gt; setCount(count() + 1);

  return (
    &lt;button type="button" onClick={increment}&gt;
      Count: {count()}
    &lt;/button&gt;
  );
}
</code></pre>
<p>Al pulsar el botón, solo se actualizan el lector de la señal <code>count()</code> y el nodo de texto del DOM que depende de ella. La propia función <code>Counter</code> no vuelve a ejecutarse. Esta actualización “quirúrgica” es la fuente del rendimiento excepcional de SolidJS.</p>
<h2>3. APIs principales</h2>
<p>El sistema reactivo de SolidJS se compone principalmente de tres primitivas fundamentales:</p>
<ul>
<li><strong><code>createSignal(initialValue)</code></strong>: Crea una señal reactiva y devuelve un getter y un setter: <code>[count, setCount]</code>.</li>
<li><strong><code>createEffect(() =&gt; {})</code></strong>: Crea un “efecto” que rastrea automáticamente todas las señales leídas en su interior y vuelve a ejecutarse cuando cambia cualquiera de ellas. Es ideal para realizar efectos secundarios, como manipular el DOM manualmente.</li>
<li><strong><code>createMemo(() =&gt; {})</code></strong>: Crea un valor calculado derivado y almacenado en caché. Solo vuelve a calcularse cuando cambia alguna de las señales de las que depende.</li>
</ul>
<pre><code>import { createSignal, createEffect } from 'solid-js';

function App() {
  const [firstName, setFirstName] = createSignal('John');
  const [lastName, setLastName] = createSignal('Smith');

  // createEffect 会在 firstName 或 lastName 变化时自动运行
  createEffect(() =&gt; {
    console.log(`Full name: ${firstName()} ${lastName()}`);
  });

  // ...
}
</code></pre>
<h2>4. ¿Por qué elegir SolidJS?</h2>
<ul>
<li><strong>Rendimiento excepcional</strong>: SolidJS suele ocupar los primeros puestos en distintas pruebas de rendimiento independientes, con resultados muy cercanos al código JavaScript nativo.</li>
<li><strong>Tamaño de paquete extremadamente pequeño</strong>: Como no tiene un runtime de VDOM, su paquete principal es muy pequeño.</li>
<li><strong>Experiencia de desarrollo familiar</strong>: Si conoces React Hooks, puedes aprender SolidJS con rapidez. La combinación de JSX y primitivas reactivas es potente e intuitiva.</li>
<li><strong>Reactividad real</strong>: Su modelo se acerca más a MobX o a la Composition API de Vue, pero el compilador permite conseguirlo sin coste en tiempo de ejecución.</li>
</ul>
<h2>Conclusión</h2>
<p>SolidJS representa una dirección importante en la evolución de los frameworks frontend: hacer más trabajo durante la compilación mediante el compilador para obtener menos código en tiempo de ejecución y un mayor rendimiento. Demuestra que es posible liberarse de las restricciones del DOM virtual sin sacrificar una experiencia de desarrollo declarativa. Para las aplicaciones que buscan el máximo rendimiento, SolidJS ofrece una opción muy atractiva.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[¿De verdad sabes usar Fetch? Cancelación de peticiones con AbortController]]></title>
            <link>https://blog.m1ng.space/es/posts/javascript/fetch-with-abortcontroller/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/es/posts/javascript/fetch-with-abortcontroller/</guid>
            <pubDate>Sat, 25 Sep 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[En el desarrollo Web moderno, la API Fetch se ha convertido en el estándar para realizar peticiones HTTP. Pero ¿sabes cómo cancelar de forma elegante una petición que ya no necesitas? AbortController es la respuesta.]]></description>
            <content:encoded><![CDATA[<h2>1. Fetch API: la base de las peticiones Web modernas</h2>
<p>La <code>Fetch API</code> ha sustituido al anticuado <code>XMLHttpRequest</code> como método estándar para realizar peticiones HTTP en el navegador. Está basada en Promises y ofrece una forma más sencilla y potente de hacer peticiones.</p>
<p>Sin embargo, la <code>Fetch API</code> no proporciona de forma predeterminada un mecanismo directo para cancelar peticiones. Esta función es muy importante en muchas situaciones:</p>
<ul>
<li><strong>Optimización del rendimiento</strong>: Cuando el usuario cambia rápidamente de página o introduce términos de búsqueda, cancelar las peticiones antiguas que ya no son necesarias ahorra ancho de banda y recursos del servidor.</li>
<li><strong>Evitar condiciones de carrera</strong>: En un escenario de búsqueda mientras se escribe, una petición antigua puede responder más tarde que una nueva y hacer que la interfaz muestre datos obsoletos.</li>
<li><strong>Prevenir fugas de memoria</strong>: Si un componente se desmonta antes de que termine una petición iniciada durante su ciclo de vida, intentar actualizar un componente que ya no existe puede provocar errores o fugas de memoria.</li>
</ul>
<h2>2. <code>AbortController</code>: una señal de cancelación de propósito general</h2>
<p><code>AbortController</code> es una API Web de propósito general que proporciona un mecanismo para abortar una o varias peticiones Web. Es independiente de la <code>Fetch API</code>, pero funciona perfectamente con <code>Fetch</code>.</p>
<p>La idea central de <code>AbortController</code> es:</p>
<ol>
<li>Crear una instancia de <code>AbortController</code>.</li>
<li>Obtener un objeto <code>AbortSignal</code> de esa instancia.</li>
<li>Pasar el <code>AbortSignal</code> a una API Web que se pueda abortar, como <code>fetch</code>.</li>
<li>Cuando sea necesario cancelar, llamar al método de la instancia de <code>AbortController</code>, <code>abort()</code>.</li>
</ol>
<h2>3. Combinar <code>AbortController</code> con <code>Fetch</code></h2>
<p>Veamos mediante un ejemplo cómo usar <code>AbortController</code> para cancelar una petición <code>Fetch</code>.</p>
<pre><code>const controller = new AbortController();
const signal = controller.signal; // 获取信号对象

async function fetchDataWithCancellation(url) {
  try {
    console.log('Fetching data...');
    const response = await fetch(url, { signal }); // 将信号传递给 fetch
    const data = await response.json();
    console.log('Data received:', data);
    return data;
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Fetch request was aborted.');
    } else {
      console.error('Fetch error:', error);
    }
  }
}

// 示例用法
const promise = fetchDataWithCancellation('https://jsonplaceholder.typicode.com/posts/1');

// 假设在 500 毫秒后，我们决定取消这个请求
setTimeout(() =&gt; {
  controller.abort(); // 调用 abort() 方法取消请求
  console.log('Request aborted by timeout.');
}, 500);
</code></pre>
<p>Cuando se llama a <code>controller.abort()</code>, la petición <code>fetch</code> se interrumpe inmediatamente y lanza un <code>AbortError</code>. Debes capturar este error en el bloque <code>catch</code> y comprobar <code>error.name === 'AbortError'</code> para distinguir una cancelación de otros errores de red.</p>
<h2>4. Casos de uso reales</h2>
<h4>a. Búsqueda mientras se escribe (Search-as-you-type)</h4>
<p>Cuando el usuario escribe rápidamente en un cuadro de búsqueda, cada entrada puede activar una nueva petición. Podemos cancelar la petición anterior que siga pendiente y conservar únicamente la más reciente.</p>
<pre><code>let currentController = null;

document.getElementById('searchInput').addEventListener('input', (event) =&gt; {
  if (currentController) {
    currentController.abort(); // 取消上一个请求
  }
  currentController = new AbortController();
  const signal = currentController.signal;

  const query = event.target.value;
  if (query.length &gt; 2) {
    fetchDataWithCancellation(`/api/search?q=${query}`, signal);
  }
});
</code></pre>
<h4>b. Cancelar una petición al desmontar un componente</h4>
<p>En frameworks como React o Vue, cancelar al desmontar un componente las peticiones pendientes que este inició ayuda a evitar fugas de memoria y actualizaciones innecesarias de la interfaz.</p>
<pre><code>// React 示例
useEffect(() =&gt; {
  const controller = new AbortController();
  fetchDataWithCancellation('/api/data', controller.signal);

  return () =&gt; {
    controller.abort(); // 组件卸载时取消请求
  };
}, []);
</code></pre>
<h2>Conclusión</h2>
<p><code>AbortController</code> es una herramienta indispensable en el desarrollo Web moderno. Proporciona un mecanismo nativo de cancelación para la <code>Fetch API</code> y otras operaciones asíncronas, y ayuda a los desarrolladores a crear aplicaciones más robustas, eficientes y agradables de usar. Dominar <code>AbortController</code> es una de las habilidades esenciales para convertirse en un excelente desarrollador frontend.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
    </channel>
</rss>