<?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/en</link>
        <description>m1ngsama's blog</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>en</language>
        <copyright>Copyright © 2026 m1ngsama</copyright>
        <atom:link href="https://blog.m1ng.space/en/rss.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[How to Build Your Own Operating System]]></title>
            <link>https://blog.m1ng.space/en/posts/myarch/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/myarch/</guid>
            <pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Arch Linux embraces minimalism, allowing users to build any functionality they desire. This guide walks you through setting up your own Arch...]]></description>
            <content:encoded><![CDATA[<p>Arch Linux embraces <a href="https://wiki.archlinux.org/title/Arch_Linux">minimalism</a>, allowing users to build any functionality they desire. This guide walks you through setting up your own Arch Linux system on a physical machine.</p>
<h2>Preparation</h2>
<p>You’ll need: a computer, a USB drive (or any removable storage device), an internet connection, and basic research skills.</p>
<ul>
<li>Regardless of the installation image you choose, even for offline setups, I recommend having an internet connection to ensure kernel and tool updates. If you’re experienced, you can decide otherwise.</li>
<li>For Wi-Fi, ensure the network name is in English, as the tty environment cannot display non-ASCII characters, which will appear as unreadable blocks.</li>
<li>If you plan to dual-boot on the same drive, allocate sufficient disk space for Arch Linux—at least 100GB is recommended for future software installations. Ensure the EFI partition is at least 256MB or <a href="https://wiki.archlinux.org/title/EFI_system_partition">create an additional mount point</a>.</li>
<li>Check if your Windows 10 partition uses BitLocker encryption. Obtain the recovery key in advance and disable Fast Startup in the power settings!</li>
</ul>
<blockquote>
<p>Before proceeding, carefully read and research anything you don’t understand. Operate cautiously, back up regularly—data is priceless.</p>
</blockquote>
<h2>Creating the Installation Media</h2>
<ol>
<li>Download the installation image only from the <a href="https://archlinux.org/download/">official Arch Linux download page</a>. Note that Arch Linux is a rolling-release distribution.</li>
<li>If you want to compile your own kernel, refer to the <a href="https://wiki.archlinux.org/title/Kernel/Traditional_compilation">Kernel/Traditional compilation</a> guide.</li>
<li>For the official installation image, I recommend using <a href="https://www.ventoy.net/">Ventoy</a> to create a bootable USB.</li>
</ol>
<h2>Base Installation</h2>
<h3>1. Booting from the Arch Linux Media</h3>
<blockquote>
<p>Power off, insert the USB drive, and start the computer. Enter the BIOS, select the USB as the boot device, choose the first option, and press Enter to access the Arch Linux installation environment.</p>
</blockquote>
<h3>2. UEFI Check</h3>
<pre><code>systemctl stop reflector.service
# Disable automatic mirror updates, as geographic network conditions may cause issues.
</code></pre>
<pre><code>ls /sys/firmware/efi/efivars
# If a list of EFI variables is displayed, the system is booted in UEFI mode. Most machines in 2025 use UEFI.
</code></pre>
<h3>3. Network Setup</h3>
<blockquote>
<p>Arch Linux installation requires an internet connection. Offline installation is more complex; see the <a href="https://wiki.archlinux.org/title/Offline_installation">Offline installation</a> guide.</p>
<p>For wired connections, plug in the Ethernet cable, check if the interface LED blinks, and wait a few seconds for the connection to establish.</p>
<p>In a campus network, authentication may be required via an upstream router. Refer to the <a href="https://github.com/nbtca/nbtverify">nbtverify</a> project.</p>
<p>For Wi-Fi, use <code>iwctl</code> to connect.</p>
</blockquote>
<pre><code>lspci -k | grep Network
# Check if the wireless adapter is working. Skip this if you’re sure it’s functional.
</code></pre>
<blockquote>
<p>Verify if the kernel has loaded the wireless driver.</p>
<p>You should see something like: <code>00:14.3 Network controller: Intel Corporation Wi-Fi 6 AX201 (rev 20)</code>.</p>
<p>If nothing appears, check if the wireless connection is disabled (blocked: yes).</p>
</blockquote>
<pre><code>rfkill list
# The wireless adapter is usually named wlan0.
</code></pre>
<pre><code>ip link set wlan0 up
# If you see an error like “Operation not possible due to RF-kill,” run:
rfkill Unblock wifi
</code></pre>
<pre><code># Connect to Wi-Fi using iwctl
iwctl # Enter interactive mode
device list # List wireless devices, e.g., wlan0
station wlan0 scan # Scan for networks
station wlan0 get-networks # List available Wi-Fi networks
station wlan0 connect wifi-name # Connect to the network. Non-ASCII names are not supported. Enter the password when prompted.
exit # Exit after connecting

ping www.google.com # Test network connectivity
</code></pre>
<blockquote>
<p>For network configuration issues, refer to <a href="https://wiki.archlinux.org/title/Network_configuration/Wireless">Network configuration/Wireless</a>.</p>
</blockquote>
<h3>5. Sync System Clock</h3>
<pre><code>timedatectl set-ntp true # Sync system time with network time
timedatectl status # Check service status
</code></pre>
<h3>6. Update Mirror List (Optional for U.S. Users)</h3>
<pre><code>vim /etc/pacman.d/mirrorlist # Edit the mirror list if needed
Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch # Kernel.org mirror
Server = https://mirrors.mit.edu/archlinux/$repo/os/$arch # MIT mirror
Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch # Rackspace mirror
</code></pre>
<h3>7. Create Btrfs Partitions</h3>
<h4>Check Disk Information</h4>
<pre><code>lsblk
</code></pre>
<p>Review the current partition layout. <strong>Carefully identify the target disk for Arch Linux installation</strong>.</p>
<p>Disk naming conventions:</p>
<ul>
<li><strong>SATA drives</strong>: <code>sda</code>, <code>sdb</code>, <code>sdc</code> … Partitions: <code>sda1</code>, <code>sda2</code>, etc.</li>
<li><strong>NVMe drives</strong>: <code>nvme0n1</code>, <code>nvme1n1</code> … Partitions: <code>nvme0n1p1</code>, <code>nvme0n1p2</code>, etc.</li>
</ul>
<blockquote>
<p>This example uses a SATA disk. Replace <code>/dev/sdx</code> with your actual disk.</p>
</blockquote>
<pre><code>cfdisk /dev/sdx
</code></pre>
<p>You should see a user-friendly TUI partitioning interface. 😄</p>
<h4>Partitioning Steps</h4>
<h5>1. Create Swap Partition</h5>
<ul>
<li>Use arrow keys to select <strong>Free space</strong>.</li>
<li>Press <code>[New]</code>, press Enter, and enter the size (recommended: 60%–100% of RAM).</li>
<li>Press <code>[Type]</code> and select <strong>Linux swap</strong>.</li>
</ul>
<h5>2. Create Root Partition (for Btrfs)</h5>
<ul>
<li>Select the remaining Free space, press <code>[New]</code>, and press Enter.</li>
<li>Enter the size (default: use all remaining space).</li>
<li>Keep the type as the default <strong>Linux filesystem</strong>.</li>
</ul>
<h5>3. Write Partition Table</h5>
<ul>
<li>Select <code>[Write]</code>, type <code>yes</code>, and press Enter.
<blockquote>
<p>⚠️ <strong>Note: Changes won’t take effect until written!</strong></p>
</blockquote>
</li>
</ul>
<h4>Format Partitions</h4>
<h5>Recheck Disks</h5>
<pre><code>fdisk -l
</code></pre>
<h5>Format EFI Partition (if creating a new one)</h5>
<pre><code>mkfs.fat -F32 /dev/sdxn
</code></pre>
<blockquote>
<p>💡 For dual-boot users, you can reuse the Windows EFI partition without formatting, but ensure it has enough space. See <a href="https://wiki.archlinux.org/title/Dual_boot_with_Windows">Dual boot with Windows</a>.</p>
</blockquote>
<h5>Format Swap Partition</h5>
<pre><code>mkswap /dev/sdxn
</code></pre>
<h5>Format Btrfs Partition</h5>
<pre><code>mkfs.btrfs -L myArch /dev/sdxn
</code></pre>
<h4>Create and Mount Btrfs Subvolumes</h4>
<pre><code>mount -t btrfs -o compress=zstd /dev/sdxn /mnt

# Create subvolumes
btrfs subvolume create /mnt/@        # Root subvolume
btrfs subvolume create /mnt/@home    # /home subvolume

umount /mnt
</code></pre>
<h4>⚠️ Final Reminder</h4>
<ul>
<li>Double-check all commands and operations!</li>
<li><strong>Mistakes can lead to data loss, especially deleting Windows partitions 😥.</strong></li>
</ul>
<h3>8. Mount Partitions, Starting with Root</h3>
<pre><code>mount -t btrfs -o subvol=/@,compress=zstd /dev/sdxn /mnt # Mount / directory
mkdir /mnt/home # Create /home directory
mount -t btrfs -o subvol=/@home,compress=zstd /dev/sdxn /mnt/home # Mount /home directory
mkdir -p /mnt/boot # Create /boot directory
mount /dev/sdxn /mnt/boot # Mount /boot directory
swapon /dev/sdxn # Enable swap partition
</code></pre>
<pre><code>df -h # Check mounts
free -h # Verify swap partition mount
</code></pre>
<h3>9. Install the System</h3>
<pre><code>pacstrap /mnt base base-devel linux linux-firmware btrfs-progs
# Install btrfs-progs if using Btrfs
</code></pre>
<pre><code>pacman -S archlinux-keyring
# If you encounter GPG key errors, it may be due to an outdated image. Update archlinux-keyring to resolve.
</code></pre>
<pre><code>pacstrap /mnt networkmanager vim sudo zsh zsh-completions
# Install essential functional packages with pacstrap
</code></pre>
<h3>10. Generate fstab File</h3>
<blockquote>
<p>Generate fstab to define disk partitions, based on current mounts.</p>
</blockquote>
<pre><code>genfstab -U /mnt &gt; /mnt/etc/fstab
</code></pre>
<h3>11. Enter the New System</h3>
<pre><code>arch-chroot /mnt
# Lost code highlighting? Don’t worry—you’ve successfully chrooted!
</code></pre>
<h3>12. Set Hostname and Time Zone</h3>
<pre><code>vim /etc/hostname
# Choose a hostname (avoid special characters or spaces to prevent issues; omitting a hostname can cause GUI apps to fail unexpectedly).
</code></pre>
<pre><code>vim /etc/hosts
# Edit the hosts file
</code></pre>
<blockquote>
<p>Add the following (replace myarch with your hostname, use tabs for alignment):</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/America/New_York /etc/localtime
# Create a symlink for the New York time zone (adjust as needed)
</code></pre>
<pre><code>ls /usr/share/zoneinfo/
# Check available time zones and update the command above if needed
</code></pre>
<h3>13. Hardware Clock Setup</h3>
<pre><code>hwclock --systohc
# Sync system time to hardware clock
</code></pre>
<h3>14. Set Locale</h3>
<pre><code>vim /etc/locale.gen
# Edit /etc/locale.gen, uncomment en_US.UTF-8 UTF-8
# This determines the language and character set for software
</code></pre>
<pre><code>locale-gen
# Generate locale
</code></pre>
<pre><code>echo 'LANG=en_US.UTF-8' &gt; /etc/locale.conf
# Set locale.conf
</code></pre>
<h3>15. Set Root Password</h3>
<pre><code>passwd root
# Password input is hidden—not a keyboard issue! 😄
</code></pre>
<h3>16. Install Microcode</h3>
<pre><code>pacman -S intel-ucode # For Intel CPUs
pacman -S amd-ucode # For AMD CPUs
</code></pre>
<h3>17. Install Grub Bootloader</h3>
<pre><code>pacman -S grub efibootmgr os-prober
# grub is the bootloader, efibootmgr writes boot entries to NVRAM, os-prober enables Windows 10 detection
</code></pre>
<pre><code>grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=ARCH
# Install grub to the EFI partition
</code></pre>
<pre><code>vim /etc/default/grub
# Edit boot parameters
</code></pre>
<pre><code># Change "loglevel=3 quiet" to "loglevel=5 nowatchdog"
# Add at the end: GRUB_DISABLE_OS_PROBER=false
</code></pre>
<ul>
<li>Remove the <code>quiet</code> parameter from GRUB_CMDLINE_LINUX_DEFAULT.</li>
<li>Change <code>loglevel</code> from 3 to 5 for better error debugging.</li>
<li>Add <code>nowatchdog</code> to improve boot/shutdown speed.</li>
<li>Enable <code>os-prober</code> for Windows 10 detection.</li>
</ul>
<pre><code>grub-mkconfig -o /boot/grub/grub.cfg
# Generate grub configuration file
# If Windows 10 is detected, you’ll see output like: “Found Windows Boot Manager on /dev/nvme0n1p1@/EFI/Microsoft/Boot/bootmgfw.efi done”
# If Windows is on another disk, re-mount and rerun this command after booting.
</code></pre>
<blockquote>
<p>See <a href="https://wiki.archlinux.org/title/GRUB">Arch Wiki</a> for all parameters.</p>
</blockquote>
<h3>18. Complete Installation</h3>
<pre><code>exit # Return to the installation environment
umount -R /mnt # Unmount new partitions
reboot # Reboot
</code></pre>
<blockquote>
<p>Log in with the root account after reboot.</p>
</blockquote>
<pre><code>systemctl enable --now NetworkManager # Enable and start NetworkManager service
ping www.google.com # Test network connectivity
</code></pre>
<blockquote>
<p>For Wi-Fi:</p>
</blockquote>
<pre><code>nmcli dev wifi list # List nearby Wi-Fi networks
nmcli dev wifi connect "Wi-Fi SSID" password "network password" # Connect to a Wi-Fi network
</code></pre>
<pre><code>nmtui
# I prefer nmtui—it’s user-friendly! 😄
</code></pre>
<pre><code>pacman -S fastfetch
fastfetch
# Install fastfetch to check system info
# Time for the classic neofetch moment! 😄
</code></pre>
<pre><code>shutdown 0
shutdown -h now
poweroff
# All three commands shut down the system. 😄 Shut down properly, as power policies aren’t configured yet.
</code></pre>
<h2>Congratulations 🎉</h2>
<blockquote>
<p>You’ve successfully installed a minimal, non-graphical Arch Linux system!</p>
<p>A graphical interface guide will be included in the next update, but as always: read the manual!</p>
<p>This guide is a starting point, hoping to inspire more enthusiasts to join the tech community!</p>
</blockquote>
<hr />
<blockquote>
<p>Related: <a href="https://nbtca.space/">NBTCA</a></p>
</blockquote>
<ul>
<li>📧 NBTCA Email: <a href="mailto:contact@nbtca.com">contact@nbtca.com</a></li>
<li>🌐 NBTCA GitHub: <a href="https://github.com/nbtca">https://github.com/nbtca</a></li>
</ul>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Understanding Hydration in Depth: A “Necessary Evil” of Modern Frontend Frameworks?]]></title>
            <link>https://blog.m1ng.space/en/posts/frameworks/deep-dive-into-hydration/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/frameworks/deep-dive-into-hydration/</guid>
            <pubDate>Sat, 10 Aug 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Server-side rendering (SSR) delivers a fast initial load, but it also introduces Hydration and its performance cost. What is it? Why does it cause an “interaction delay”? And which alternatives is the community exploring?]]></description>
            <content:encoded><![CDATA[<h2>1. The Benefits and Troubles of SSR</h2>
<p>Server-side rendering (SSR) greatly improves the “first-screen loading speed” (FCP) of web applications. The browser receives complete HTML content and renders it immediately, allowing users to see the page quickly, which is highly beneficial for both SEO and perceived performance.</p>
<p>At this point, however, the page is only a “static shell.” Although it appears to have finished loading, button clicks and input interactions do nothing. To bring this static page “to life,” we need a process called <strong>Hydration</strong>.</p>
<h2>2. What Is Hydration?</h2>
<p>Hydration is the process in which a client-side JavaScript framework “takes over” the static HTML rendered by the server.</p>
<p>The process is roughly as follows:</p>
<ol>
<li>The browser downloads and executes the JavaScript bundles required by the page, such as the React or Vue runtime and application code.</li>
<li>The framework rebuilds the component tree in memory.</li>
<li>The framework traverses the server-rendered DOM and attaches event listeners, such as <code>onClick</code>, to the corresponding DOM nodes.</li>
<li>The framework ensures that the client-side component state matches the state used during server rendering.</li>
</ol>
<p>Only after this process is complete does the page become truly interactive. You can think of Hydration like this: you receive an already assembled LEGO model (HTML), but to make one part move, you must read the entire assembly manual (JS) from beginning to end, check the position of every brick, and only then install a battery in that part (attach event listeners).</p>
<h2>3. The Hydration Problem: “A Non-Interactive Interactive Interface”</h2>
<p>Hydration solves the inability of an SSR page to respond to interactions, but it also introduces a new performance bottleneck known as the <strong>“Uncanny Valley”</strong> or <strong>“Hydration Gap.”</strong></p>
<p>This refers to the delay between the moment users see page content (FCP) and the moment the page can actually respond to their interactions (TTI). During this interval, the page looks usable but is effectively “frozen.”</p>
<p>The reasons include:</p>
<ul>
<li><strong>Blocking JavaScript download and execution</strong>: The browser must download, parse, and execute a large amount of JavaScript before the Hydration process can begin.</li>
<li><strong>Expensive startup cost</strong>: The framework must do substantial work on the client to rebuild the component tree and attach event listeners, even when 90% of the page consists of non-interactive static content.</li>
</ul>
<h2>4. Hydration Optimizations and Alternatives</h2>
<p>To address Hydration's performance problems, the community has explored several more advanced patterns.</p>
<h4>a. Partial Hydration</h4>
<p>The <strong>Islands Architecture</strong> popularized by <strong>Astro</strong> is a typical implementation of partial Hydration. Its core idea is that, by default, every component outputs only static HTML (zero JS). Components that require interaction can be explicitly marked as “islands.”</p>
<p>At build time, Astro bundles and sends JavaScript only for these “island” components. The browser therefore hydrates only a few isolated parts of the page instead of the entire page, greatly reducing the amount of JavaScript that must be loaded and executed at startup.</p>
<h4>b. Progressive Hydration</h4>
<p>This is a more fine-grained optimization strategy that hydrates components according to a priority order. For example, components within the viewport or those the user is about to interact with can be hydrated first, while non-critical components near the bottom of the page are deferred.</p>
<h4>c. Resumability</h4>
<p>The <strong>Qwik</strong> framework proposes a revolutionary concept—<strong>Resumability</strong>—that aims to eliminate Hydration entirely.</p>
<p>It works as follows:</p>
<ol>
<li><strong>Serialization</strong>: On the server, Qwik serializes all application state, component relationships, event-listener information, and other data, then embeds it in the HTML.</li>
<li><strong>Resumption</strong>: On the client, Qwik's tiny runtime (about 1KB) does not need to rebuild the component tree or attach every event at startup. Through a global event listener, it can determine exactly which small piece of code should be downloaded and executed when the user clicks a particular button.</li>
</ol>
<p>Qwik's goal is <strong>Instant-on</strong> interactivity. Instead of “re-executing” the server's work, it “resumes” execution from where the server stopped.</p>
<h2>Conclusion</h2>
<p>Hydration is the bridge between server rendering and client interaction, but in traditional implementations it is an expensive process that may harm the user experience. Modern frontend frameworks are using innovative patterns such as partial Hydration (Astro) and Resumability (Qwik) to move beyond this “necessary evil” and pursue even better performance and faster interaction.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[How WebAssembly (WASM) Is Changing Frontend Development]]></title>
            <link>https://blog.m1ng.space/en/posts/wasm/wasm-is-changing-frontend/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/wasm/wasm-is-changing-frontend/</guid>
            <pubDate>Sun, 30 Jun 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[JavaScript is no longer the only player in the browser. By delivering near-native performance, WebAssembly (WASM) is bringing desktop-class applications such as Photoshop and Figma to the Web.]]></description>
            <content:encoded><![CDATA[<h2>1. The Browser's “Second Language”</h2>
<p>For a long time, JavaScript was virtually the only language for Web frontend development. Although it has been tremendously successful, as a dynamically interpreted language it still encounters performance bottlenecks when handling CPU-intensive tasks such as 3D rendering, video encoding and decoding, and complex computation.</p>
<p>WebAssembly, or WASM, emerged to break through this limitation. It is not intended to replace JavaScript, but to serve as a powerful complement that brings unprecedented performance and possibilities to the Web platform.</p>
<h2>2. What Is WebAssembly?</h2>
<p>WebAssembly is a <strong>binary instruction format</strong> designed for a stack-based virtual machine. It is a low-level, assembly-like language, but it is not meant to be written directly by developers.</p>
<p>Instead, it is designed as a <strong>compilation target</strong> for higher-level languages such as C, C++, Rust, and Go. You can write code in these high-performance languages, compile it into a <code>.wasm</code> file, and run it in the browser at near-native speed.</p>
<p><strong>Key points:</strong></p>
<ul>
<li><strong>It is not a replacement for JavaScript</strong>: WASM and JavaScript work as partners.</li>
<li><strong>It is a compilation target</strong>: You write C++ or Rust and compile it to WASM.</li>
<li><strong>It is fast, efficient, and portable</strong>: Performance has been a core goal from the beginning.</li>
</ul>
<h2>3. How JavaScript and WASM Work Together</h2>
<p>A WASM module runs inside a sandbox. It cannot directly access the DOM, call Web APIs, or make network requests. All of these operations need JavaScript to act as an intermediary “glue” layer.</p>
<p>A typical collaboration model looks like this:</p>
<ol>
<li><strong>JavaScript handles orchestration</strong>: JS code manages the application's overall logic, processes user events, and updates the DOM.</li>
<li><strong>WASM handles computation</strong>: When a computation-intensive task appears, JS calls functions exported from the <code>.wasm</code> module.</li>
<li><strong>Data exchange</strong>: JS and WASM can exchange data efficiently, primarily numeric values and blocks of linear memory.</li>
</ol>
<p>Think of JavaScript as the “manager” and WebAssembly as the “expert engineer.” The manager communicates and coordinates, while the engineer solves the most demanding technical problems.</p>
<h2>4. Real-World Use Cases</h2>
<p>WASM is no longer an experimental technology. Many leading Web applications already use it to power their core features:</p>
<ul>
<li><strong>Figma</strong>: The core rendering engine of this popular online design tool is written in C++ and compiled to WASM, enabling a fluid graphics-editing experience.</li>
<li><strong>Adobe Photoshop &amp; Lightroom</strong>: Adobe successfully brought the C++ core codebases of its flagship desktop applications to the Web through WASM, allowing users to work with a powerful Photoshop experience in the browser.</li>
<li><strong>Google Earth</strong>: The new Google Earth runs entirely in the browser, with WASM driving its complex 3D globe rendering.</li>
<li><strong>AutoCAD Web App</strong>: Autodesk compiled its large C++ CAD engine to WASM, making it possible to run a complete AutoCAD experience in the browser.</li>
</ul>
<p>These examples show that WASM can already bring complex software once considered possible only as desktop applications to the Web platform.</p>
<h2>5. The Future: WASI and Life Beyond the Browser</h2>
<p>WASM's ambitions extend beyond the browser. <strong>WASI (WebAssembly System Interface)</strong> is an emerging standard intended to provide WASM with a standard set of system-level APIs, including file-system and network access.</p>
<p>This means that <code>.wasm</code> files may eventually become a universal, cross-platform, secure binary format that can run anywhere—from servers, where it could challenge Docker, to edge-computing nodes and IoT devices—truly achieving “compile once, run anywhere.”</p>
<h2>Conclusion</h2>
<p>WebAssembly is profoundly changing how we understand the limits of Web applications. It lets JavaScript focus on what it does best—UI interaction and application orchestration—while handing the performance ceiling to WASM modules compiled from systems languages such as Rust and C++. For frontend developers, understanding WASM's capabilities and appropriate use cases will be essential to building the next generation of high-performance Web applications.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Creating Seamless Page Transitions with the View Transitions API]]></title>
            <link>https://blog.m1ng.space/en/posts/css/view-transitions-api/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/css/view-transitions-api/</guid>
            <pubDate>Mon, 22 Apr 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[The “white flash” during page navigation has long harmed the user experience. Browsers now provide the native View Transitions API, allowing us to create cinematic page transitions with just a few lines of code.]]></description>
            <content:encoded><![CDATA[<h2>1. The Experience Gap in Page Navigation</h2>
<p>In web development, we have long faced a dilemma in user experience:</p>
<ul>
<li><strong>Multi-Page Applications (MPAs)</strong>: Their architecture is simple and stable, but every navigation performs a complete page load, causing a “white screen” or “flash” and breaking continuity.</li>
<li><strong>Single-Page Applications (SPAs)</strong>: Frontend routing provides a smooth experience without page reloads, but developers must manually handle complex transition animations, state management, and code splitting, which makes development expensive.</li>
</ul>
<p>Is there a way to retain the simplicity of an MPA while gaining the smooth transitions of an SPA? The View Transitions API was created for exactly this purpose.</p>
<h2>2. What Is the View Transitions API?</h2>
<p>The View Transitions API is a native browser API that provides a mechanism for easily creating animated transitions between two different DOM states.</p>
<p>Its core workflow is very simple:</p>
<ol>
<li>When you trigger a transition, the API takes a “snapshot” of the current page (the old state).</li>
<li>You then use JavaScript to update the DOM to its new state.</li>
<li>The API also takes a “snapshot” of the new page (the new state).</li>
<li>Finally, the browser creates a smooth default transition, usually a cross-fade, between the “old snapshot” and the “new snapshot.”</li>
</ol>
<p>The browser handles all of this efficiently at a low level; we only need to call a simple function.</p>
<h2>3. How Do You Use It?</h2>
<p>Using View Transitions in a single-page application is very simple. Just wrap your DOM update logic in the <code>document.startViewTransition</code> function.</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>That alone gives your page navigation a default cross-fade effect!</p>
<h2>4. Customizing the Transition Animation</h2>
<p>The default cross-fade is excellent, but the API's real power lies in its customizability. When <code>startViewTransition</code> runs, the browser creates a DOM structure containing these pseudo-elements:</p>
<ul>
<li><code>::view-transition</code>: The root element that contains the entire transition.</li>
<li><code>::view-transition-old(root)</code>: The “snapshot” of the old view.</li>
<li><code>::view-transition-new(root)</code>: The “snapshot” of the new view.</li>
</ul>
<p>We can override the default effect with a CSS <code>animation</code>, for example to create a slide-in animation:</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. “Morphing” Between Elements: <code>view-transition-name</code></h2>
<p>The API's most impressive capability is recognizing <strong>different elements</strong> on two pages as <strong>the same object</strong> and creating a smooth “morphing” animation between them.</p>
<p>This is implemented through the <code>view-transition-name</code> CSS property.</p>
<p><strong>Page A (list page):</strong></p>
<pre><code>&lt;img src="thumbnail.jpg" style="view-transition-name: hero-image;" /&gt;
</code></pre>
<p><strong>Page B (detail page):</strong></p>
<pre><code>&lt;img src="full-size.jpg" style="view-transition-name: hero-image;" /&gt;
</code></pre>
<p>When navigating from Page A to Page B, the browser sees that both elements have the same <code>view-transition-name</code>. It automatically calculates the differences in their position, size, and shape, then generates a smooth transition instead of a simple cross-fade. This works especially well for transitions involving images, cards, and similar elements.</p>
<h2>Conclusion</h2>
<p>The View Transitions API brings long-awaited native transition capabilities to the web. It greatly simplifies interactions that previously required complex JavaScript animation libraries, making cinematic and fluid application experiences easier to create than ever. As frameworks such as Astro and Nuxt integrate it, and as support for multi-page applications becomes more widespread, it is certain to become a standard skill for modern web developers.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Understanding React Server Components in Depth]]></title>
            <link>https://blog.m1ng.space/en/posts/react/deep-dive-into-rsc/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/react/deep-dive-into-rsc/</guid>
            <pubDate>Fri, 15 Mar 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[React Server Components (RSC) are one of React’s most important paradigm shifts in recent years. This article explores their core concepts, the problems they solve, and how they reshape the way we build applications.]]></description>
            <content:encoded><![CDATA[<h2>1. What Are React Server Components (RSC)?</h2>
<p>React Server Components (RSC) are a new type of React component rendered <strong>at build time or on the server</strong>. Unlike traditional components rendered in the client browser—now called “Client Components”—RSC code is never sent to the client. The browser receives only rendered HTML or a special streaming format.</p>
<p>This marks React's evolution from a purely client-side library into a full-stack framework that can work seamlessly across the server and the client.</p>
<h2>2. What Problems Do RSC Solve?</h2>
<p>RSC were created primarily to address the following core pain points:</p>
<ul>
<li><strong>Huge JavaScript bundle sizes</strong>: Traditional React applications bundle and send the JavaScript code for every component to the client, including components that are not directly interactive, resulting in slow initial loads. RSC let us keep many components—such as text, layouts, and data displays—on the server, achieving a <strong>zero-JavaScript footprint</strong> for them.</li>
<li><strong>Request waterfalls</strong>: The typical client-side data-fetching pattern is: render a component -&gt; <code>useEffect</code> -&gt; make a request -&gt; wait for the data -&gt; render again. Deeply nested components can create a request waterfall that delays page rendering. RSC can fetch data directly on the server with <code>async/await</code> and stream the data together with the components to the client, solving this problem at its root.</li>
<li><strong>Direct access to backend resources</strong>: RSC run in a server environment such as Node.js, which means they can directly and securely access databases, file systems, or internal APIs without exposing additional API endpoints to the client.</li>
</ul>
<h2>3. Server Components vs. Client Components</h2>
<p>Understanding the difference between them is the key to mastering RSC.</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Server Components</th>
<th>Client Components</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Runtime environment</strong></td>
<td>Server (Node.js, etc.)</td>
<td>Client (browser)</td>
</tr>
<tr>
<td><strong>JS sent to the client</strong></td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>State</strong></td>
<td>Not supported (e.g., <code>useState</code>)</td>
<td>Supported</td>
</tr>
<tr>
<td><strong>Lifecycle/Hooks</strong></td>
<td>Not supported (e.g., <code>useEffect</code>)</td>
<td>Supported</td>
</tr>
<tr>
<td><strong>Interaction (events)</strong></td>
<td>Not supported (e.g., <code>onClick</code>)</td>
<td>Supported</td>
</tr>
<tr>
<td><strong>Data fetching</strong></td>
<td>Supports <code>async/await</code></td>
<td>Through <code>useEffect</code> or a data-fetching library</td>
</tr>
<tr>
<td><strong>Import rules</strong></td>
<td>Cannot import Client Components</td>
<td>Can import Server Components (as <code>children</code> or a <code>prop</code>)</td>
</tr>
</tbody>
</table>
<p><strong>Rule of thumb</strong>: Treat all components as Server Components by default. Only when a component needs <code>useState</code>, <code>useEffect</code>, or user-event handlers such as <code>onClick</code> should you add the <code>"use client";</code> directive at the top of the file to mark it as a Client Component.</p>
<h2>4. How Do They Work Together?</h2>
<p>A modern React application is a mixture of both. Consider a blog post page, for example:</p>
<ul>
<li><code>PageLayout</code> (Server Component): Handles the overall layout.</li>
<li><code>ArticleContent</code> (Server Component): Retrieves article data from a database or Markdown file and renders it.</li>
<li><code>LikeButton</code> (Client Component): Contains <code>useState</code> and an <code>onClick</code> event to handle the like interaction.</li>
<li><code>Comments</code> (Client Component): Fetches and displays comments, with interactions such as form submission.</li>
</ul>
<p>Server Components can reserve “slots” for Client Components on the server, and the browser ultimately assembles the two seamlessly.</p>
<h2>Conclusion</h2>
<p>React Server Components represent a profound paradigm shift. They extend React's capabilities from the browser to the server, bringing significant performance benefits and a better development experience. Although they introduce a new mental model, practical adoption through frameworks such as the Next.js App Router is quickly making RSC a new standard for building high-performance, scalable Web applications.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Say Goodbye to process.env.UNDEFINED: Type-Safe Environment Variables in Your Project]]></title>
            <link>https://blog.m1ng.space/en/posts/typescript/typesafe-environment-variables/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/typescript/typesafe-environment-variables/</guid>
            <pubDate>Sun, 25 Feb 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Missing or malformed environment variables are a common source of runtime bugs. With a validation library such as Zod, an application can verify at startup that every environment variable exists and has the correct type, eliminating this class of problem.]]></description>
            <content:encoded><![CDATA[<h2>1. The <code>process.env</code> “Trap”</h2>
<p>In a Node.js application, we access environment variables through <code>process.env</code>. It is a simple and effective mechanism, but it has an inherent problem: it is <strong>not type-safe</strong>.</p>
<p>Values in the <code>process.env</code> object are either <code>string</code> or <code>undefined</code>. This leads to several common problems:</p>
<ul>
<li><strong>Unexpected <code>undefined</code></strong>: If you forget to set a variable in the <code>.env</code> file or on the server, <code>process.env.MY_VAR</code> will be <code>undefined</code> at runtime. This may cause a <code>TypeError</code> deep inside the application's logic.</li>
<li><strong>Type mismatch</strong>: You may expect a port number to have the <code>number</code> type, but <code>process.env.PORT</code> is always a string, so you have to call <code>parseInt</code> manually wherever it is used.</li>
<li><strong>Scattered validation logic</strong>: We often write defensive code throughout the codebase, such as <code>const port = process.env.PORT || 3000;</code>, which makes environment-variable management disorderly.</li>
</ul>
<h2>2. Core Principle: Fail Fast</h2>
<p>For critical configuration such as environment variables, a best practice is to <strong>fail fast</strong>.</p>
<p>This means the application should check at the very beginning of startup whether every required environment variable has been provided and is correctly formatted. If anything is wrong, the application should immediately throw an error and stop instead of crashing at some unpredictable point later because of a configuration error.</p>
<p>This lets us discover configuration problems immediately during deployment or development, rather than when a user visits the application.</p>
<h2>3. Type-Safe Validation with Zod</h2>
<p>Zod is a TypeScript-first schema declaration and validation library. It is an excellent fit for solving type-safety problems with environment variables.</p>
<p>Our strategy is:</p>
<ol>
<li>Define a Zod schema for all environment variables.</li>
<li>Parse <code>process.env</code> with this schema when the application starts.</li>
<li>If parsing fails—that is, validation does not pass—Zod throws an error and application startup fails.</li>
<li>If parsing succeeds, we receive a fully typed object and use it throughout the application.</li>
</ol>
<h4>Implementation Example</h4>
<p>First, install Zod: <code>pnpm add zod</code></p>
<p>Then create a dedicated file for handling environment variables, such as <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>You can now import the object from <code>src/env</code> as <code>env</code> anywhere else in your application.</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. Benefits</h2>
<p>This pattern brings immediate benefits:</p>
<ul>
<li><strong>Complete type safety</strong>: When you access <code>env.PORT</code> in code, TypeScript knows it is a <code>number</code>.</li>
<li><strong>Centralized documentation and validation</strong>: The <code>env.ts</code> file itself becomes the authoritative documentation for environment variables, with all validation logic in one place.</li>
<li><strong>Reliable runtime behavior</strong>: It prevents runtime bugs caused by misspelled, missing, or incorrectly formatted environment variables.</li>
<li><strong>Fail fast</strong>: Any configuration problem is discovered immediately during deployment or development.</li>
</ul>
<h2>Conclusion</h2>
<p>Moving environment-variable validation to application startup and using a tool such as Zod to guarantee type safety is an engineering practice with an excellent return on investment. It can significantly improve application robustness and developer confidence, making it an essential part of a modern TypeScript project.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Vue 3.4 Update: Simpler v-bind Syntax and Better Performance]]></title>
            <link>https://blog.m1ng.space/en/posts/vue/vue-3-4-v-bind-updates/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/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” has been released with a more efficient reactivity-system rewrite and a stable defineModel API, greatly improving the developer experience for components with two-way binding.]]></description>
            <content:encoded><![CDATA[<h2>1. Introducing Vue 3.4 “Slam Dunk”</h2>
<p>At the beginning of 2024, the Vue team released version 3.4, codenamed “Slam Dunk.” Although this release is not as revolutionary as version 3.0, it brings important improvements in two key areas: <strong>internal performance optimization</strong> and <strong>developer-experience improvements</strong>.</p>
<p>The most notable changes include a rewritten reactivity system, the stable <code>defineModel</code> API, and more concise <code>v-bind</code> syntax.</p>
<h2>2. Reactivity-System Rewrite</h2>
<p>Vue 3.4 includes a major rewrite of its core reactivity system. The primary goal of this work is to improve the efficiency of <code>computed</code> properties.</p>
<p>In previous versions, a computed property could be recalculated unnecessarily even when its dependencies had not actually changed. With smarter dependency tracking, the new version ensures that computation is triggered only when it is genuinely needed, reducing unnecessary component rerenders.</p>
<p>For most developers, this is a “free lunch”: no code changes are required, and upgrading to version 3.4 provides the performance improvement automatically.</p>
<h2>3. <code>defineModel</code>: Elegant Two-Way Binding for Components</h2>
<p>Before version 3.4, implementing <code>v-model</code>-style two-way binding on a component required a fair amount of boilerplate:</p>
<p><strong>Before 👎:</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 moves the <code>defineModel</code> API from experimental status to a stable release. The same behavior now takes just one line of code:</p>
<p><strong>Now (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>The <code>defineModel</code> macro automatically registers the <code>modelValue</code> prop and the <code>update:modelValue</code> event, then returns a directly readable and writable <code>ref</code>. This greatly simplifies the development of components that support <code>v-model</code>.</p>
<h2>4. Same-Name Shorthand for <code>v-bind</code></h2>
<p>Another syntax improvement for developer experience is same-name shorthand for <code>v-bind</code>. When a prop passed to a component has the same name as a variable defined in <code>&lt;script setup&gt;</code>, you can now omit the attribute value.</p>
<p><strong>Before 👎:</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>Now (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>This small change makes template code cleaner and easier to read.</p>
<h2>Conclusion</h2>
<p>Vue 3.4 is a solid iteration. Through low-level performance optimization and high-level API simplification, it further reinforces Vue's philosophy as a “progressive” framework—providing strong performance guarantees for large applications while delivering practical convenience in everyday development. The stabilization of <code>defineModel</code> is especially valuable: it resolves a long-standing pain point in component encapsulation and is the most praiseworthy highlight of this release.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Goodbye Rome, Hello Biome: A New All-in-One Frontend Toolchain]]></title>
            <link>https://blog.m1ng.space/en/posts/build-tools/intro-to-biome/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/build-tools/intro-to-biome/</guid>
            <pubDate>Fri, 10 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[The vision behind the Rome toolchain once excited countless developers. After its commercial effort failed, the community carried on its legacy under the name Biome. Can this Rust-based all-in-one toolchain become the next member of our toolbox?]]></description>
            <content:encoded><![CDATA[<h2>1. “Tool Fatigue” in Frontend Development</h2>
<p>In modern frontend development, a project usually needs a complex combination of tools to run smoothly:</p>
<ul>
<li><strong>Linter</strong>: ESLint checks the quality of JavaScript/TypeScript code.</li>
<li><strong>Formatter</strong>: Prettier keeps code style consistent.</li>
<li><strong>Compiler</strong>: Babel or TypeScript (tsc) transforms code.</li>
<li><strong>Bundler</strong>: Webpack, Rollup, or Vite bundles the application.</li>
</ul>
<p>Managing these tools' configurations, plugins, versions, and interactions is a substantial job in itself. This is what is known as “tool fatigue.” For years, the community has explored the possibility of an all-in-one toolchain, hoping that one tool could solve every problem.</p>
<h2>2. Rome's Vision and the Birth of Biome</h2>
<p>Rome was an ambitious project started by former Facebook employee and Babel author Sebastian McKenzie. It aimed to rewrite the entire frontend toolchain in TypeScript. Its vision was to provide a zero-configuration, high-performance unified tool. After years of development and attempts at commercialization, however, Rome Labs announced in 2023 that it would cease operations.</p>
<p>Fortunately, Rome's core code was open source. The community acted quickly, creating a fork named <strong>Biome</strong> and placing it under the maintenance of a dedicated community organization to carry on and realize Rome's original vision.</p>
<h2>3. What Is Biome?</h2>
<p>Biome is a high-performance frontend toolchain rewritten in Rust. It aims to provide a unified, extremely fast development experience that can replace a series of independent tools such as ESLint and Prettier.</p>
<p>By the end of 2023, Biome's core functionality was focused mainly on its <strong>Linter</strong> and <strong>Formatter</strong>, where it had already demonstrated remarkable strength.</p>
<h4>Main advantages:</h4>
<ul>
<li><strong>Outstanding performance</strong>: Thanks to Rust, Biome runs far faster than ESLint and Prettier, which are written in JavaScript. For large codebases, the improvement can reach an order of magnitude.</li>
<li><strong>Unified configuration</strong>: A single <code>biome.json</code> file manages the behavior of every tool, replacing scattered files such as <code>.eslintrc</code>, <code>.prettierrc</code>, and <code>.editorconfig</code>.</li>
<li><strong>Detailed diagnostics</strong>: Biome's Linter not only identifies errors but also provides extensive context and suggested fixes, helping developers understand the root cause.</li>
<li><strong>Prettier compatibility</strong>: Biome's formatter aims to be compatible with more than 95% of Prettier's rules, keeping migration costs from Prettier very low.</li>
</ul>
<h2>4. Getting Started</h2>
<p>You can quickly try Biome in your project through <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>Create a <code>biome.json</code> file to customize rules and configuration:</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. Looking Ahead</h2>
<p>Biome has a very clear roadmap: after stabilizing the Linter and Formatter, it plans to implement a Compiler, Bundler, Test Runner, and other features step by step, ultimately becoming a true all-in-one toolchain.</p>
<p>Although it is still young, Biome's excellent performance, open community-driven model, and focus on developer experience have already made it a new force that cannot be ignored in frontend tooling. For teams seeking to simplify their tool stack and improve development efficiency, Biome offers a highly attractive option for the future.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Bun 1.0 Is Here: The All-in-One Runtime Challenging Node.js]]></title>
            <link>https://blog.m1ng.space/en/posts/build-tools/bun-1-0-release/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/build-tools/bun-1-0-release/</guid>
            <pubDate>Sun, 10 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[In September 2023, the release of Bun 1.0 made waves in the JavaScript community. Built from scratch, this JavaScript toolkit officially challenged Node.js dominance with its remarkable speed and all-in-one design.]]></description>
            <content:encoded><![CDATA[<h2>1. A New Contender Among JavaScript Runtimes</h2>
<p>For more than a decade, Node.js has been synonymous with server-side JavaScript. Although Deno introduced new ideas around security and modern APIs, Node.js never seemed truly shaken. In September 2023, however, the official release of Bun 1.0 suggested that this might change.</p>
<p>Bun is not merely another JavaScript runtime. It is a performance-focused, all-in-one toolkit designed from scratch to cover the entire lifecycle from development to deployment.</p>
<h2>2. Bun's Core: Speed, Speed, and More Speed</h2>
<p>Performance is Bun's foremost design goal. To achieve it, Bun made several distinctive technical choices:</p>
<ul>
<li><strong>The Zig language</strong>: Bun is implemented in Zig, a modern systems programming language focused on performance and memory control, allowing it to optimize low-level details aggressively.</li>
<li><strong>The JavaScriptCore engine</strong>: Unlike Node.js and Deno, which use Google's V8 engine, Bun chose Apple's JavaScriptCore (JSC). JSC is known for faster startup and generally lower memory consumption.</li>
</ul>
<p>These choices have enabled Bun to show remarkable performance in script startup, <code>bun install</code> speed, and the execution efficiency of its built-in APIs.</p>
<h2>3. More Than a Runtime</h2>
<p>Bun's all-in-one design is another defining feature. It is not only a replacement for the <code>node</code> command; it also includes:</p>
<ul>
<li><strong>Package manager</strong>: <code>bun install</code> is several or even dozens of times faster than <code>npm install</code>. It uses a global module cache and efficient dependency resolution algorithms to reduce installation time substantially.</li>
<li><strong>Build tool/bundler</strong>: Bun includes a high-performance bundler that can package a project directly into an executable or browser code, with performance comparable to esbuild.</li>
<li><strong>Native transpiler</strong>: You can run TypeScript (<code>.ts</code>) and JSX (<code>.jsx</code>, <code>.tsx</code>) files directly, without configuring <code>tsc</code> or <code>babel</code> in advance. Bun transpiles them extremely quickly at runtime.</li>
<li><strong>Test runner</strong>: <code>bun test</code> provides a testing environment that is highly compatible with Jest, but runs much faster.</li>
</ul>
<p>This means that for a new project, installing the single <code>bun</code> tool may be enough to handle the entire development, testing, and bundling process.</p>
<h2>4. Node.js Compatibility</h2>
<p>To make migration easier, the Bun team has invested substantial effort in compatibility with Node.js APIs. Bun includes support for <code>node_modules</code> resolution, CommonJS (<code>require</code>) and ESM (<code>import</code>) modules, and many Node.js core modules such as <code>fs</code>, <code>path</code>, and <code>http</code>.</p>
<p>In theory, many existing Node.js projects can run directly with <code>bun</code> and immediately benefit from improved performance.</p>
<h2>5. Challenges and the Future</h2>
<p>Bun challenges the frontend world's long-standing philosophy of combining small, specialized tools. Its comprehensive model offers unmatched convenience and high out-of-the-box performance, but it may also sacrifice some flexibility.</p>
<p>Node.js has an enormously large and mature ecosystem that Bun cannot match in the short term. Nevertheless, the release of Bun 1.0 marked its readiness for production. Its remarkable performance and integrated development experience are irresistibly attractive to new projects and teams pursuing maximum efficiency.</p>
<p>Whether or not Bun eventually overturns Node.js dominance, its arrival has already brought new energy to the evolution of the JavaScript toolchain and established a new performance benchmark.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Native CSS Nesting Is Finally Here!]]></title>
            <link>https://blog.m1ng.space/en/posts/css/native-css-nesting/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/css/native-css-nesting/</guid>
            <pubDate>Fri, 01 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[We waited ten years! Selector nesting, a core Sass/Less feature, is finally supported natively by major browsers. Say goodbye to preprocessors and welcome a cleaner, more intuitive way to write CSS.]]></description>
            <content:encoded><![CDATA[<h2>1. The “Unofficial” Feature We Used for a Decade</h2>
<p>If you wrote frontend code several years ago, you are certainly familiar with Sass or Less. These CSS preprocessors have one feature loved by nearly every developer: <strong>selector nesting</strong>. It lets us write child selector rules inside a parent selector, much like writing HTML, greatly improving code readability and organization.</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>For years, we relied on build tools to compile this syntax into ordinary CSS that browsers could understand. Now, however, things have changed.</p>
<h2>2. Native CSS Nesting Arrives</h2>
<p>Starting in early 2023, major browsers including Chrome, Safari, and Firefox successively announced support for the native CSS Nesting Module. This means we can write nested rules directly in <code>.css</code> files without any preprocessing.</p>
<p>The example above can be written in native CSS like this:</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>Yes, it looks exactly like Sass!</p>
<h2>3. The Important Role of <code>&amp;</code></h2>
<p>As in preprocessors, the <code>&amp;</code> symbol plays a key role in native nesting: it represents the parent selector. This is especially useful when handling pseudo-classes, pseudo-elements, or combined selectors.</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. One Small but Important Difference from Sass</h2>
<p>In Sass, you can nest a class selector directly, as in <code>.card { .title { ... } }</code>. This was not allowed in early versions of the native CSS Nesting specification: every nested rule had to begin with a symbol such as <code>&amp;</code>, <code>&gt;</code>, <code>+</code>, or <code>~</code>.</p>
<p>Although the latest specification relaxed this restriction and permits directly nested type selectors, such as <code>article { h1 { ... } }</code>, using <code>&amp;</code> remains the best practice when nesting class selectors because it is explicit and avoids ambiguity.</p>
<pre><code>/* 推荐写法 */
.article {
  &amp; .author-name {
    font-style: italic;
  }
}

/* 不推荐的写法 (在某些早期实现或严格解析器中可能无效) */
.article {
  .author-name {
    font-style: italic;
  }
}
</code></pre>
<p>Using <code>&amp;</code> clearly expresses that <code>.author-name</code> is a descendant of <code>.article</code>.</p>
<h2>5. Browser Support and the Future</h2>
<p>As of September 2023, all major modern browsers—Chrome 112+, Safari 16.5+, and Firefox 117+—support CSS Nesting. This means we can begin adopting it gradually in production.</p>
<p>The arrival of native CSS nesting is another important milestone in the evolution of the Web platform itself. It reduces our dependence on build tools, makes CSS more powerful and expressive, and lets new frontend developers write and understand style code more intuitively.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[The Rise of Utility-First CSS: Tailwind CSS as an Example]]></title>
            <link>https://blog.m1ng.space/en/posts/css/the-rise-of-utility-first/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/css/the-rise-of-utility-first/</guid>
            <pubDate>Sun, 20 Aug 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Say goodbye to the frustrations of BEM and CSS-in-JS? Utility-First CSS and its leading framework, Tailwind CSS, have swept through the frontend community in recent years. What makes them so appealing?]]></description>
            <content:encoded><![CDATA[<h2>1. The Difficulties of Traditional CSS Methodologies</h2>
<p>Before Utility-First appeared, we had many excellent CSS methodologies for organizing styles, including:</p>
<ul>
<li><strong>BEM (Block, Element, Modifier)</strong>: Uses the <code>block__element--modifier</code> naming convention to keep styles independent and maintainable.</li>
<li><strong>OOCSS (Object-Oriented CSS)</strong>: Advocates separating structure from skin and containers from content.</li>
<li><strong>CSS-in-JS</strong>: Writes CSS directly inside JavaScript components to provide component-level style encapsulation.</li>
</ul>
<p>These methods solve global CSS pollution and code-organization problems to some extent, but they often introduce new issues: the mental effort spent naming countless classes, the inconvenience of switching between files, and the possible runtime overhead of CSS-in-JS.</p>
<h2>2. What Is Utility-First?</h2>
<p>Utility-First is a CSS philosophy that provides a series of <strong>single-purpose utility classes with stable names</strong>, then builds complex UIs by combining those classes.</p>
<p>For example, instead of writing a <code>.card</code> class and defining its <code>display</code>, <code>padding</code>, and <code>box-shadow</code> in a CSS file,
you write this directly in HTML:</p>
<pre><code>&lt;div class="block p-6 rounded-lg shadow-lg bg-white"&gt;
  
&lt;/div&gt;
</code></pre>
<p>Here, <code>block</code>, <code>p-6</code>, <code>rounded-lg</code>, and the other utility classes each handle only one small task.</p>
<h2>3. Tailwind CSS: The Model of Utility-First</h2>
<p>Tailwind CSS is currently the most popular Utility-First CSS framework. It provides an extremely comprehensive set of utility classes that covers almost every commonly used CSS property.</p>
<p><strong>Core advantages:</strong></p>
<ul>
<li><strong>Extremely fast development</strong>: You barely need to leave the HTML file. By combining atomic classes, you can quickly build almost any design, greatly improving prototyping and development efficiency.</li>
<li><strong>An enforced design system</strong>: Because every style—colors, spacing, and font sizes—comes from a predefined configuration (theme), team members can easily keep the UI consistent and avoid “magic numbers.”</li>
<li><strong>No more naming headaches</strong>: “There are two hard things in computer science: cache invalidation and naming things.” Tailwind largely frees you from naming CSS classes.</li>
<li><strong>Exceptional performance</strong>: During a production build, Tailwind scans your files and removes every unused CSS class through PurgeCSS (or its built-in JIT engine), so the final CSS file is usually very small.</li>
</ul>
<h2>4. “Class-Name Hell”? — A Different Perspective on the Drawbacks</h2>
<p>The most common criticism of Tailwind is that it makes HTML bloated and difficult to read, as if we had returned to the era of “inline styles.”</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>This certainly takes some adjustment. Supporters, however, argue that:</p>
<ol>
<li>Style and structure are already tightly coupled, so placing them together can actually make maintenance easier.</li>
<li>Reusable components, such as React or Vue components, can encapsulate this apparent “mess.”</li>
</ol>
<p>For reusable combinations of styles, Tailwind provides the <code>@apply</code> directive:</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>You can then use <code>.btn-primary</code> in HTML. However, Tailwind's official recommendation is to solve reuse through componentization.</p>
<h2>Conclusion</h2>
<p>The success of Utility-First and Tailwind CSS marks a shift in frontend thinking about “separation of concerns”—from the separation of languages (HTML/CSS/JS) to the separation of components. Through an approach that may appear “primitive,” it solves many practical problems in modern web development and offers developers a shortcut to efficient, consistent, and high-performance UIs.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[The Next Generation of End-to-End Testing: Getting Started with Playwright]]></title>
            <link>https://blog.m1ng.space/en/posts/testing/intro-to-playwright/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/testing/intro-to-playwright/</guid>
            <pubDate>Thu, 20 Jul 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Tired of unstable end-to-end tests? With cross-browser support, automatic waiting, and powerful debugging tools, Microsoft’s Playwright is becoming a new benchmark for test automation.]]></description>
            <content:encoded><![CDATA[<h2>1. The Challenges of End-to-End Testing</h2>
<p>End-to-end (E2E) testing is the final line of defense for application quality. It simulates real user actions—clicking, typing, and navigating—to verify that the application's complete flows work smoothly. Yet E2E tests are also notorious for being “fragile” and “unstable”:</p>
<ul>
<li><strong>Asynchronous behavior</strong>: When a script runs, DOM elements may not have loaded yet, causing the test to fail. Developers have to add many manual <code>wait</code> or <code>sleep</code> calls, making tests unreliable.</li>
<li><strong>Cross-browser compatibility</strong>: Ensuring that tests run reliably in every major browser—Chrome, Firefox, and Safari—is a major challenge.</li>
<li><strong>Difficult debugging</strong>: When a test fails in a CI/CD environment, reproducing and locating the problem is difficult because snapshots of the scene and network logs are missing.</li>
</ul>
<p>A new generation of testing tools emerged to solve these problems, and Playwright is one of the strongest examples.</p>
<h2>2. What Is Playwright?</h2>
<p>Playwright is a Node.js library developed by Microsoft for automating Chromium (Chrome and Edge), Firefox, and WebKit (Safari). It was created by core members of the team that originally developed Google Puppeteer. It can be regarded as Puppeteer's spiritual successor, but with a more modern and powerful design.</p>
<h2>3. Playwright's Core Advantages</h2>
<h4>a. True Cross-Browser Testing</h4>
<p>This is Playwright's most prominent advantage. With one unified API, you can write tests that run on all three major browser engines. This makes it easy to find and fix bugs that appear only in a specific browser, such as Safari.</p>
<h4>b. Automatic Waiting (Auto-Waits)</h4>
<p>Playwright addresses asynchronous behavior at its foundation. Before it performs an action such as <code>page.click()</code>, Playwright automatically runs a series of actionability checks, for example:</p>
<ul>
<li>Waiting for the element to appear in the DOM.</li>
<li>Waiting for the element to become visible.</li>
<li>Waiting for the element to no longer be covered by an animation.</li>
<li>Waiting for the element to receive events.</li>
</ul>
<p>This means you almost never need to write manual waiting code, greatly improving test stability.</p>
<h4>c. Powerful Companion Tools</h4>
<ul>
<li>
<p><strong>Codegen (code generator)</strong>: This is a revolutionary feature. You can run <code>pnpm exec playwright codegen example.com</code>, and Playwright opens a browser window. Every action you perform in the browser is recorded automatically and converted into test code. This dramatically lowers the barrier to writing E2E tests.</p>
</li>
<li>
<p><strong>Trace Viewer</strong>: This is Playwright's “time machine.” When a test fails, you can generate a complete trace file. Open it in Trace Viewer, and you can:</p>
<ul>
<li>Inspect a DOM snapshot for every step of the test.</li>
<li>Inspect the complete network-request log.</li>
<li>Inspect console logs and error messages.</li>
<li>Move back and forth along the timeline to see intuitively how the page changed before and after every action.</li>
</ul>
</li>
</ul>
<p>This makes debugging failures in CI easier than ever before.</p>
<h2>4. A Simple Test Example</h2>
<p>Playwright's API is designed to be very intuitive.</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>Conclusion</h2>
<p>With excellent cross-browser support, reliable automatic waiting, and unmatched debugging tools—especially Trace Viewer—Playwright has set a new standard for E2E testing in modern Web applications. It not only improves test stability and reliability, but also greatly improves the experience of writing tests through tools such as Codegen. If you are looking for a modern, powerful test-automation solution for your project, Playwright should be one of your leading choices.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Signals: The New Wave of Frontend Reactivity]]></title>
            <link>https://blog.m1ng.space/en/posts/frameworks/the-rise-of-signals/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/frameworks/the-rise-of-signals/</guid>
            <pubDate>Sat, 20 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[From SolidJS and Qwik to Preact and Svelte 5, Signals are becoming a core paradigm for high-performance reactive updates in modern frontend frameworks. This article explores how they work and their impact on the ecosystem.]]></description>
            <content:encoded><![CDATA[<h2>1. The Evolution of Reactive Models</h2>
<p>The essence of frontend development is “mapping state to UI.” Over the years, we have witnessed the continuous evolution of reactive models: from manual DOM manipulation, to MVC/MVVM patterns, and then to the Virtual DOM (VDOM) popularized by React. By batching state changes and performing diffing at the component level, the VDOM greatly simplified UI development.</p>
<p>However, the VDOM is not a silver bullet. Its component-level rerendering and diffing overhead can become a performance bottleneck in certain highly dynamic scenarios. In recent years, an older paradigm revitalized by modern compiler optimizations—Fine-Grained Reactivity—has returned to center stage, and its core vehicle is <strong>Signals</strong>.</p>
<h2>2. What Are Signals?</h2>
<p>A Signal is a reactive primitive that wraps a value and automatically notifies all of its dependencies when that value changes. Unlike VDOM frameworks, which track changes at the component level, Signals automatically establish a dependency graph at the point where data is read.</p>
<p>When a Signal's value is updated, only the computations (<code>Memo</code>) or side effects (<code>Effect</code>) that directly or indirectly depend on that Signal are rerun. This enables precise, “surgical” DOM updates and completely bypasses the need for VDOM diffing.</p>
<p>Its core usually consists of three primitives:</p>
<ul>
<li><strong><code>createSignal(value)</code></strong>: Creates a reactive state unit.</li>
<li><strong><code>createEffect(() =&gt; {})</code></strong>: Creates a side effect that automatically tracks dependencies and responds to changes.</li>
<li><strong><code>createMemo(() =&gt; {})</code></strong>: Creates a derived, cached reactive computation.</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>One key mental model is that, in a Signal-based framework such as SolidJS, the component function itself runs only once during initialization. Subsequent updates are driven entirely by the reactive system rather than by rerendering the component.</p>
<h2>3. Broad Adoption Across the Ecosystem</h2>
<p>Signals are not a completely new concept; their ideas can be traced back to early frameworks such as Knockout.js. With the help of modern JavaScript compilers, however, they have gained new vitality and been widely adopted by major frameworks:</p>
<ul>
<li><strong>SolidJS &amp; Qwik</strong>: Built entirely around Signals and considered benchmark implementations of the paradigm.</li>
<li><strong>Preact</strong>: Introduces official support through the <code>@preact/signals</code> package, which can be integrated into existing Preact/React projects.</li>
<li><strong>Vue</strong>: The <code>ref</code> and <code>computed</code> primitives in its Composition API are essentially an implementation of Signals.</li>
<li><strong>Svelte 5</strong>: The upcoming “Runes” update is a redesign of Svelte's reactive model, with Signals as a central source of inspiration and implementation pattern.</li>
<li><strong>Angular</strong>: Recent versions have also introduced Signals as a new reactive primitive.</li>
</ul>
<p>This convergence across frameworks demonstrates the value of Signals as an efficient and predictable reactive model.</p>
<h2>4. Advantages and Trade-offs</h2>
<p><strong>Advantages:</strong></p>
<ol>
<li><strong>Excellent performance</strong>: By avoiding the VDOM and component-level rerendering, Signals usually perform very well in benchmarks.</li>
<li><strong>Predictability</strong>: The flow of state updates is clear and easy to understand and debug.</li>
<li><strong>Low memory usage</strong>: There is no need to maintain a virtual representation of the entire component tree.</li>
</ol>
<p><strong>Trade-offs:</strong></p>
<ol>
<li><strong>A different mental model</strong>: Developers accustomed to React's rendering cycle need to adapt to the new model in which “a component runs only once.”</li>
<li><strong>Ecosystem integration</strong>: Although Signals are powerful on their own, deep integration with the vast React VDOM ecosystem—for example, certain UI libraries—may require additional adaptation work.</li>
</ol>
<h2>Conclusion</h2>
<p>The revival of Signals marks an important evolution in frontend state-management paradigms. It shifts developers' attention away from “how a component renders” and back toward the more fundamental question of “how state flows.” Through deep integration with compilers, Signals deliver an excellent developer experience while opening up new possibilities at the performance boundaries of Web applications.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Say Goodbye to API Documentation: Building End-to-End Type-Safe Applications with tRPC]]></title>
            <link>https://blog.m1ng.space/en/posts/typescript/typesafe-apis-with-trpc/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/typescript/typesafe-apis-with-trpc/</guid>
            <pubDate>Wed, 15 Mar 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[How can frontend and backend data contracts remain synchronized in a full-stack TypeScript project? Without code generation or an intermediate schema, tRPC provides genuine end-to-end type safety.]]></description>
            <content:encoded><![CDATA[<h2>1. The “Contract” Problem in Frontend–Backend Collaboration</h2>
<p>In traditional frontend–backend development, the API is the “contract” between the two sides. We usually maintain this contract in the following ways:</p>
<ul>
<li><strong>RESTful API</strong>: Relying on tools such as OpenAPI (Swagger) to generate and maintain detailed API documentation.</li>
<li><strong>GraphQL</strong>: Relying on a strict Schema Definition Language (SDL) to define data structures and operations.</li>
</ul>
<p>These approaches work, but they share one problem: <strong>the contract and the implementation are separate</strong>. When a frontend developer calls an API, they trust that it will return the data structure described by the documentation or schema. If the backend implementation changes—for example, a field is renamed—but the documentation or schema is not updated in time, the mismatch will surface only at runtime and cause a bug.</p>
<p>Is there a way for the “contract” to stay synchronized with the implementation automatically, or even expose mismatches at compile time?</p>
<h2>2. tRPC's Core Idea: Share Types, Not Schemas</h2>
<p>tRPC (TypeScript Remote Procedure Call) proposes a radical yet extremely simple approach: <strong>if both your frontend and backend use TypeScript, why not share types directly?</strong></p>
<p>tRPC lets you write plain TypeScript functions as backend APIs and call them directly from the frontend with complete type inference and autocompletion, just as if you were calling a function from a local module.</p>
<p>It does not depend on a schema or code generation. The only “contract” is the TypeScript type itself.</p>
<h2>3. How Does It Work?</h2>
<p>tRPC's magic comes from type inference and a little clever encapsulation.</p>
<h4>a. Backend: Define an API Router</h4>
<p>On the backend—usually a Node.js service—you use tRPC functions to create one or more “routers.” Each router is a set of callable “procedures,” which are your API endpoints.</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: Create a Client and Call the API</h4>
<p>On the frontend, you only need to import <code>AppRouter</code> as a <strong>type</strong> from the backend. Notice that you import only a type, not any server-side code.</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>Now you can call the API from a React component as if you were calling a local function, with complete type safety and autocompletion.</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>If the backend developer now renames the field returned by <code>getUser</code> from <code>name</code> to <code>fullName</code>, <code>userQuery.data?.name</code> in the frontend will immediately cause a TypeScript compilation error instead of being discovered at runtime.</p>
<h2>4. Why Choose tRPC?</h2>
<ul>
<li><strong>Absolute end-to-end type safety</strong>: This is its core value. It eliminates an entire class of bugs caused by mismatched API contracts.</li>
<li><strong>Excellent developer experience</strong>: IDE autocompletion means you no longer need to consult documentation or guess the API's structure. Refactoring becomes exceptionally easy and safe.</li>
<li><strong>No code generation</strong>: There is no additional build step, so the feedback loop is extremely fast.</li>
<li><strong>Lightweight and flexible</strong>: tRPC itself is very small and can integrate with any frontend framework and backend service.</li>
</ul>
<h2>Conclusion</h2>
<p>tRPC brings unprecedented fluidity to full-stack TypeScript development. By eliminating the dependency on API documentation and schemas, it makes collaboration between frontend and backend seamless and exceptionally safe. Its advantages become even more pronounced in a monorepo architecture. If you are building a full-stack TypeScript application, tRPC is a revolutionary tool well worth your time.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Exploring Astro: Built for Content-Driven Websites]]></title>
            <link>https://blog.m1ng.space/en/posts/astro/exploring-astro-for-content-sites/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/astro/exploring-astro-for-content-sites/</guid>
            <pubDate>Tue, 10 Jan 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[At a time when single-page applications (SPAs) are everywhere, Astro takes a different path and focuses on peak performance for content websites. What makes its islands architecture so powerful?]]></description>
            <content:encoded><![CDATA[<h2>1. Do We Really Need That Much JavaScript?</h2>
<p>Modern frontend frameworks such as React, Vue, and Svelte have greatly improved the developer experience of building complex web applications. But when we use them to build blogs, portfolios, documentation, or marketing websites, a question follows: do these websites, whose main purpose is presenting content, really need to load the entire page as a single-page application (SPA)?</p>
<p>The answer is usually “no.” For these websites, the user's primary need is to access content quickly. Too much JavaScript instead slows down Time to Interactive (TTI), harming both user experience and SEO.</p>
<p>Astro was created precisely to solve this problem.</p>
<h2>2. Astro's Core Idea: Content First, Zero JS by Default</h2>
<p>Astro is a modern static site generator (SSG) whose core idea is <strong>content first</strong>. It aims to do as much work as possible at build time and send as little JavaScript as possible to the browser.</p>
<p>To achieve this, Astro introduced two major innovations:</p>
<ul>
<li><strong>Zero-JS by Default</strong>: Astro renders all your UI components—whether written in React, Vue, or Svelte—into plain HTML at build time. The browser therefore receives a static page and does not need to load any framework's JS runtime, making it extremely fast to load.</li>
<li><strong>Islands Architecture</strong>: This is the essence of Astro. In an “ocean” of static HTML, any component that needs client-side interaction can be marked as an “island.” Astro independently bundles and loads the JavaScript required by those islands, while the rest of the page remains completely static.</li>
</ul>
<h2>3. How Do “Islands” Work?</h2>
<p>In Astro, you can mark a component as an interactive island by adding a <code>client:*</code> directive.</p>
<p>For example, suppose we have a counter component written in React called <code>Counter.jsx</code>. We can use it in an Astro page like this:</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>The <code>client:load</code> directive tells Astro:</p>
<ol>
<li>Render the initial HTML for the <code>Counter</code> component on the server.</li>
<li>Create a separate JS bundle for the <code>Counter</code> component.</li>
<li>Automatically load this JS bundle when the page loads and “activate” (hydrate) the component so that it becomes interactive.</li>
</ol>
<p>Astro also provides more precise loading directives, such as <code>client:idle</code> (load when the browser is idle) and <code>client:visible</code> (load when the component enters the viewport), taking performance optimization even further.</p>
<h2>4. Framework Agnosticism</h2>
<p>Another major attraction of Astro is its openness to UI frameworks. In the same Astro project, you can use components from different frameworks such as React, Vue, Svelte, SolidJS, and Lit at the same time. This gives teams tremendous flexibility in collaboration and technology choices.</p>
<h2>Conclusion</h2>
<p>Astro is not intended to replace React or Vue; it offers a better solution for specific scenarios. If your project is a content-centered website with extremely high requirements for performance and SEO—this blog itself is built with Astro, for example—then Astro is undoubtedly an excellent choice worth exploring in depth. It perfectly combines the performance advantages of static sites with the developer experience of modern frameworks.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[SvelteKit 1.0 Officially Released: A New Way to Build Web Applications]]></title>
            <link>https://blog.m1ng.space/en/posts/svelte/sveltekit-1-0-release/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/svelte/sveltekit-1-0-release/</guid>
            <pubDate>Thu, 01 Dec 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[After more than two years of development, SvelteKit 1.0 was finally released in December 2022. Hailed as the future of building Web applications at any scale, it offers distinctive core features and a thoughtful design philosophy.]]></description>
            <content:encoded><![CDATA[<h2>SvelteKit 1.0 Is Here!</h2>
<p>In December 2022, the Svelte team officially announced the release of SvelteKit 1.0. It was a milestone for both the Svelte community and the wider frontend field. SvelteKit is no longer merely “Next.js or Nuxt.js for Svelte.” It brings its own distinctive design philosophy and aims to offer a simpler, more flexible, and higher-performance way to build Web applications.</p>
<h2>What Is SvelteKit?</h2>
<p>If you are familiar with Svelte, you probably know that Svelte itself is a <strong>component framework</strong>. Through a radical compiler, it transforms your <code>.svelte</code> files into efficient imperative JavaScript at build time, achieving remarkable performance and extremely low runtime overhead.</p>
<p>SvelteKit, meanwhile, is an <strong>application framework</strong> built on top of Svelte. It handles everything needed to build a complete application—routing, server-side rendering (SSR), data loading, deployment adapters, and more—so that you can focus on developing the business logic.</p>
<h2>Overview of Core Features</h2>
<h4>1. Filesystem-Based Routing</h4>
<p>Like Next.js, SvelteKit automatically generates routes from the structure of your file system. The file structure under <code>src/routes</code> maps directly to the application's URLs.</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>This convention-over-configuration approach greatly simplifies route management.</p>
<h4>2. Flexible Rendering Modes</h4>
<p>SvelteKit lets you precisely control the rendering mode at the page level. Server-side rendering (SSR), static site generation (SSG), or a hybrid of the two can all be implemented in the same application. You can even disable SSR for a single page and turn it into a purely client-side single-page application (SPA).</p>
<h4>3. The Universal <code>load</code> Function</h4>
<p>SvelteKit unifies the data-loading model. Alongside each page or layout, you can create a <code>+page.js</code> or <code>+page.server.js</code> file and export a <code>load</code> function.</p>
<ul>
<li>In <code>+page.js</code>, the <code>load</code> function runs on both the server and the client.</li>
<li>In <code>+page.server.js</code>, the <code>load</code> function runs <strong>only on the server</strong>, so you can safely access a database or private API there.</li>
</ul>
<p>The data returned by the <code>load</code> function is automatically passed to the corresponding <code>+page.svelte</code> component.</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. Adapters</h4>
<p>“Write once, deploy anywhere” is one of SvelteKit's core promises. Through <strong>adapters</strong>, SvelteKit can package your application in the format required by any target platform.</p>
<ul>
<li><code>@sveltejs/adapter-node</code>: For deployment to a traditional Node.js server.</li>
<li><code>@sveltejs/adapter-vercel</code>: Adapts the application for the Vercel platform.</li>
<li><code>@sveltejs/adapter-static</code>: Prerenders the entire application as static files suitable for any static host.</li>
<li>More official and community adapters are available for platforms such as Netlify and Cloudflare Workers.</li>
</ul>
<h2>Conclusion</h2>
<p>The release of SvelteKit 1.0 marked the maturity of the Svelte ecosystem. It combines the exceptional performance of the Svelte compiler with a carefully designed application framework, providing developers with a powerful and enjoyable development experience. If you are looking for a versatile framework that can build anything from simple static pages to complex dynamic applications, SvelteKit is certainly worth trying.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[The Evolution of React State Management: From Props Drilling to Zustand]]></title>
            <link>https://blog.m1ng.space/en/posts/react/state-management-evolution/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/react/state-management-evolution/</guid>
            <pubDate>Tue, 15 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[React state management has evolved from early “props drilling,” through Redux’s unified approach, to today’s flourishing ecosystem of lightweight libraries such as Zustand. This article reviews that fascinating history.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>“How can state be managed elegantly?” This is a core question that every React developer must face as a project grows. From simple state inside a component to complex global state shared across components, the React community has explored many solutions. The evolution of these solutions also reflects our deepening understanding of component-based development.</p>
<h2>Stage One: The Simple Era — <code>useState</code> and Props Drilling</h2>
<p>At the beginning, we only had <code>useState</code> (or <code>this.state</code> in class components). When state needed to be shared by multiple components, React's official recommendation was <strong>“Lifting State Up.”</strong> We moved the state to the nearest common parent of those components, then passed the state and its update function down through props.</p>
<p>When the component hierarchy became deep, this pattern led to <strong>Props Drilling</strong>: some intermediate components received props only to pass them to descendants, without using those props themselves. This increased coupling between components and made refactoring and maintenance more difficult.</p>
<h2>Stage Two: The Official Answer — Context API</h2>
<p>To solve props drilling, React officially introduced the Context API. It lets us create a “context,” provide a value at the top of a component tree, and consume that value directly from a child component at any depth without manually passing it through every level.</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>However, the Context API has a pitfall: <strong>performance</strong>. Whenever a <code>Provider</code>'s <code>value</code> changes, <strong>every</strong> component that consumes that Context rerenders, even if it only cares about a small part of the <code>value</code> object. This makes the Context API unsuitable for complex global state that changes frequently.</p>
<h2>Stage Three: The Era of Unification — Redux</h2>
<p>Before the Context API matured, Redux burst onto the scene and quickly became the de facto standard for state management in large, complex applications. Drawing on the Flux architecture and functional-programming ideas, it introduced:</p>
<ul>
<li><strong>Single Source of Truth</strong>: The state of the entire application is stored in one store.</li>
<li><strong>State is read-only</strong>: The only way to change state is to dispatch an action.</li>
<li><strong>Changes are made with pure functions</strong>: A reducer receives the previous state and an action, then returns the new state.</li>
</ul>
<p>With its predictability, powerful debugging tools (time travel), and rich middleware ecosystem, Redux solved state-management problems in large applications. But its drawback was equally apparent: cumbersome <strong>boilerplate</strong>. To implement a simple feature, developers had to write Actions, Reducers, and Dispatchers, creating a substantial mental burden.</p>
<h2>Stage Four: The Renaissance — Lightweight, Hooks-First Solutions</h2>
<p>As React Hooks became popular and developers reconsidered Redux's complexity, a new generation of lighter state-management libraries emerged in the community. They shared a concise API, a simple mental model, and extensive use of Hooks.</p>
<p><strong>Zustand</strong> is a standout example. It provides an extremely simple <code>create</code> function for building a store, and offers:</p>
<ul>
<li><strong>No Provider required</strong>: The store exists outside the React component tree and can be imported anywhere.</li>
<li><strong>A minimal API</strong>: State can be accessed and updated through a single Hook.</li>
<li><strong>Selective subscriptions and better performance</strong>: A component can subscribe only to the part of state it needs, avoiding the Context API's performance problem.</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>Conclusion: There Is No Silver Bullet—Choose for the Situation</h2>
<p>The history of React state management tells us there is no once-and-for-all “best solution,” only the solution that best fits the current situation.</p>
<ul>
<li><strong><code>useState</code></strong>: Always the first choice for local component state.</li>
<li><strong>Context API</strong>: Suitable for global data that does not change often, such as themes and user authentication information.</li>
<li><strong>Zustand / Jotai</strong>: For most applications that need global client-side state, they strike an excellent balance between performance and developer experience.</li>
<li><strong>Redux (Redux Toolkit)</strong>: Still a reliable choice for very large applications that require strict data-flow conventions, complex middleware, and powerful debugging capabilities.</li>
</ul>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[From Webpack to Vite: A Smooth Migration Journey]]></title>
            <link>https://blog.m1ng.space/en/posts/build-tools/migrating-from-webpack-to-vite/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/build-tools/migrating-from-webpack-to-vite/</guid>
            <pubDate>Mon, 05 Sep 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Does your Webpack project take a minute to start and more than ten seconds to hot-update? It is time to embrace the next generation of frontend build tools. This article records the process and lessons of a migration from Webpack to Vite.]]></description>
            <content:encoded><![CDATA[<h2>1. Why Migrate? Webpack's Pain Points</h2>
<p>Webpack is an extremely powerful and configurable module bundler that has been a cornerstone of frontend projects for years. As projects grow, however, its core bundling model creates performance bottlenecks:</p>
<ul>
<li><strong>Slow cold starts</strong>: Every time the development server (<code>dev-server</code>) starts, Webpack must traverse the entire dependency graph and bundle all modules into memory. In a large project, this process can take several minutes.</li>
<li><strong>Slow hot updates (HMR)</strong>: When you modify a file, Webpack must recalculate and replace the related modules. Although this is faster than refreshing the entire page, the delay can still reach several or even more than ten seconds in a large project, interrupting the development flow.</li>
</ul>
<h2>2. How Does Vite Solve These Problems?</h2>
<p>Vite, whose name means “fast” in French, takes a different path. It uses modern browsers' native ES module (ESM) support and divides the build process into two parts:</p>
<ul>
<li><strong>During development</strong>: Vite starts a server without bundling every module in advance. Instead, it intercepts module requests from the browser and transforms and serves source code on demand. For example, the browser requests <code>main.js</code>, and Vite serves it; <code>main.js</code> imports <code>Button.vue</code>, so the browser sends another request, and Vite serves the transformed <code>Button.vue</code>. This makes development server startup nearly instantaneous.</li>
<li><strong>For production</strong>: Vite uses Rollup, another efficient bundler, to produce highly optimized static assets.</li>
</ul>
<p>This model transforms the development experience, delivering lightning-fast startup and millisecond-level hot updates.</p>
<h2>3. Practical Migration Steps</h2>
<p>Migrating an existing Webpack project to Vite usually involves the following steps:</p>
<h4>a. Install Dependencies and Create the Configuration File</h4>
<p>First, install Vite:</p>
<pre><code>pnpm add -D vite @vitejs/plugin-react # 或 @vitejs/plugin-vue
</code></pre>
<p>Then create <code>vite.config.js</code> in the project root:</p>
<pre><code>import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [react()],
})
</code></pre>
<h4>b. Move <code>index.html</code></h4>
<p>Webpack usually places <code>index.html</code> in the <code>public</code> directory and automatically injects the bundled JS. Vite treats <code>index.html</code> as the application entry point. You need to move it to the project root and add the script reference manually:</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. Replace Webpack Plugins</h4>
<p>You need to find Vite plugins corresponding to your Webpack Loaders and Plugins. The community ecosystem is rich, and solutions exist for most needs.</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> (built-in Babel)</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>Built into Vite</td>
</tr>
<tr>
<td><code>webpack-dev-server</code></td>
<td>Development server built into Vite</td>
</tr>
</tbody>
</table>
<h4>d. Handle Environment Variables</h4>
<p>In Webpack, we are accustomed to using <code>process.env.NODE_ENV</code>. In Vite, environment variables must be accessed through <code>import.meta.env</code>, for example <code>import.meta.env.VITE_API_URL</code>.</p>
<h4>e. Configure Path Aliases</h4>
<p>Configuring path aliases in <code>vite.config.js</code> is straightforward:</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. Results After Migration</h2>
<p>After migrating to Vite, the most immediate impression is <strong>speed</strong>.</p>
<ul>
<li>Development server startup dropped from <strong>50 seconds</strong> to <strong>2 seconds</strong>.</li>
<li>Hot updates fell from an <strong>average of 3–5 seconds</strong> to <strong>almost imperceptible tens of milliseconds</strong>.</li>
<li>The configuration file (<code>vite.config.js</code>) became much simpler than <code>webpack.config.js</code>.</li>
</ul>
<h2>Conclusion</h2>
<p>Although the migration may require handling some project-specific configuration issues, moving from Webpack to Vite brings an enormous improvement to the development experience. If you are still enduring a slow build process, now is the best time to embrace Vite.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Zero-Runtime CSS-in-JS: Combining Developer Experience with Exceptional Performance]]></title>
            <link>https://blog.m1ng.space/en/posts/css/zero-runtime-css-in-js/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/css/zero-runtime-css-in-js/</guid>
            <pubDate>Fri, 05 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[CSS-in-JS offers an excellent developer experience, but its runtime performance cost has long been controversial. Zero-runtime CSS-in-JS extracts styles into static CSS files at build time, delivering the best of both worlds.]]></description>
            <content:encoded><![CDATA[<h2>1. The “Double-Edged Sword” of CSS-in-JS</h2>
<p>CSS-in-JS solutions such as <code>styled-components</code> and <code>Emotion</code> transformed style management in component-based development by allowing developers to write CSS inside JavaScript or TypeScript files. They offer many benefits:</p>
<ul>
<li><strong>Component scope</strong>: Styles are associated with components by default, solving global CSS naming conflicts.</li>
<li><strong>Dynamic styles</strong>: Dynamic styles can easily be created from component props or state.</li>
<li><strong>Code colocation</strong>: Styles and component logic live in the same file, making them easier to maintain and organize.</li>
</ul>
<p>These conveniences, however, come at a cost. The core of traditional CSS-in-JS libraries is the <strong>Runtime</strong>. When a component mounts in the browser, the CSS-in-JS library's JavaScript runtime:</p>
<ol>
<li>Parses CSS in template strings or objects.</li>
<li>Generates unique class names.</li>
<li>Dynamically injects the styles into the document's <code>&lt;head&gt;</code>, inside <code>&lt;style&gt;</code> tags.</li>
</ol>
<p>This process increases the JavaScript bundle size and adds computational overhead during application startup and rendering, affecting performance.</p>
<h2>2. How “Zero Runtime” Breaks the Deadlock</h2>
<p>To solve runtime performance problems, the community proposed a new approach: <strong>Zero-Runtime CSS-in-JS</strong>.</p>
<p>The core idea is to retain the CSS-in-JS developer experience while completing all the work at <strong>build time</strong>. A build tool, usually a Babel or Vite plugin, scans the code, finds every CSS-in-JS usage, and then:</p>
<ol>
<li>Extracts the CSS text.</li>
<li>Generates static <code>.css</code> files.</li>
<li>Replaces the original CSS-in-JS code with generated, unique class names.</li>
</ol>
<p>As a result, the JavaScript bundle delivered to the browser <strong>contains no CSS-in-JS runtime library</strong>, producing an outcome equivalent to highly optimized, hand-written static CSS files.</p>
<h2>3. Leading Zero-Runtime Solutions</h2>
<h4>a. Linaria</h4>
<p>Linaria is one of the pioneers of zero-runtime CSS-in-JS. It lets you use the familiar <code>styled</code> tagged-template-literal syntax.</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>Developed by the team at SEEK, vanilla-extract goes a step further by using TypeScript to create fully type-safe styles. Every style definition is an exported variable in a <code>.css.ts</code> file, providing powerful type inference and autocompletion.</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 is a newer competitor. Inspired by Tailwind CSS, it provides a “Style Props” developer experience while guaranteeing zero-runtime output.</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. Advantages and Trade-Offs</h2>
<p><strong>Advantages</strong>:</p>
<ul>
<li><strong>Exceptional performance</strong>: The final output is static CSS with no JavaScript runtime overhead.</li>
<li><strong>Smaller bundle size</strong>: There is no need to load a CSS-in-JS library on the client.</li>
<li><strong>Powerful developer experience</strong>: You still benefit from component scope, TypeScript type safety, and code colocation.</li>
</ul>
<p><strong>Trade-offs</strong>:</p>
<ul>
<li><strong>Build-time dependency</strong>: The solution must be integrated into the build process, making configuration somewhat more complex.</li>
<li><strong>Limited dynamism</strong>: It cannot generate entirely new styles from values available only at runtime, such as data from an API. However, techniques such as CSS variables can cover most dynamic scenarios.</li>
</ul>
<h2>Conclusion</h2>
<p>Zero-runtime CSS-in-JS is a course correction for the traditional CSS-in-JS paradigm. It cleverly separates the “development-time experience” from “runtime performance,” so we no longer have to make a difficult compromise between them. For modern web applications pursuing exceptional performance and smaller bundle sizes, it offers an almost perfect solution.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[ES2022 Features Overview: Top-Level Await, .at(), and More]]></title>
            <link>https://blog.m1ng.space/en/posts/es/es2022-features-overview/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/es/es2022-features-overview/</guid>
            <pubDate>Fri, 15 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[ECMAScript 2022 (ES2022) has been officially released with a series of practical new features. This article offers a quick look at the most notable updates, including Top-Level await and the .at() method.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>With TC39's annual release process, the JavaScript language continues to evolve every year. ECMAScript 2022 (ES2022) brings a series of new features designed to improve the developer experience and code readability. Let us look at some of the most notable additions.</p>
<h2>1. Top-Level <code>await</code></h2>
<p>This is one of the most anticipated features in ES2022. Previously, the <code>await</code> keyword could be used only inside an <code>async</code> function. That was inconvenient when handling asynchronous operations at the top level of a module, and we usually had to wrap asynchronous code in an IIFE (Immediately Invoked Function Expression).</p>
<p><strong>Before 👎:</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>Now (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> greatly simplifies scenarios such as dynamic module loading and dependency initialization, making asynchronous code more intuitive to write.</p>
<h2>2. The <code>.at()</code> Array/String Indexing Method</h2>
<p>In JavaScript, getting the last element of an array usually requires writing <code>arr[arr.length - 1]</code>, which is both verbose and error-prone. ES2022 introduces the <code>.at()</code> method, providing a consistent way to use forward and reverse indexes.</p>
<p>The <code>.at()</code> method accepts an integer. A positive integer returns the element at that index, while a negative integer counts backward from the end.</p>
<p><strong>Before 👎:</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>Now (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>To check whether an object has an own property rather than an inherited one, we commonly use <code>Object.prototype.hasOwnProperty.call(obj, prop)</code>. This is very cumbersome and can fail in certain cases, such as objects created with <code>Object.create(null)</code>.</p>
<p><code>Object.hasOwn()</code> provides a shorter and more reliable static method.</p>
<p><strong>Before 👎:</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>Now (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. Other Noteworthy Features</h2>
<ul>
<li><strong>Error Cause</strong>: The <code>Error</code> constructor can now receive a second argument that specifies the “cause” of the error, making it easier to build clearer error chains.<pre><code>try {
  // ...
} catch (err) {
  throw new Error('New error message', { cause: err });
}
</code></pre>
</li>
<li><strong>RegExp Match Indices (<code>/d</code> flag)</strong>: When the <code>/d</code> flag is used, regular-expression match results additionally provide the start and end indexes of each capture group.</li>
</ul>
<h2>Conclusion</h2>
<p>Although the new features in ES2022 are not revolutionary, they refine JavaScript in important details, solve many long-standing developer pain points, and make code more concise and robust.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Goodbye Redux Boilerplate? A Detailed Guide to the Lightweight Zustand State Manager]]></title>
            <link>https://blog.m1ng.space/en/posts/react/zustand-vs-redux/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/react/zustand-vs-redux/</guid>
            <pubDate>Sun, 10 Apr 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Does Redux’s complexity put you off? Zustand offers a minimalist, Hooks-based state-management solution for React. This article introduces its core usage and design philosophy.]]></description>
            <content:encoded><![CDATA[<h2>1. The “Trouble” with Redux</h2>
<p>Redux is unquestionably the best-known and most powerful state-management library in the React ecosystem. Features such as unidirectional data flow and time-travel debugging make it a reliable choice for large, complex applications. For many small and medium-sized projects, however, Redux's “boilerplate” is often a headache:</p>
<ul>
<li><strong>Actions &amp; Action Creators</strong>: Defining many action types and creator functions.</li>
<li><strong>Reducers</strong>: Writing large <code>switch</code> statements to handle different actions.</li>
<li><strong>Dispatch &amp; Selectors</strong>: Dispatching actions with <code>dispatch</code> and subscribing to state changes with <code>useSelector</code> inside components.</li>
<li><strong>Context Provider</strong>: Wrapping the application root in a <code>&lt;Provider&gt;</code>.</li>
</ul>
<p>All of this means that adding even a simple piece of state requires changes across several files, making the process cumbersome.</p>
<h2>2. Zustand: A Breath of Fresh Air</h2>
<p>Zustand (German for “state”) is a lightweight state-management library developed by the Poimandres team, the creators of <code>react-three-fiber</code>. Its design philosophy is <strong>minimalism</strong> and <strong>non-intrusiveness</strong>.</p>
<p><strong>Core features:</strong></p>
<ul>
<li><strong>Very little code</strong>: Implementing a feature with Zustand usually requires only a fraction of the code needed with Redux.</li>
<li><strong>Hooks-based</strong>: Everything revolves around a custom Hook, fitting naturally into modern React development.</li>
<li><strong>No Context Provider required</strong>: You do not need to wrap the top level of the application in any Provider. The store is independent of the component tree and can be imported and used anywhere.</li>
<li><strong>Easy to learn</strong>: The API is extremely simple, and its core usage can be learned in minutes.</li>
</ul>
<h2>3. Core Usage</h2>
<p>Using Zustand involves two steps: <strong>creating a Store</strong> and <strong>using it in a component</strong>.</p>
<h4>a. Creating a Store</h4>
<p>You can define a store in any <code>.js</code> or <code>.ts</code> file.</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>The <code>create</code> function receives a callback whose argument is the <code>set</code> function, similar to React's <code>setState</code>. This is where you define your state and the methods that update it.</p>
<h4>b. Using It in a Component</h4>
<p>Use it in any component just like an ordinary Hook.</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>Notice that we subscribe through the selector function <code>(state) =&gt; state.bears</code> to the <code>bears</code> state. This is important because it ensures that the component rerenders only when <code>bears</code> changes, avoiding unnecessary performance costs.</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>Getting an action is just as simple.</p>
<h2>4. Asynchronous Actions</h2>
<p>Zustand also handles asynchronous operations naturally. You do not need middleware such as <code>redux-thunk</code> or <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>Conclusion</h2>
<p>Zustand is not intended to replace Redux completely. Redux still has advantages in strict conventions, traceability, and its vast ecosystem.</p>
<p>For the overwhelming majority of React applications, however, Zustand offers a simpler and faster choice with less mental overhead. If you are tired of Redux's formalities, or your next project needs a nimble and flexible state manager, Zustand is certainly an excellent option worth trying.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Managing Your Monorepo with Turborepo]]></title>
            <link>https://blog.m1ng.space/en/posts/build-tools/intro-to-turborepo/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/build-tools/intro-to-turborepo/</guid>
            <pubDate>Sun, 20 Feb 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[As projects grow more complex, Monorepo has become a popular way to organize code. But how can the resulting build performance problems be solved? Turborepo is a high-performance build system created for exactly this purpose.]]></description>
            <content:encoded><![CDATA[<h2>1. Why Choose a Monorepo?</h2>
<p>A Monorepo, or single code repository, is a strategy that stores multiple independent projects or packages in one repository. Compared with the Polyrepo model, where each project has its own repository, a Monorepo offers several notable advantages:</p>
<ul>
<li><strong>Code sharing</strong>: Sharing components, utility functions, or type definitions between projects becomes very easy.</li>
<li><strong>Atomic commits</strong>: If a feature change touches multiple packages, it can be completed in a single commit, keeping versions consistent.</li>
<li><strong>Simplified dependency management</strong>: All projects share one <code>node_modules</code> directory, or optimize it through pnpm/yarn workspaces, reducing dependency conflicts and inconsistent versions.</li>
</ul>
<p>As the repository grows, however, a Monorepo also brings a challenge: <strong>build performance</strong>.</p>
<h2>2. The Pain Points of a Monorepo</h2>
<p>Imagine that your repository contains <code>docs</code>, <code>webapp</code>, and a shared <code>ui</code> component library. When you only fix a typo in <code>docs</code>, the last thing you want is for every project in the repository, including <code>webapp</code> and <code>ui</code>, to be rebuilt and tested.</p>
<p>Traditional Monorepo management tools such as Lerna have limitations in task orchestration and build caching, resulting in:</p>
<ul>
<li><strong>Repeated work</strong>: Every CI/CD run builds and tests everything from scratch, even when most of the code has not changed.</li>
<li><strong>Long build times</strong>: Tasks cannot make effective use of multiple CPU cores to run in parallel.</li>
<li><strong>Complex scripts</strong>: Complicated <code>package.json</code> scripts are needed to control task execution order manually.</li>
</ul>
<h2>3. Turborepo: Built for Speed</h2>
<p>Turborepo is a high-performance build system designed for JavaScript/TypeScript Monorepos and later acquired by Vercel. It solves the problems above through two core technologies: <strong>incremental builds</strong> and <strong>remote caching</strong>.</p>
<h4>a. Incremental Builds and Task Pipelines</h4>
<p>Turborepo lets you define dependencies between tasks in a root-level <code>turbo.json</code> file.</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>: The <code>^</code> symbol means that a package's <code>build</code> task depends on the <code>build</code> tasks of every package it depends on. Turborepo executes tasks in the most efficient parallel order based on this graph.</li>
<li><strong><code>outputs</code></strong>: Tells Turborepo which output files the task produces.</li>
</ul>
<p>When you run <code>pnpm turbo build</code>, Turborepo calculates which files have changed and rebuilds only the affected packages. If a package's source and dependencies have not changed, Turborepo uses the previous build output directly, which is instantaneous.</p>
<h4>b. Remote Caching</h4>
<p>This is Turborepo's “killer feature.” It not only caches build results on your local machine but can also upload those caches to a shared remote server, such as Vercel or your own S3 bucket.</p>
<p>This means:</p>
<ul>
<li><strong>Your colleague</strong> can pull the latest code and, if you have already built the part they need, download the cache instead of rebuilding it locally.</li>
<li><strong>The CI/CD server</strong> can connect to the same remote cache. Once a PR has been built and cached in CI, other developers and later CI tasks can benefit from it immediately.</li>
</ul>
<p>This can reduce a whole team's build time from tens of minutes to a few minutes or even tens of seconds.</p>
<h2>Conclusion</h2>
<p>Turborepo did not invent the Monorepo, but by introducing advanced caching and task scheduling, it greatly improves the Monorepo development experience and addresses its central performance bottleneck. If you use or plan to adopt a Monorepo architecture, Turborepo is undoubtedly a powerful tool that can save you and your team a great deal of time.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[SolidJS: A Truly Reactive JavaScript Framework]]></title>
            <link>https://blog.m1ng.space/en/posts/frameworks/introduction-to-solidjs/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/frameworks/introduction-to-solidjs/</guid>
            <pubDate>Fri, 05 Nov 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[It looks like React, but it has no virtual DOM. Through its innovative fine-grained reactivity system, SolidJS reaches new heights in performance. Let us take a closer look.]]></description>
            <content:encoded><![CDATA[<h2>1. The “Holy Grail” of Frontend Frameworks: Performance and Experience</h2>
<p>Since React popularized the Virtual DOM, the VDOM has seemed to become standard equipment for modern frontend frameworks. By maintaining a virtual representation of the UI in memory and using a diff algorithm to calculate the minimum DOM updates, it improves both the developer experience and performance in most scenarios.</p>
<p>But the VDOM is not free. It consumes memory, and the diffing process also has a computational cost. This raises a question: can we bypass the VDOM and apply state changes precisely to the DOM while retaining a declarative, React-like development experience?</p>
<p>SolidJS provides its answer.</p>
<h2>2. The Core of SolidJS: Fine-Grained Reactivity</h2>
<p>SolidJS is a declarative, reactive JavaScript framework. Although it uses JSX and looks very similar to React, its underlying principles are completely different.</p>
<p>The SolidJS compiler transforms JSX code into optimal native DOM operations. Instead of using a VDOM, it builds a dependency graph composed of reactive “Signals.” When the value of a signal changes, only the “Effects” or computations (Memos) subscribed to that signal run again.</p>
<p><strong>A disruptive mental model: components run only once!</strong></p>
<p>In React, the entire component function runs again when state or props change. In SolidJS, however, your component function <strong>runs from beginning to end only once</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>When you click the button, only the reader of the <code>count()</code> signal and the DOM text node that depends on it are updated. The <code>Counter</code> function itself does not run again. This “surgical” update is the source of SolidJS's exceptional performance.</p>
<h2>3. Core APIs</h2>
<p>The SolidJS reactivity system is mainly composed of three core primitives:</p>
<ul>
<li><strong><code>createSignal(initialValue)</code></strong>: Creates a reactive signal and returns a getter and a setter: <code>[count, setCount]</code>.</li>
<li><strong><code>createEffect(() =&gt; {})</code></strong>: Creates an “effect” that automatically tracks every signal read inside it and runs again when any of those signals changes. It is ideal for side effects such as manually manipulating the DOM.</li>
<li><strong><code>createMemo(() =&gt; {})</code></strong>: Creates a cached, derived computed value. It recalculates only when one of its internal signal dependencies changes.</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. Why Choose SolidJS?</h2>
<ul>
<li><strong>Exceptional performance</strong>: SolidJS often ranks near the top in independent benchmarks, with performance very close to native JavaScript code.</li>
<li><strong>Extremely small bundle size</strong>: Because there is no VDOM runtime, its core package is very small.</li>
<li><strong>Familiar developer experience</strong>: If you are familiar with React Hooks, you can get started with SolidJS quickly. The combination of JSX and reactive primitives is both powerful and intuitive.</li>
<li><strong>True reactivity</strong>: Its model is closer to MobX or Vue's Composition API, but the compiler makes it possible without runtime overhead.</li>
</ul>
<h2>Conclusion</h2>
<p>SolidJS represents an important direction in the evolution of frontend frameworks: using the compiler to do more work at build time in exchange for less runtime code and higher performance. It proves that we can escape the constraints of the Virtual DOM without sacrificing a declarative development experience. For applications that pursue exceptional performance, SolidJS offers a highly attractive choice.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
        <item>
            <title><![CDATA[Do You Really Know How to Use Fetch? Canceling Requests with AbortController]]></title>
            <link>https://blog.m1ng.space/en/posts/javascript/fetch-with-abortcontroller/</link>
            <guid isPermaLink="false">https://blog.m1ng.space/en/posts/javascript/fetch-with-abortcontroller/</guid>
            <pubDate>Sat, 25 Sep 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[In modern Web development, the Fetch API has become the standard way to make HTTP requests. But do you know how to gracefully cancel a request that is no longer needed? AbortController is the answer.]]></description>
            <content:encoded><![CDATA[<h2>1. Fetch API: The Foundation of Modern Web Requests</h2>
<p>The <code>Fetch API</code> has replaced the aging <code>XMLHttpRequest</code> as the standard way to make HTTP requests in the browser. Built on Promises, it offers a cleaner and more powerful request API.</p>
<p>However, the <code>Fetch API</code> does not provide a direct request-cancellation mechanism by default. Cancellation is very important in many situations:</p>
<ul>
<li><strong>Performance optimization</strong>: When users quickly switch pages or enter search terms, canceling old requests that are no longer needed saves bandwidth and server resources.</li>
<li><strong>Avoiding race conditions</strong>: In a search-as-you-type scenario, an older request may return more slowly than a newer one, causing the UI to display stale data.</li>
<li><strong>Preventing memory leaks</strong>: If a component is unmounted before a request started during its lifecycle completes, an attempt to update a component that no longer exists may cause errors or memory leaks.</li>
</ul>
<h2>2. <code>AbortController</code>: A General-Purpose Cancellation Signal</h2>
<p><code>AbortController</code> is a general-purpose Web API that provides a mechanism for aborting one or more Web requests. It is independent of the <code>Fetch API</code>, but works perfectly with <code>Fetch</code>.</p>
<p>The core idea of <code>AbortController</code> is:</p>
<ol>
<li>Create an <code>AbortController</code> instance.</li>
<li>Obtain an <code>AbortSignal</code> object from that instance.</li>
<li>Pass the <code>AbortSignal</code> to an abortable Web API such as <code>fetch</code>.</li>
<li>When cancellation is needed, call the <code>AbortController</code> instance's <code>abort()</code> method.</li>
</ol>
<h2>3. Combining <code>AbortController</code> with <code>Fetch</code></h2>
<p>Let's look at an example of using <code>AbortController</code> to cancel a <code>Fetch</code> request.</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>When <code>controller.abort()</code> is called, the <code>fetch</code> request is immediately aborted and throws an <code>AbortError</code>. You need to catch this error in the <code>catch</code> block and check <code>error.name === 'AbortError'</code> to distinguish cancellation from other network errors.</p>
<h2>4. Practical Use Cases</h2>
<h4>a. Search as You Type</h4>
<p>When a user types quickly in a search box, every input event may trigger a new request. We can cancel the previous unfinished request and keep only the latest one.</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. Canceling a Request When a Component Unmounts</h4>
<p>In frameworks such as React or Vue, canceling unfinished requests started inside a component when it unmounts can effectively prevent memory leaks and unnecessary UI updates.</p>
<pre><code>// React 示例
useEffect(() =&gt; {
  const controller = new AbortController();
  fetchDataWithCancellation('/api/data', controller.signal);

  return () =&gt; {
    controller.abort(); // 组件卸载时取消请求
  };
}, []);
</code></pre>
<h2>Conclusion</h2>
<p><code>AbortController</code> is an indispensable tool in modern Web development. It provides a native cancellation mechanism for the <code>Fetch API</code> and other asynchronous operations, helping developers build applications that are more robust, efficient, and pleasant to use. Mastering <code>AbortController</code> is one of the essential skills of an excellent frontend developer.</p>
]]></content:encoded>
            <author>m1ngsama</author>
        </item>
    </channel>
</rss>