﻿<?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Xheldon Blog</title>
        <description>The Answer to Life, the Universe and Everything is...</description>
        <link>https://www.xheldon.com/en/</link>
        <atom:link href="https://www.xheldon.com/en/feed.xml" rel="self" type="application/rss+xml" />
        <pubDate>Wed, 03 Jun 2026 01:26:21 +0000</pubDate>
        <lastBuildDate>Wed, 03 Jun 2026 01:26:21 +0000</lastBuildDate>
        <generator>Hexo v7.3.0</generator>
        
        <item>
            <title>How to Use the Same Shortcuts Across Dual Operating Systems</title>
            <description>&lt;h2 id=&#34;preface&#34;&gt;Preface&lt;/h2&gt;
&lt;p&gt;My last experience with Windows dates back to ten years ago when I was in school, using a Lenovo Y470-P. This laptop had a design flaw—once when I closed the lid, a component somehow burned out, and I had to replace the motherboard in the end.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-809c-9b03-ca17b71cfe98.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;  
&lt;p&gt;After graduation, I immediately bought a 15-inch MacBook Pro (2015 model). Over the years, though I’ve upgraded my laptop, I’ve stayed within the Apple ecosystem. As a result, my impression of Windows remained stuck in the past: a fragmented ecosystem, clunky software, ugly design (this hasn’t changed), and bizarre interactions.&lt;/p&gt;
&lt;p&gt;During the recent 618 sales, I built a mid-to-high-end PC with a 14700KF, 5070Ti, 64GB of Crucial 6400MHz RAM, and a 2TB Crucial SSD. I’m absolutely loving it—turns out a high-spec Windows machine can be this smooth! Aside from the still-questionable system design language, other aspects like software launch speed and the design language of well-made apps (e.g., the Windows version of Apple Music) are quite impressive. Of course, the main reasons I bought a Windows PC were gaming (80%) and writing platform-specific C# code (20%).&lt;/p&gt;
&lt;h2 id=&#34;the-essence-of-efficient-input&#34;&gt;The Essence of Efficient Input&lt;/h2&gt;
&lt;p&gt;Having used an HHKB on a Mac for a long time (9 years), I prefer mapping Mac’s HHKB keybindings to Windows. Beyond habit, here’s why:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Common text-editing functions like moving the cursor to the start/end of a line or deleting forward require dedicated keys on Windows—Home, End, Del, etc. These can’t be achieved via modifier + key combinations. On macOS, however, Emacs shortcuts are system-wide: Control + A for line start, Control + E for line end, Control + D for forward delete—all effortless.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Similarly, cursor movement: Windows relies on dedicated arrow keys, while macOS allows Control + B/F/N/P (back/forward/next/previous).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You might wonder: Why do I prefer modifier + key combos over dedicated keys?&lt;/p&gt;
&lt;p&gt;The answer is also why I love the HHKB’s 60% layout: &lt;strong&gt;My palms can perform all operations without significant movement, ensuring input efficiency.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; In the programmer world, using a mouse when you could use the keyboard is a punishable offense! &lt;/p&gt;&lt;/blockquote&gt;  
&lt;p&gt;For example, imagine typing rapidly on Windows when you spot a typo a few words back. Here’s the process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Right hand leaves the home row, fingers fumbling to the arrow keys (unless you’re on an 87+ key layout, where arrows are easier to find).&lt;/li&gt;
&lt;li&gt;Press arrow keys to move the cursor.&lt;/li&gt;
&lt;li&gt;Right hand leaves arrows, moves to Backspace.&lt;/li&gt;
&lt;li&gt;Press Backspace.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On an HHKB with Mac keybindings, the same operation is done with just the left hand:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Left pinky presses Control + B to move the cursor.&lt;/li&gt;
&lt;li&gt;Right pinky hits Backspace, or left pinky uses Control + H/D (delete left/right).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s it—&lt;strong&gt;your hands never leave the home row.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Even after editing, Windows requires your right hand to leave the home row again for End, while macOS just needs Control + E.&lt;/p&gt;
&lt;h2 id=&#34;the-struggle-with-backtick-input&#34;&gt;The Struggle with Backtick &lt;code&gt;`&lt;/code&gt; Input&lt;/h2&gt;
&lt;p&gt;Another reason I love the HHKB: As a frontend developer, I frequently use backticks (the &lt;code&gt;~&lt;/code&gt; key). On most 60% keyboards, this key is mapped to Esc via a combo. But the HHKB places it as a dedicated key in the top-right, relocating Delete downward and &lt;code&gt;｜&lt;/code&gt; shifting a key upward (splitting the standard Backspace into two 1u keys).&lt;/p&gt;
&lt;p caption=&#39;普通60配列&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-8098-b185-c92a8eaf2c94.webp&#39; alt=&#39;普通60配列&#39; title=&#39;普通60配列&#39;&gt;&lt;/p&gt;  
&lt;p caption=&#39;HHKB 配列&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-804c-bc1a-e7a740ecc5e9.webp&#39; alt=&#39;HHKB 配列&#39; title=&#39;HHKB 配列&#39;&gt;&lt;/p&gt;  
&lt;h2 id=&#34;the-awkward-control-key&#34;&gt;The Awkward Control Key&lt;/h2&gt;
&lt;p&gt;Another efficiency-killer in standard layouts: Control is at the bottom-left. Pressing Control + anything forces your wrist to bend left and your palm to shift—any input method requiring palm movement is inelegant and slows you down.&lt;/p&gt;
&lt;p caption=&#39;普通键盘位置尴尬的 Ctrl&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-809a-af2b-fab48765e5cb.webp&#39; alt=&#39;普通键盘位置尴尬的 Ctrl&#39; title=&#39;普通键盘位置尴尬的 Ctrl&#39;&gt;&lt;/p&gt;  
&lt;p&gt;Some online even suggest pressing the bottom-left Ctrl with the part of the palm below the pinky—absurd:&lt;/p&gt;
&lt;p caption=&#39;左侧手掌按 Ctrl 法&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-80c4-8ba9-ffdccbbec543.webp&#39; alt=&#39;左侧手掌按 Ctrl 法&#39; title=&#39;左侧手掌按 Ctrl 法&#39;&gt;&lt;/p&gt;  
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; I’ll go further: Any language requiring jaw movement to articulate sounds is inelegant. An elegant language should rely solely on the tongue and throat for clear pronunciation, minimizing mouth movement. Thus, I consider Japanese the most elegant language—though this also limits its expressive power, especially for strong emotions like swearing. &lt;/p&gt;&lt;/blockquote&gt;  
&lt;h2 id=&#34;the-useless-caps-lock-key&#34;&gt;The Useless Caps Lock Key&lt;/h2&gt;
&lt;p&gt;I believe very few people frequently type in uppercase letters for extended periods, so placing the Caps Lock key—a key of such little importance—on the left pinky finger, a crucial position on a standard keyboard layout, is utterly absurd.&lt;/p&gt;
&lt;p caption=&#39;用处不多的 CapLock 占据了一个黄金位置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-8088-9b22-efdde222997e.webp&#39; alt=&#39;用处不多的 CapLock 占据了一个黄金位置&#39; title=&#39;用处不多的 CapLock 占据了一个黄金位置&#39;&gt;&lt;/p&gt;
&lt;p&gt;In summary, I consider the HHKB layout to be the most perfect, not just for its symmetrical beauty but also for its efficiency in typing. As for keyboard shortcuts on macOS, at least in terms of text editing efficiency, they are superior to those on Windows.&lt;/p&gt;
&lt;p&gt;Therefore, I’ll now share &lt;strong&gt;how to replicate macOS keybindings on Windows&lt;/strong&gt;, allowing you to use the same muscle memory across both operating systems for efficient editing.&lt;/p&gt;
&lt;p caption=&#39;HHKB 配列 Control 在 CapLock 位置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80e6-8b9e-cda2586acf71.webp&#39; alt=&#39;HHKB 配列 Control 在 CapLock 位置&#39; title=&#39;HHKB 配列 Control 在 CapLock 位置&#39;&gt;&lt;/p&gt;
&lt;p&gt;As for the most criticized aspect of the HHKB—the arrow keys—in almost every scenario where you’d use arrow keys to move the cursor, you can instead use Emacs shortcuts. The exceptions are… certain terminals, like Python, where Control-D exits the CLI, or when remotely accessing a Linux host via SSH. This can be a bit awkward, but using a good terminal can mitigate some of the hassle. Consider Warp, for example, which allows you to input commands in a text-editor-like manner, meaning you can use Emacs shortcuts to edit commands. If this sounds unclear, just try it—you’ll immediately notice the difference from a standard terminal.&lt;/p&gt;
&lt;h2 id=&#34;keyboard-requirements&#34;&gt;Keyboard Requirements&lt;/h2&gt;
&lt;p&gt;You’ll need a keyboard that supports multiple layers of key remapping, meaning it can handle combinations like FN + another key. Macro support is optional. Some keyboards support up to 6 layers of mapping (accounting for dual-system switching with 3 layers per system). I’m using my newly purchased &lt;strong&gt;Nuphy Air75 V3&lt;/strong&gt; keyboard. The reason I chose this over the HHKB is that I also game on Windows, making the F-row (F1–F12) indispensable. Thus, a 75% layout is the smallest key count I can accept.&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; &#34;Who missed the cannon minion? I’m sending a question mark.&#34; — Faker&lt;/p&gt;&lt;/blockquote&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-8072-a06b-f6c9ead05059.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;implementing-emacs-text-editing-via-fn-layer&#34;&gt;Implementing Emacs Text Editing via FN Layer&lt;/h2&gt;
&lt;p&gt;Here are the text-editing functions to replicate (formatted as Windows key → macOS shortcut):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Move cursor to line start: Home → Control + A&lt;/li&gt;
&lt;li&gt;Move cursor to line end: End → Control + E&lt;/li&gt;
&lt;li&gt;Move cursor down: ↓ → Control + N (Next)&lt;/li&gt;
&lt;li&gt;Move cursor up: ↑ → Control + P (Previous)&lt;/li&gt;
&lt;li&gt;Move cursor left: ← → Control + B (Back)&lt;/li&gt;
&lt;li&gt;Move cursor right: → → Control + F (Front)&lt;/li&gt;
&lt;li&gt;Delete right: Del → Control + D (Delete)&lt;/li&gt;
&lt;li&gt;Delete left: Backspace → Control + H (The “H” stands for delete, though there’s no corresponding word due to historical reasons—feel free to look it up if interested).&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Personally, I prefer using Control + C/V for copy/paste on macOS rather than Command + C/V (though I haven’t remapped this). Perhaps due to historical reasons, Emacs doesn’t support copy/paste, and since these aren’t text-editing-specific shortcuts, macOS assigned them to the Command key. &lt;/p&gt;&lt;/blockquote&gt;  
&lt;p&gt;To replicate HHKB’s macOS keybindings on Windows, &lt;strong&gt;you’ll need to remap Caps Lock to an FN layer&lt;/strong&gt; (I set mine to FN6) and then map each key individually:&lt;/p&gt;
&lt;p caption=&#39;修改 CapLock 为 FN6 层&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80d2-bbf1-e143863958db.webp&#39; alt=&#39;修改 CapLock 为 FN6 层&#39; title=&#39;修改 CapLock 为 FN6 层&#39;&gt;&lt;/p&gt;  
&lt;p&gt;Then, in the FN6 layer, assign each key to its corresponding Windows function:&lt;/p&gt;
&lt;p caption=&#39;FN6 层映射&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-809e-897f-e5af7ec726c4.webp&#39; alt=&#39;FN6 层映射&#39; title=&#39;FN6 层映射&#39;&gt;&lt;/p&gt;  
&lt;h2 id=&#34;modifying-the-alpha-key-area&#34;&gt;Modifying the Alpha Key Area&lt;/h2&gt;
&lt;p&gt;On the HHKB, the key above Enter is Delete &lt;code&gt;Backspace&lt;/code&gt;, not the &lt;code&gt;｜&lt;/code&gt; key. Initially, I swapped Backspace and the | key, but for some inexplicable reason, my hands adapted to the 75% layout, causing me to consistently hit the | key instead of Backspace. Switching back to the HHKB forced me to relearn the lower row, which was frustrating. So, I simply remapped both keys to Backspace and relegated the rarely used | key to the F13 position in the F-row.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-806c-acc9-fde5c028f922.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;  
&lt;p&gt;Next, I swapped the Control and Alt keys. This ensures that macOS shortcuts like Command + C/V/T/W align with their Windows counterparts (Control + C/V/T/W), maintaining consistent muscle memory.&lt;/p&gt;
&lt;p&gt;Finally, I swapped the Windows key and Alt (placing the Windows key in the bottom-left corner) to match the position of Alt (Opt) on macOS.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80ae-98c6-ceb3f49fd02f.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;  
&lt;p&gt;Now, pressing the key to the left of the spacebar (Control on Windows, Command on macOS) and combining it with other keys produces identical results: Control/Command + C for copy, Control/Command + V for paste, Control/Command + T to open a new tab, Control/Command + Shift + T to reopen a closed tab, etc.&lt;/p&gt;
&lt;h2 id=&#34;other-key-modifications&#34;&gt;Other Key Modifications&lt;/h2&gt;
&lt;p&gt;Since the Home and End shortcuts already exist, the Home and End keys on the far right of the keyboard are unnecessary. Therefore, I made two modifications: I remapped the Home key to a frequently used shortcut—I set it to Win + V, which opens the clipboard history on Windows. On Mac, I mapped it to Cmd + Opt + C, which opens Alfred’s clipboard history window.&lt;/p&gt;
&lt;p&gt;The Home key is mapped to an M1 macro to open the copy history, while the End key is set to WinLock to prevent accidental presses of the Win key in the bottom-left corner due to muscle memory. For now, it’s locked during gaming, and I’ll unlock it once I’ve adjusted.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-8011-957a-e63f205dd9e1.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;Win + V 打开复制历史&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80e7-9c7b-fa95908c6985.webp&#39; alt=&#39;Win + V 打开复制历史&#39; title=&#39;Win + V 打开复制历史&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;Cmd + Opt + C 打开复制历史（Alfred）&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80a0-a366-ecc5f5024d0a.webp&#39; alt=&#39;Cmd + Opt + C 打开复制历史（Alfred）&#39; title=&#39;Cmd + Opt + C 打开复制历史（Alfred）&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;a-minor-drawback&#34;&gt;A Minor Drawback&lt;/h2&gt;
&lt;p&gt;On Windows, Alt + Tab cannot be remapped, and this shortcut is crucial for switching between application windows. With the above setup, the key combination becomes the second key to the left of the spacebar + Tab.&lt;/p&gt;
&lt;p&gt;However, the same functionality on Mac—switching between applications—uses Cmd + Tab.&lt;/p&gt;
&lt;p&gt;As a result, aside from the application-switching shortcut (which is system-level and unmappable in both systems), all other keybindings in my setup can be executed with the same muscle memory, eliminating the need to distinguish between Windows and Mac shortcut positions.&lt;/p&gt;
&lt;h2 id=&#34;afterword&#34;&gt;Afterword&lt;/h2&gt;
&lt;p&gt;What works best for you is what matters most. There’s no need to debate which keybinding method is superior—habit is a powerful force. Once you’re accustomed to it, efficiency naturally follows.&lt;/p&gt;
&lt;p&gt;Happy Hacking to all!&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-8028-96b3-f2d7bb973946.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;</description>
            <pubDate>Thu, 07 Aug 2025 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/life/input-more-efficiently.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/life/input-more-efficiently.html</guid>
            
                <category>生活</category>
            
                <category>Apple</category>
            
                <category>经验</category>
            
                <category>总结</category>
            
                <category>VSCode</category>
            
                <category>工具</category>
            
                <category>踩坑</category>
            
                <category>细节</category>
            
                <category>Windows</category>
            
            
                <category>life</category>
            
        </item>
        
        <item>
            <title>App Development Journey (Part 1): The Marvelous Mystery of Swift</title>
            <description>&lt;h2 id=&#34;explanation&#34;&gt;Explanation&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Someone asked why not JavaScript—because it&#39;s a scripting language without a type system, making it completely incomparable to Swift. &lt;/p&gt;&lt;/blockquote&gt;  
&lt;p&gt;As a front-end developer, I have only a beginner-level understanding of C, Java, and Python, but I’ve reached an advanced level (self-assessed) in TypeScript (hereafter referred to as TS) in terms of features and usage. Therefore, some things that shocked me might seem like “just ordinary language features” to others, or even prompt questions like, “Are the designers of TypeScript geniuses?”&lt;/p&gt;
&lt;p&gt;I won’t necessarily feign shock at every difference between Swift and TS, because some differences stem from TS’s own limitations, while others are commonplace in programming languages (e.g., distinguishing between &lt;code&gt;Int&lt;/code&gt; and &lt;code&gt;Double&lt;/code&gt; instead of having just one &lt;code&gt;Number&lt;/code&gt; type). It’s JavaScript’s design that’s truly shocking (after all, it was hastily designed in a week), not Swift’s.&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; My belief is: If a language has too many rules, too many exceptions, or too many reserved words, it’s not a good language. &lt;/p&gt;&lt;/blockquote&gt;  
&lt;h2 id=&#34;preface&#34;&gt;Preface&lt;/h2&gt;
&lt;p&gt;This article follows the order of Swift’s official language introduction, focusing only on the parts that shocked me while omitting those that didn’t or that I didn’t understand, such as &lt;code&gt;附加宏&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;basics&#34;&gt;Basics&lt;/h2&gt;
&lt;h3 id=&#34;comments&#34;&gt;Comments&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking discovery: Xcode doesn’t have a `/** */` block comment shortcut—only `cmd + /` for line comments. &lt;/p&gt;&lt;/blockquote&gt;  
&lt;p&gt;VSCode’s block comment shortcut is also awkward to press: &lt;code&gt;Alt + Shift + A&lt;/code&gt;. Do people not use this feature often?&lt;/p&gt;
&lt;p&gt;Of course, in both VSCode and Xcode, you can automatically generate block comments by pressing &lt;code&gt;/**&lt;/code&gt; and then Enter.&lt;/p&gt;
&lt;h3 id=&#34;optional-binding&#34;&gt;Optional Binding&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking discovery: While debugging, I wanted to write a value that’s always `true` in `if` for testing, but it’s not allowed—the optional binding in `if` statements must be an optional value. &lt;/p&gt;&lt;/blockquote&gt;  
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &amp;#123; &amp;#125; &lt;span class=&#34;hljs-comment&#34;&gt;// 错误！&lt;/span&gt;&lt;br&gt;```  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-type&#34;&gt;Is&lt;/span&gt; this really necessary&lt;span class=&#34;hljs-operator&#34;&gt;?&lt;/span&gt;  &lt;br&gt;&lt;br&gt;## &lt;span class=&#34;hljs-type&#34;&gt;Collection&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Types&lt;/span&gt;&lt;br&gt;&lt;br&gt;&amp;#123;&lt;span class=&#34;hljs-operator&#34;&gt;%&lt;/span&gt; render_quote color&lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class=&#34;hljs-operator&#34;&gt;%&lt;/span&gt;&amp;#125; &lt;span class=&#34;hljs-type&#34;&gt;Shocking&lt;/span&gt; point: &lt;span class=&#34;hljs-type&#34;&gt;Among&lt;/span&gt; array methods, there&amp;#x27;s `sort` &lt;span class=&#34;hljs-keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;hljs-operator&#34;&gt;-&lt;/span&gt;place sorting, and another `sorted` that returns a new array. &amp;#123;&lt;span class=&#34;hljs-operator&#34;&gt;%&lt;/span&gt; endrender_quote &lt;span class=&#34;hljs-operator&#34;&gt;%&lt;/span&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt; language doing what frameworks typically &lt;span class=&#34;hljs-keyword&#34;&gt;do&lt;/span&gt;—very fitting &lt;span class=&#34;hljs-keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Apple&lt;/span&gt;&amp;#x27;s style. &lt;span class=&#34;hljs-type&#34;&gt;There&lt;/span&gt; are many such examples, but &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt;&amp;#x27;s not dwell on &lt;span class=&#34;hljs-keyword&#34;&gt;each&lt;/span&gt; one.&lt;br&gt;&lt;br&gt;## &lt;span class=&#34;hljs-type&#34;&gt;Control&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Flow&lt;/span&gt;&lt;br&gt;&lt;br&gt;### &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Expressions&lt;/span&gt;&lt;br&gt;&lt;br&gt;&amp;#123;&lt;span class=&#34;hljs-operator&#34;&gt;%&lt;/span&gt; render_quote color&lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class=&#34;hljs-operator&#34;&gt;%&lt;/span&gt;&amp;#125; &lt;span class=&#34;hljs-type&#34;&gt;Shocking&lt;/span&gt; point: &lt;span class=&#34;hljs-type&#34;&gt;To&lt;/span&gt; address the issue of `&lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;hljs-operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt;` assigning values to the same variable, it provides the `if` expression syntax. &lt;span class=&#34;hljs-type&#34;&gt;Similarly&lt;/span&gt;, `switch` also has an analogous expression form. &amp;#123;&lt;span class=&#34;hljs-operator&#34;&gt;%&lt;/span&gt; endrender_quote &lt;span class=&#34;hljs-operator&#34;&gt;%&lt;/span&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-type&#34;&gt;Swift&lt;/span&gt; really goes above and beyond:&lt;br&gt;&lt;br&gt;```swift&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 平平无奇的写法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; str: &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt; &amp;#123;&lt;br&gt;    str &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;小&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;30&lt;/span&gt; &amp;#123;&lt;br&gt;    str &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;大&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &amp;#123;&lt;br&gt;    str &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;中&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 简写法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; str &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;小&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;30&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;大&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;中&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;switch’s-powerful-matching&#34;&gt;Switch’s Powerful Matching&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: switch has no implicit fallthrough. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This is understandable and more reasonable—who in their right mind would want case 1 to automatically spill into case 2 without explicitly using &lt;code&gt;break&lt;/code&gt;?&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: switch can match object values! &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This is actually considered &lt;code&gt;feature&lt;/code&gt;, and intuitively more reasonable—truly an expert’s approach.&lt;/p&gt;
&lt;p&gt;In TypeScript, since the &lt;code&gt;switch&lt;/code&gt; &lt;code&gt;case&lt;/code&gt; statement uses strict equality (===) for comparison, you generally wouldn’t pass an object inside your &lt;code&gt;switch&lt;/code&gt; parentheses. This is because objects are compared by reference, and in TypeScript, there are rarely situations where you need to check object equality, let alone using a &lt;code&gt;switch&lt;/code&gt; statement for such checks.&lt;/p&gt;
&lt;p&gt;However, in Swift, you can “capture” the matched value (officially termed a “pattern”) within a &lt;code&gt;case&lt;/code&gt; statement:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; somePoint &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; (&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;) &lt;span class=&#34;hljs-comment&#34;&gt;// 一个平平无奇的元组罢了&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 我现在，开 始 判 断：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;switch&lt;/span&gt; somePoint &amp;#123;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;):&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在原点&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;): &lt;span class=&#34;hljs-comment&#34;&gt;// 忽略第一个值&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在 x 轴&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt;): &lt;span class=&#34;hljs-comment&#34;&gt;// 忽略第二个值&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在 y 轴&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;hljs-operator&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;hljs-operator&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;): &lt;span class=&#34;hljs-comment&#34;&gt;// 判断元组的两个值是否分别在给定区间，在就匹配成功&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在盒子内部&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;default&lt;/span&gt;:&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在盒子外部&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 还可以值绑定！&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;switch&lt;/span&gt; somePoint &amp;#123;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; x, &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;): &lt;span class=&#34;hljs-comment&#34;&gt;// 匹配第二个值，捕获第一个值&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;x轴值：&lt;span class=&#34;hljs-subst&#34;&gt;\(x)&lt;/span&gt;&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; y): &lt;span class=&#34;hljs-comment&#34;&gt;// 匹配第一个值，捕获第二个值&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;y 轴值：&lt;span class=&#34;hljs-subst&#34;&gt;\(y)&lt;/span&gt;&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; x, &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; y): &lt;span class=&#34;hljs-comment&#34;&gt;// 兜底，因为 x 和 y 都是 let，还可以写作 case let (x, y)&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(x)&lt;/span&gt; 和 &lt;span class=&#34;hljs-subst&#34;&gt;\(y)&lt;/span&gt;&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 还可以加 where 限定：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; (x, y) &lt;span class=&#34;hljs-keyword&#34;&gt;where&lt;/span&gt; x &lt;span class=&#34;hljs-operator&#34;&gt;==&lt;/span&gt; y:&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;//还可以多个匹配&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;b&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;c&amp;quot;&lt;/span&gt;:&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;It’s worth noting that TypeScript also supports “multiple matches,” but this is entirely different from Swift’s case-based multiple matching.&lt;/p&gt;
&lt;p&gt;In Swift, for multiple matches separated by commas, the case statement executes as long as any one of the comma-separated items matches. In contrast, TypeScript’s comma-separated matching essentially only matches the last item, because comma-separated expressions in TypeScript only return the final value:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs typescript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;d&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;switch&lt;/span&gt; (a) &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;d&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;c&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;b&amp;quot;&lt;/span&gt;:&lt;br&gt;  &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;)&lt;span class=&#34;hljs-comment&#34;&gt;// 此处不会执行，因为此 case 匹配 &amp;quot;b&amp;quot; &lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&#34;functions&#34;&gt;Functions&lt;/h2&gt;
&lt;h3 id=&#34;labeled-parameters-for-functions&#34;&gt;Labeled Parameters for Functions&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: Formal parameters (labeled parameters) can share the same name??? And the rule (here comes another restriction, sigh): parameters following a variadic parameter must have argument labels (if there&#39;s only one, the actual argument becomes the formal parameter) and cannot be omitted. &lt;/p&gt;&lt;/blockquote&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 下面两个 a 标签参数，完全合法（因为是 label 无所谓）&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 而且 a b 后面的参数 a c（或者单独的一个参数）必须存在，不能用 _ 省略，&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;//  也可以理解，要不咋知道是第二个参数，而不是前面的可变参数来的？&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;a&lt;/span&gt; (&lt;span class=&#34;hljs-params&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt;..., &lt;span class=&#34;hljs-params&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;c&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; b.reduce(&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt;) &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; c&lt;br&gt;&amp;#125;&lt;br&gt;a(a: &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;3&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;4&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;5&lt;/span&gt;, a: &lt;span class=&#34;hljs-number&#34;&gt;6&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&#34;closures&#34;&gt;Closures&lt;/h2&gt;
&lt;h3 id=&#34;n-abbreviated-forms-of-closures&#34;&gt;N Abbreviated Forms of Closures&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: There are too many syntactic sugars for closures to list here, but the most outrageous is the form that requires only a `&gt;` symbol. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The essence of why this works is that String has a function called &lt;code&gt;&amp;gt;&lt;/code&gt;. Yes, you read that right—a symbol can also be a function! More on this shocking detail later.&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; num &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; [&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;9&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;1&amp;quot;&lt;/span&gt;]&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; sortedNum &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; num.sroted(by: &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Absolutely unbelievable.&lt;/p&gt;
&lt;h3 id=&#34;omitting-return-statements&#34;&gt;Omitting Return Statements&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: You might understand &#34;single-line returns can omit `return`,&#34; but you&#39;ll never comprehend &#34;multi-line returns can also omit `return`.&#34; &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In TS, omitting the &lt;code&gt;return&lt;/code&gt; statement works the same as in Swift’s ordinary syntax—single-line returns omit &lt;code&gt;return&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs typescript&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 省略写法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;a&lt;/span&gt; = (&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;) =&amp;gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 正常写法是：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;a&lt;/span&gt; = (&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;) =&amp;gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Ordinary Swift syntax:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;a&lt;/span&gt;()-&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// -&amp;gt; 因为单行，所以省略了 return&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;But! The SwiftUI way of writing it:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-type&#34;&gt;VStack&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-type&#34;&gt;Text&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt;)&lt;br&gt; &lt;span class=&#34;hljs-type&#34;&gt;Text&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;World&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;You read that right—this is also a trailing closure function, where &lt;code&gt;VStack&lt;/code&gt; is a function. The closure parameter, being the last and only parameter, allows omitting the parentheses of &lt;code&gt;VStack&lt;/code&gt;. But! It returns two &lt;code&gt;Text&lt;/code&gt; function calls without writing &lt;code&gt;return&lt;/code&gt;. Why?&lt;/p&gt;
&lt;p&gt;Because it uses &lt;code&gt;ViewBuilder&lt;/code&gt;, which shares the same syntactic sugar as &lt;code&gt;resultBuilder&lt;/code&gt; later.&lt;/p&gt;
&lt;h3 id=&#34;auto-closures&#34;&gt;Auto-Closures&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: At first glance, assigning a plain closure seems unremarkable, but its lazy evaluation capability left me utterly shocked. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;When I first encountered auto-closures, they were introduced with this example, touting their “lazy evaluation” capability:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; customersInLine &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; [&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Chris&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Alex&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Ewa&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Barry&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Daniella&amp;quot;&lt;/span&gt;]&lt;br&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(customersInLine.count)&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “5”&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; customerProvider &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &amp;#123; customersInLine.remove(at: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;) &amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(customersInLine.count)&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 ”5“&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Now serving &lt;span class=&#34;hljs-subst&#34;&gt;\(customerProvider())&lt;/span&gt;!&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “Now serving Chris!”&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(customersInLine.count)&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “4”&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;I thought, “Isn’t this just a plain closure, similar to TS, where the &lt;code&gt;customerProvider&lt;/code&gt; variable is assigned a closure?” The same implementation in TS looks like this:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;customerProvider&lt;/span&gt; = (&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;) =&amp;gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// 省略逻辑&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Of course, the closure’s logic is only executed when it is called—what kind of lazy evaluation is this!&lt;/p&gt;
&lt;p&gt;But its lazy evaluation truly shines when closures are passed as parameters:&lt;/p&gt;
&lt;p&gt;A plain and straightforward explicit closure invocation, similar to how it’s done in TS, where the closure is passed as a parameter and written within a pair of curly braces:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// customersInLine 是 [&amp;quot;Alex&amp;quot;, &amp;quot;Ewa&amp;quot;, &amp;quot;Barry&amp;quot;, &amp;quot;Daniella&amp;quot;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;serve&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;customer&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;customerProvider&lt;/span&gt;: () -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;) &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Now serving &lt;span class=&#34;hljs-subst&#34;&gt;\(customerProvider())&lt;/span&gt;!&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;serve(customer: &amp;#123; customersInLine.remove(at: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;) &amp;#125; )&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “Now serving Alex!”&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;However, once you mark &lt;code&gt;customerProvider&lt;/code&gt; as &lt;code&gt;@autoclosuer&lt;/code&gt;, things become different. In this case, your &lt;code&gt;serve&lt;/code&gt; function call can be written like this:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// customersInLine 是 [&amp;quot;Ewa&amp;quot;, &amp;quot;Barry&amp;quot;, &amp;quot;Daniella&amp;quot;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;serve&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;customer&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;customerProvider&lt;/span&gt;: &lt;span class=&#34;hljs-keyword&#34;&gt;@autoclosure&lt;/span&gt; () -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;) &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Now serving &lt;span class=&#34;hljs-subst&#34;&gt;\(customerProvider())&lt;/span&gt;!&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;serve(customer: customersInLine.remove(at: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;))&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “Now serving Ewa!”&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Note the invocation of the &lt;code&gt;serve&lt;/code&gt; function at the end. Its parameter &lt;code&gt;customer&lt;/code&gt; is a statement &lt;code&gt;customersInLine.remove(at: 0)&lt;/code&gt;. In TS, regardless of the scenario, the call stack would first evaluate this value before invoking the &lt;code&gt;serve&lt;/code&gt; function. But in Swift, this syntax is merely a different form of the above—the logic remains the same. That is, the &lt;code&gt;serve&lt;/code&gt; function is executed first, and the statement is executed internally only when &lt;code&gt;customerProvider&lt;/code&gt; is called. This is what they call &lt;code&gt;延迟计算&lt;/code&gt;, right?&lt;/p&gt;
&lt;p&gt;If you write this often, you’ll encounter many cases like the following code:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;a(b.remove(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;))&lt;br&gt;c(d.add4())&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;At this point, it becomes hard to tell whether the statements inside the parentheses or the outer function are executed first. As a result, the Swift official documentation also provides a warning:&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Overusing autoclosure can make your code difficult to understand. The context and function name should clearly indicate that evaluation is being deferred. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This is really absurd—if it’s not recommended, why design it in the first place?!&lt;/p&gt;
&lt;h2 id=&#34;enums&#34;&gt;Enums&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking fact: In other languages, enums are merely a convenient, optional type for state machine checks, but in Swift, they are a first-class type and one of the most commonly used features—even capable of replacing Structs in certain scenarios. Can you believe it? &lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking fact 2: Enums are value types. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In TS, enums are just plain old “enums,” nothing more than listing values, mostly used to describe states in state machines:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs typescript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; A &amp;#123;&lt;br&gt; B = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;B&amp;quot;&lt;/span&gt;&lt;br&gt; C = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;c&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Of course, the tricks TS plays with runtime and compile-time differences are another story. In TS, you could easily replace an enum with an object:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs typescript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; A &amp;#123;&lt;br&gt;  B = &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;,&lt;br&gt;  C = &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;,&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; A = &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;B&lt;/span&gt;: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;,&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;C&lt;/span&gt;: &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;,&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt;;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;associated-values&#34;&gt;Associated Values&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: Swift, What Were You Thinking Designing Enums Like This?! &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In Swift, enums hold paramount importance. You can iterate through all their cases (requiring conformance to the &lt;code&gt;CaseIterable&lt;/code&gt; protocol), treat a case as a function, and pass values during invocation for the enum instance to process—this is called “associated values”:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; b(&lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;, &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) &lt;span class=&#34;hljs-comment&#34;&gt;// 声明方式好像一个协议（也就是抽象类）&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; c(&lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Then, when using it, you can pass values:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; a = A.&lt;span class=&#34;hljs-title function_&#34;&gt;b&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;1024&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;9527&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Enums are most commonly used in &lt;code&gt;switch&lt;/code&gt; statements. Combined with Swift’s surprisingly powerful &lt;code&gt;case&lt;/code&gt; matching, you can do this:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;switch&lt;/span&gt; a &amp;#123;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; .&lt;span class=&#34;hljs-title function_&#34;&gt;b&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; d, &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; f):&lt;br&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;A.b 关联值为:\(d) 和 \(f)&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; .&lt;span class=&#34;hljs-title function_&#34;&gt;c&lt;/span&gt;(g): &lt;span class=&#34;hljs-comment&#34;&gt;// 上面提到的另一种 switch case 写法&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;A.c:\(g)&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;The first time I saw this, I found it quite abstract and couldn’t think of any practical use cases (since I’d never done it this way).&lt;/p&gt;
&lt;h3 id=&#34;implicit-assignment&#34;&gt;Implicit Assignment&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: The type declaration in enums doesn&#39;t specify the enum&#39;s own type (key-value pairs) but rather the type of its cases. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Implicit assignment in Swift is consistent with TypeScript or other C-like languages: the first value is &lt;code&gt;n&lt;/code&gt;, and subsequent values are &lt;code&gt;n + 1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But Swift goes a step further—it performs implicit assignment based on the type you declare, which isn’t the default behavior. For example:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 声明了 Int 类型，所以隐式赋值了&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; b, c, d &lt;span class=&#34;hljs-comment&#34;&gt;// b c d 分别为 0 1 2(需要使用 A.b.rawValue 才能访问，这又是另一个震惊点了）&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;B&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 声明了 String 类型，所以隐式赋值了&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; bb, cc, dd &lt;span class=&#34;hljs-comment&#34;&gt;// bb cc dd分别为 &amp;quot;bb&amp;quot; &amp;quot;cc&amp;quot; &amp;quot;dd&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;In TypeScript, you can’t even specify the &lt;code&gt;Int&lt;/code&gt; type that Swift allows (this type actually refers to the case’s type). Instead, you can only specify the enum’s type (usually key-value pairs &lt;code&gt;Record&lt;/code&gt; elsewhere).&lt;/p&gt;
&lt;h2 id=&#34;structs-and-classes&#34;&gt;Structs and Classes&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: Structs are actually value types?! &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This seemingly straightforward struct is actually a value type (using &lt;code&gt;Immutable&lt;/code&gt; optimizations for performance):&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;a&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c: &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Something wrapped in curly braces doesn’t look like a value at all! Bizarre! Shocking!&lt;/p&gt;
&lt;h2 id=&#34;properties&#34;&gt;Properties&lt;/h2&gt;
&lt;h3 id=&#34;various-property-wrappers-observers&#34;&gt;Various Property Wrappers/Observers&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: Swift does way too much. Concepts like computed properties, stored properties, property wrappers (which TypeScript also has), and property observers (TypeScript has these too, bundled with wrappers) are typically framework-level features, such as Vue&#39;s `computed` and `watch`. Yet Swift implements them directly in structs and classes—unbelievable! &lt;/p&gt;&lt;/blockquote&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 存储属性，直接有值，在 struct 实例化的时候就确定了&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 一个只读计算属性，不存储值，因为单行，return 可以省略&lt;/span&gt;&lt;br&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Note: Property wrappers are not available for global variables.&lt;/p&gt;
&lt;h2 id=&#34;subscripts&#34;&gt;Subscripts&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: Honestly, the mere existence of the &#34;subscript&#34; concept is shocking enough. It provides a shortcut for accessing elements in collections, lists, or dictionaries. What&#39;s more, subscripts can even accept multiple values, just like functions. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Essentially, subscript calls can be thought of as function calls on data structures (like the aforementioned collections, lists, or dictionaries) to access values. The difference is that function calls use &lt;code&gt;()&lt;/code&gt;, while subscripts use &lt;code&gt;[]&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;inheritance&#34;&gt;Inheritance&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: Similar to all the previous shocking points, Swift seems to casually add keywords for every possible purpose or effect. As a result, the number of reserved words (though they can&#39;t strictly be called &#34;reserved&#34; since Swift allows using any keyword by wrapping it in backticks—more on that later) related to inheritance is overwhelming. Examples include: `final`, `override`, `open`, `required`, and so on. &lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;initialization&#34;&gt;Initialization&lt;/h2&gt;
&lt;h3 id=&#34;memberwise-initializers-for-structs&#34;&gt;Memberwise Initializers for Structs&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking Point: Because structs are value types, their initialization rules differ from those of traditional classes. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;At first glance, structs and classes are just ordinary data structures, no different from classes in TypeScript. But once again, Swift complicates things here.&lt;/p&gt;
&lt;p&gt;Since Struct is a value type, it’s special in that it has a constructor form called the “memberwise initializer.” This means when a Struct has no &lt;code&gt;init&lt;/code&gt; at all, its properties can be initialized by passing parameters without explicitly writing them out.&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 此 Struct 没有 init，因此适用逐一成员构造器&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123; &lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 上述效果等同于：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;a&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;, &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; a&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; b&lt;br&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 上面两个都可以这么用：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;(a: &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;, b: &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;)&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;However, this rule doesn’t apply to Classes. A Class must explicitly have an init constructor method to initialize its properties.&lt;/p&gt;
&lt;h3 id=&#34;designated-initializers-and-convenience-initializers-for-classes&#34;&gt;Designated Initializers and Convenience Initializers for Classes&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: What? There are two types of initializers???&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The designated initializer of a class functions just like a regular &lt;code&gt;construct&lt;/code&gt; in TS—it’s a &lt;code&gt;init&lt;/code&gt; method. But its convenience initializer… is marked by adding the &lt;code&gt;convenience&lt;/code&gt; keyword before init (here we go again!):&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;convenience&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;() &amp;#123;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Actually, the Swift documentation doesn’t clearly explain to readers why convenience initializers are needed. Instead, it dives straight into class construction, inheritance, and initializer delegation. Here, I’ll explain to beginners why convenience initializers exist—for a very simple reason: &lt;code&gt;类中的多个指定构造器不允许互相调用。&lt;/code&gt;. That’s it! Can’t resist the urge to call it? Want to simplify the initialization process? Go ahead and use a convenience initializer!&lt;/p&gt;
&lt;p&gt;Standard approach:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c: &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;, &lt;span class=&#34;hljs-params&#34;&gt;c&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; b&lt;br&gt; &amp;#125;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;() &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;UnKnown&amp;quot;&lt;/span&gt;&lt;br&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Doesn’t it feel repetitive writing &lt;code&gt;self.b&lt;/code&gt; twice like this? So, in the second &lt;code&gt;init()&lt;/code&gt;, you might want to do this:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;() &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.&lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;(b: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, c: &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;UnKnown&amp;quot;&lt;/span&gt;)	&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Sorry, the compiler throws an error: &lt;code&gt;Designated initializer for &#39;A&#39; cannot delegate (with &#39;self.init&#39;); did you mean this to be a convenience initializer?&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In this case, you must use a convenience initializer:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;converience &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;() &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.&lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;(b: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, c: &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;UnKnown&amp;quot;&lt;/span&gt;)	&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;All for the sake of “convenience”!&lt;/p&gt;
&lt;h3 id=&#34;failable-initializers&#34;&gt;Failable Initializers&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: Initializers can fail???&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;A so-called failable initializer simply means the initialization process (the init function call) might throw an error.&lt;/p&gt;
&lt;p&gt;Therefore, if the initializer throws an error, you don’t need to wrap it in a &lt;code&gt;try-catch&lt;/code&gt; like in TS. Instead, you can directly indicate it by adding a question mark after &lt;code&gt;init&lt;/code&gt;, turning it into &lt;code&gt;init?&lt;/code&gt;. Then, at the point where an error might occur, return &lt;code&gt;nil&lt;/code&gt;, and when instantiating, check if it’s &lt;code&gt;nil&lt;/code&gt;. That’s it!&lt;/p&gt;
&lt;p&gt;In Swift, normal constructors don’t return values, which is the same as in TypeScript—but TypeScript constructors can return values. If the return value is an object type, it replaces the &lt;code&gt;this&lt;/code&gt; object, which seems even more shocking.&lt;/p&gt;
&lt;h2 id=&#34;optional-chaining&#34;&gt;Optional Chaining&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: Q: Under what circumstances does a function have parentheses written but not execute? A: In the context of optional chaining. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Without further ado, let’s look at an example:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;A&lt;/span&gt;() -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;)&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;B&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c: &lt;span class=&#34;hljs-type&#34;&gt;C&lt;/span&gt;?&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;C&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; d: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;?&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; d &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;B&lt;/span&gt;()&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 震惊的事情发生了：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 这里，A 函数未执行，不会打印 OK，赋值也不会成功，因为 c 是 nil，这个语句返回 nil（通常 Swift 的赋值语句都返回 Void 的，也就是空元组）&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 返回 nil 意味着你可以使用 if-let 语句判断&lt;/span&gt;&lt;br&gt;d.c&lt;span class=&#34;hljs-operator&#34;&gt;?&lt;/span&gt;.d &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;()&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&#34;error-handling&#34;&gt;Error Handling&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: Like enums, Swift&#39;s try-catch (actually do-catch) is designed to be quite complex. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The catch statement can not only capture arbitrary errors but also specific error types. Using &lt;code&gt;is&lt;/code&gt; or similar to a switch statement, there can be multiple catch branches for matching—absurd:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;do&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;try&lt;/span&gt; someError()&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;ErrorA&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// xxx&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;ErrorB&lt;/span&gt;, &lt;span class=&#34;hljs-type&#34;&gt;ErrorC&lt;/span&gt;, &lt;span class=&#34;hljs-type&#34;&gt;ErrorD&lt;/span&gt;.a &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// xxx&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&#34;concurrency&#34;&gt;Concurrency&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: While reading the documentation, I initially didn&#39;t realize that `withTaskGroup` is a built-in method for Group Task... &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;As for &lt;code&gt;await&lt;/code&gt;, it’s used exactly the same way as &lt;code&gt;await&lt;/code&gt; in TypeScript—so satisfying!&lt;/p&gt;
&lt;h2 id=&#34;extensions&#34;&gt;Extensions&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: Extensions might be the most powerful design in Swift. Any object, whether built-in or third-party, can be extended freely, at low cost, without any complex declarations or keywords. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Compared to TypeScript, where extending an existing class requires manipulating the prototype chain and then adjusting the object’s &lt;code&gt;this&lt;/code&gt; to point to the class, in Swift, you just need a &lt;code&gt;extension&lt;/code&gt;, and then you can boldly write any methods or properties you want—their &lt;code&gt;this&lt;/code&gt; will point to the instance or the extended type (i.e., static methods or properties).&lt;/p&gt;
&lt;p&gt;Absolutely mind-blowing.&lt;/p&gt;
&lt;h2 id=&#34;protocols&#34;&gt;Protocols&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: I believe protocols were introduced to address the abstraction issues of Structs, since in any language, classes themselves can be inherited (this might be an absolute statement), eliminating the need for an additional &#34;protocol&#34;—abstract classes would suffice. Incidentally, while Swift restricts classes to single inheritance, it allows protocols to play a more versatile role across classes, Structs, and enums. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Enough said—my thoughts are all above.&lt;/p&gt;
&lt;h2 id=&#34;generics&#34;&gt;Generics&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: Generics introduced &#34;associated types,&#34; which essentially serve the same purpose as generics declared upfront but are even more powerful. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Swift’s generic associated types look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;protocol&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Container&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;associatedtype&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;mutating&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;append&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;item&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt;)&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; count: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-keyword&#34;&gt;get&lt;/span&gt; &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;subscript&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;i&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-keyword&#34;&gt;get&lt;/span&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;As for why it wasn’t designed like this:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;protocol&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Container&lt;/span&gt;&amp;lt;&lt;span class=&#34;hljs-title class_&#34;&gt;Item&lt;/span&gt;&amp;gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;mutating&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;append&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;item&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt;)&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; count: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-keyword&#34;&gt;get&lt;/span&gt; &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;subscript&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;i&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-keyword&#34;&gt;get&lt;/span&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Presumably because the Item might become too lengthy and inelegant, such as:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;protocol&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;SuffixableContainer&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;Container&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;associatedtype&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Suffix&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;SuffixableContainer&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Suffix&lt;/span&gt;.&lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt; &lt;span class=&#34;hljs-operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;suffix&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;size&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Suffix&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;If written after the name, generic declarations would often exceed the screen width, which is unbearable.&lt;/p&gt;
&lt;h2 id=&#34;opaque-types-and-protocol-wrapping&#34;&gt;Opaque Types and Protocol Wrapping&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: You can achieve an effect where only the compiler knows the concrete type, while the caller only knows it conforms to a certain protocol. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Essentially, opaque types and protocol wrapping aim to hide implementation details from callers while allowing code refactoring with minimal changes. For example:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;protocol&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Shape&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;area&lt;/span&gt;() -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Circle&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;Shape&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; radius: &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;area&lt;/span&gt;() -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt; &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; .pi &lt;span class=&#34;hljs-operator&#34;&gt;*&lt;/span&gt; radius &lt;span class=&#34;hljs-operator&#34;&gt;*&lt;/span&gt; radius&lt;br&gt;    &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;makeShape&lt;/span&gt;() -&amp;gt; &lt;span class=&#34;hljs-keyword&#34;&gt;some&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Shape&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Circle&lt;/span&gt;(radius: &lt;span class=&#34;hljs-number&#34;&gt;5&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Here, &lt;code&gt;makeShape&lt;/code&gt; can return any type that conforms to the &lt;code&gt;Shape&lt;/code&gt; protocol, without explicitly specifying the &lt;code&gt;Circle&lt;/code&gt; type. This way, if new types like &lt;code&gt;Struct&lt;/code&gt; that conform to &lt;code&gt;Shape&lt;/code&gt; are added later, the function can still return them (a common practice in SwiftUI, such as &lt;code&gt;some View&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&#34;memory-safety&#34;&gt;Memory Safety&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Shocking point: Since Swift allows passing values by reference, it can lead to simultaneous read-write operations on the same memory region (which makes sense, right?). &lt;/p&gt;&lt;/blockquote&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;add&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-keyword&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) &amp;#123;&lt;br&gt;    b &lt;span class=&#34;hljs-operator&#34;&gt;+=&lt;/span&gt; a&lt;br&gt;&amp;#125;&lt;br&gt;add(&lt;span class=&#34;hljs-operator&#34;&gt;&amp;amp;&lt;/span&gt;a)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;The above code will crash at runtime, but the compiler won’t warn you. There are many similar cases where extra caution is needed. Since TypeScript doesn’t support pass-by-reference, such issues don’t arise—easy peasy.&lt;/p&gt;
&lt;h2 id=&#34;advanced-operators&#34;&gt;Advanced Operators&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Mind-blowing point: You can rewrite/customize the implementation of a function with a symbol as its name, thereby enabling operations between instances of the same class or structure. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;+&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;left&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;, &lt;span class=&#34;hljs-params&#34;&gt;right&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;(b: left.b &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; right.b, c: left.c &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; right.c)&lt;br&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 然后你就可以&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;()&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;()&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; b &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 是的你没看错，struct 实例可以相加！&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;This design is brilliant—why didn’t I think of it? Amazing, truly amazing.&lt;/p&gt;
&lt;p&gt;It’s worth noting that operator methods have their own attributes. For instance, &lt;code&gt;+&lt;/code&gt; is a binary operator and also an infix operator, so it takes two functions. &lt;code&gt;-&lt;/code&gt; can be either an infix operator (binary operator) or a prefix operator (unary operator, requiring &lt;code&gt;prefix&lt;/code&gt; before &lt;code&gt;func&lt;/code&gt;), making it overloadable.&lt;/p&gt;
&lt;p&gt;However, Swift also specifies that the assignment operator &lt;code&gt;=&lt;/code&gt; cannot be overloaded, nor can the ternary conditional operator (&lt;code&gt;a ? b : c&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;This operator is so common that you can find operator functions like &lt;code&gt;==&lt;/code&gt; for equality checks in almost any Swift built-in object or Foundation object.&lt;/p&gt;
&lt;p&gt;You can even implement your own operators!&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 需要先声明，这是一个操作符，因为有两个值在操作符，所以这是个中缀操作符(infix)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;infix&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;operator&lt;/span&gt; &lt;span class=&#34;hljs-title&#34;&gt;+++++++&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 然后扩展一下整数类型&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;extension&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;+++++++&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;left&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;, &lt;span class=&#34;hljs-params&#34;&gt;right&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; (left &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; right) &lt;span class=&#34;hljs-operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 因为有 7 个 + 号，所以我写了乘以 7&lt;/span&gt;&lt;br&gt;    &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 这么用：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;hljs-operator&#34;&gt;+++++++&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 63&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Absolutely incredible.&lt;/p&gt;
&lt;h3 id=&#34;result-builders&#34;&gt;Result Builders&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Mind-blowing point: Swift goes to extreme lengths for &#34;elegance&#34; and &#34;reusability.&#34; &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;To make the following code look cleaner, Swift “invented” the magical &lt;code&gt;@resultBuilder&lt;/code&gt;, as shown below:&lt;/p&gt;
&lt;p&gt;Standard approach:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 这里会有一个三元判断&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;(a: a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;666&lt;/span&gt; : &lt;span class=&#34;hljs-number&#34;&gt;999&lt;/span&gt; )&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Swift argues that the ternary conditional above is too cumbersome and becomes hard to read with complex logic, so it aims for elegance. Thus, result builders were born (if I understand correctly):&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;@resultBuilder&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;BBuilder&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;buildBlock&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// 对应 if/else 分支的执行结果&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; b&lt;br&gt;    &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;buildEither&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;aaa&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// 对应 if 分支&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; aaa&lt;br&gt;    &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;buildEither&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;bbb&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// 对应 else 分支&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; bbb&lt;br&gt;    &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Then, assign the &lt;code&gt;c&lt;/code&gt; variable like this:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;BB&lt;/span&gt;(&lt;span class=&#34;hljs-meta&#34;&gt;@BBuilder&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: () -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;(a: b())&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 终于等到 c 了，这里的 c 和最开始的 c 完全一样&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;BB&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;&lt;/span&gt; b &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;666&lt;/span&gt;&lt;br&gt;    &amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;999&lt;/span&gt;&lt;br&gt;    &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Reusable, elegant, efficient—brilliant!&lt;/p&gt;
&lt;h2 id=&#34;lexical-structure&#34;&gt;Lexical Structure&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Mind-blowing point: Despite Swift having an overwhelming number of reserved words, it allows you to use them as identifiers. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The method is simple: enclose them in backticks:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;`func`&lt;/span&gt;() &amp;#123;&amp;#125; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 一个叫做 `func` 的函数&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 调用也是 &lt;/span&gt;&lt;br&gt;`func`()&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;However, apart from keywords, &lt;code&gt;x&lt;/code&gt; and x refer to the same variable.&lt;/p&gt;
</description>
            <pubDate>Thu, 27 Mar 2025 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/tech/app-dev-journey-swift.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/tech/app-dev-journey-swift.html</guid>
            
                <category>Apple</category>
            
                <category>苹果</category>
            
                <category>初体验</category>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>技术</category>
            
                <category>Swift</category>
            
                <category>全栈</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>How to Develop iOS Apps with Cusor and Preview in Real Time</title>
            <description>&lt;p&gt;This article is an expanded version by the original author. The original post can be found at:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.imjp.uk/fxxk-xcode&#34; target=&#34;_blank&#34;&gt; 在 Cursor 打造高效 iOS 开发环境： AI 编程 + 实时预览完整指南&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I’m adding supplementary notes here because when I followed this tutorial, I encountered some pitfalls due to my lack of familiarity with Xcode and iOS development. Hence, I’m documenting these points for reference.&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; This article serves as a supplement to the original post. Please refer to the original first.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;supplement-to-“installing-the-swift-plugin-and-codelldb-plugin”&#34;&gt;Supplement to “Installing the Swift Plugin and CodeLLDB Plugin”:&lt;/h3&gt;
&lt;p&gt;The Swift plugin mentioned here is outdated. You should use the latest plugin instead. Search for &lt;strong&gt;Swift Programming Language&lt;/strong&gt; in the plugin marketplace.&lt;/p&gt;
&lt;h3 id=&#34;supplement-to-step-2-of-“setting-up-hot-reloading”&#34;&gt;Supplement to Step 2 of “Setting Up Hot Reloading”:&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Open the corresponding Target settings in your project -&gt; Build Settings -&gt; Search for **Other Linker Flags**, then add **-Xlinker** and **-interposable** separately.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Note that both must be added simultaneously, i.e., copy &lt;code&gt;-Xlinker -interposable&lt;/code&gt; and paste it as shown in the image below:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/use-cursor-develop-ios/197800e8-7a61-8012-b016-c6caf5e72435.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;source-kit-file-not-found-error-after-adding-new-files&#34;&gt;Source Kit File Not Found Error After Adding New Files:&lt;/h3&gt;
&lt;p&gt;You’ll need to rerun the command mentioned in the original post using Xcode-Build-Server:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs bash&#34;&gt;// 然后在项目根目录下根据你的项目文件类型执行对应的命令:&lt;br&gt;xcode-build-server config -workspace *.xcworkspace -scheme &amp;lt;XXX&amp;gt; &lt;br&gt;xcode-build-server config -project *.xcodeproj -scheme &amp;lt;XXX&amp;gt;&lt;br&gt;&lt;br&gt;//例如你用的是 exampleProject.xcodeproj：&lt;br&gt;xcode-build-server config -project exampleProject.xcodeproj -scheme exampleProject&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;issue-with-swiftui-previews-not-updating-in-real-time-during-debugging&#34;&gt;Issue with SwiftUI Previews Not Updating in Real Time During Debugging:&lt;/h3&gt;
&lt;p&gt;Certain ViewModifier additions or modifications may indeed fail to update in real time, but changes like modifying a String in Text generally work fine. I’m not entirely sure why.&lt;/p&gt;
</description>
            <pubDate>Tue, 11 Feb 2025 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/tech/use-cursor-develop-ios.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/tech/use-cursor-develop-ios.html</guid>
            
                <category>Apple</category>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>AI</category>
            
                <category>技术</category>
            
                <category>XCode</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>Migration from Jekyll to Hexo: Issue Log</title>
            <description>&lt;h2 id=&#34;preface&#34;&gt;Preface&lt;/h2&gt;
&lt;p&gt;As a front-end developer, I’m not particularly familiar with other scripting languages like Ruby. When I first started blogging, I used GitHub Pages as my platform, which defaults to the Jekyll framework. At the time, I believed content mattered more than the framework, so I stuck with it—for a full decade.&lt;/p&gt;
&lt;p&gt;Over the years, I tried several themes before settling on the one provided by Hux. It was clean, elegant, and open-source:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://huangxuan.me/&#34; target=&#34;_blank&#34;&gt; 黄玄的博客 | Hux Blog&lt;/a&gt;&lt;/p&gt;  
&lt;p&gt;I customized this theme extensively, such as adding custom right-side content, custom data, and rendering using Notion as a data source.&lt;/p&gt;
&lt;p&gt;However, with the release of Apple’s M-series chips, I found it increasingly difficult to handle Ruby’s compatibility issues between Intel and Apple silicon. For example, I had to specifically use x86 architecture instructions to occasionally build successfully—and that was only if I didn’t touch any dependencies:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;110&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;111&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;112&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;113&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;114&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;115&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;116&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;117&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;118&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;119&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;120&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;121&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;122&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;123&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;124&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;125&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;126&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;127&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;128&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;129&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;130&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;131&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;132&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;133&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;134&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;135&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;136&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;137&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;138&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;139&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;140&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;141&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;142&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;143&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;144&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;145&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;146&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;147&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;148&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;149&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;150&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;151&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;152&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;153&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;154&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;155&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;156&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;157&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;158&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;159&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;160&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;161&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;162&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;163&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;164&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;165&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;166&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;167&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;168&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;169&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;170&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;171&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;172&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;173&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;174&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs bash&#34;&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;arch&lt;/span&gt; -x86_64 bundle &lt;span class=&#34;hljs-built_in&#34;&gt;exec&lt;/span&gt; jekyll server --trace --config=_config.dev.yml --ssl-key local.xheldon.cn.key --ssl-cert local.xheldon.cn.pem&lt;br&gt;```  &lt;br&gt;&lt;br&gt;This made me realize that &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; I didn’t switch frameworks soon, I might eventually lose the ability to publish my blog altogether.  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;## Technical Selection  &lt;/span&gt;&lt;br&gt;&lt;br&gt;This section is straightforward. You could essentially think of Hexo as the JavaScript implementation of Jekyll. Most of the concepts—about 95%—are identical, making the migration effortless.  &lt;br&gt;&lt;br&gt;More importantly, someone had already ported Hux’s blog theme to Hexo, so I adopted it directly. Here’s a brief record of the process.  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;## Migration Process  &lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;### Plugin Migration  &lt;/span&gt;&lt;br&gt;&lt;br&gt;This was relatively easy. For plugins &lt;span class=&#34;hljs-keyword&#34;&gt;in&lt;/span&gt; Jekyll, I implemented helper &lt;span class=&#34;hljs-built_in&#34;&gt;functions&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;in&lt;/span&gt; Hexo. For example, here’s how I handled the Bookmark tag from Notion (path: `_plugins/add-attribute.rb`):  &lt;br&gt;&lt;br&gt;```ruby&lt;br&gt;module Jekyll&lt;br&gt;    class RenderBookMarkBlock &amp;lt; Liquid::Block&lt;br&gt;        def initialize(tag_name, attr, tokens)&lt;br&gt;            super&lt;br&gt;            &lt;span class=&#34;hljs-comment&#34;&gt;# 普通的链接没有 yid 和 bid&lt;/span&gt;&lt;br&gt;            attrs = attr.scan(/url\=\&amp;quot;(.*)\&amp;quot;\stitle\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;\syid\=\&amp;quot;(.*)\&amp;quot;\sbid\=\&amp;quot;(.*)\&amp;quot;/)&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; !attrs.empty?&lt;br&gt;                @url = attrs[0][0]&lt;br&gt;                @title = attrs[0][1]&lt;br&gt;                @img = attrs[0][2]&lt;br&gt;                @yid = attrs[0][3]&lt;br&gt;                @bid = attrs[0][4]&lt;br&gt;                @firstChar = @title.empty? ? &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; : (@title)[0].upcase&lt;br&gt;                @error = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt;&lt;br&gt;                attrs = attr.scan(/url\=\&amp;quot;(.*)\&amp;quot;\stitle\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;/)&lt;br&gt;                @url = attrs[0][0]&lt;br&gt;                @title = attrs[0][1]&lt;br&gt;                @img = attrs[0][2]&lt;br&gt;                @firstChar = @title.empty? ? &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; : (@title)[0].upcase&lt;br&gt;                @error = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br&gt;            end&lt;br&gt;        end&lt;br&gt;        def render(context)&lt;br&gt;            @desc = super&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; !@yid.nil? &amp;amp;&amp;amp; !@yid.empty?&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;https://www.youtube.com/embed/#&amp;#123;@yid&amp;#125;?rel=0&amp;#x27; title=&amp;#x27;YouTube video player&amp;#x27; frameborder=&amp;#x27;0&amp;#x27; allow=&amp;#x27;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            elsif !@bid.nil? &amp;amp;&amp;amp; !@bid.empty?&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27; style=&amp;#x27;border-bottom: 1px solid #ddd;&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;//player.bilibili.com/player.html?bvid=#&amp;#123;@bid&amp;#125;&amp;amp;high_quality=1&amp;amp;as_wide=1&amp;#x27; scrolling=&amp;#x27;no&amp;#x27; border=&amp;#x27;0&amp;#x27; frameborder=&amp;#x27;no&amp;#x27; framespacing=&amp;#x27;0&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt;&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p&amp;gt;&amp;lt;a class=&amp;#x27;link-bookmark&amp;#x27; href=&amp;#x27;#&amp;#123;@url&amp;#125;&amp;#x27; target=&amp;#x27;_blank&amp;#x27;&amp;gt;&amp;lt;span data-bookmark-img=&amp;#x27;#&amp;#123;@img&amp;#125;&amp;#x27; data-bookmark-title=&amp;#x27;#&amp;#123;@firstChar&amp;#125;&amp;#x27;&amp;gt;&amp;lt;img src=&amp;#x27;#&amp;#123;@img&amp;#125;&amp;#x27;/&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&amp;lt;span&amp;gt;#&amp;#123;@title&amp;#125;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;#&amp;#123;@desc&amp;#125;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;#&amp;#123;@url&amp;#125;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            end&lt;br&gt;        end&lt;br&gt;    end&lt;br&gt;end&lt;br&gt;&lt;br&gt;Liquid::Template.register_tag(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;render_bookmark&amp;#x27;&lt;/span&gt;, Jekyll::RenderBookMarkBlock)&lt;br&gt;```  &lt;br&gt;&lt;br&gt;In Hexo, I rewrote it like this (path: `scripts/liquid.js`):  &lt;br&gt;&lt;br&gt;```javascript&lt;br&gt;hexo.extend.tag.register(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;render_bookmark&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt;(args, content) &amp;#123;&lt;br&gt;    const [url, title, img, yid, bid] = args.map(getValue);&lt;br&gt;    const firstChar = title ? title[0].toUpperCase() : &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;;&lt;br&gt;    const strip_html = hexo.extend.helper.get(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;strip_html&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-built_in&#34;&gt;bind&lt;/span&gt;(hexo);&lt;br&gt;    const trim = hexo.extend.helper.get(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;trim&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-built_in&#34;&gt;bind&lt;/span&gt;(hexo);&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (yid) &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-built_in&#34;&gt;return&lt;/span&gt; `&amp;lt;p class=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27;&lt;/span&gt;&amp;gt;&amp;lt;iframe src=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;https://www.youtube.com/embed/$&amp;#123;yid&amp;#125;?rel=0&amp;#x27;&lt;/span&gt; title=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;YouTube video player&amp;#x27;&lt;/span&gt; frameborder=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt; allow=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&amp;#x27;&lt;/span&gt; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;`&lt;br&gt;    &amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (bid) &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-built_in&#34;&gt;return&lt;/span&gt; `&amp;lt;p class=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27;&lt;/span&gt; style=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;border-bottom: 1px solid #ddd;&amp;#x27;&lt;/span&gt;&amp;gt;&amp;lt;iframe src=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;//player.bilibili.com/player.html?bvid=$&amp;#123;bid&amp;#125;&amp;amp;high_quality=1&amp;amp;as_wide=1&amp;#x27;&lt;/span&gt; scrolling=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;no&amp;#x27;&lt;/span&gt; border=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt; frameborder=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;no&amp;#x27;&lt;/span&gt; framespacing=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;`;&lt;br&gt;    &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;return&lt;/span&gt; `&amp;lt;p&amp;gt;&amp;lt;a class=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;link-bookmark&amp;#x27;&lt;/span&gt; href=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;$&amp;#123;url&amp;#125;&amp;#x27;&lt;/span&gt; target=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;_blank&amp;#x27;&lt;/span&gt;&amp;gt;&amp;lt;span data-bookmark-img=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;$&amp;#123;img&amp;#125;&amp;#x27;&lt;/span&gt; data-bookmark-title=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;$&amp;#123;firstChar&amp;#125;&amp;#x27;&lt;/span&gt;&amp;gt;&amp;lt;img src=&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;$&amp;#123;img&amp;#125;&amp;#x27;&lt;/span&gt;/&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&amp;lt;span&amp;gt; &lt;span class=&#34;hljs-variable&#34;&gt;$&amp;#123;title&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt; &lt;span class=&#34;hljs-variable&#34;&gt;$&amp;#123;strip_html(trim(content))&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt; &lt;span class=&#34;hljs-variable&#34;&gt;$&amp;#123;url&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;`;&lt;br&gt;&amp;#125;, &amp;#123;&lt;br&gt;    ends: &lt;span class=&#34;hljs-literal&#34;&gt;true&lt;/span&gt;,&lt;br&gt;&amp;#125;);&lt;br&gt;```  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;### Permalink Issues  &lt;/span&gt;&lt;br&gt;&lt;br&gt;For some reason, even though I &lt;span class=&#34;hljs-built_in&#34;&gt;set&lt;/span&gt; `:category/:name.html` &lt;span class=&#34;hljs-keyword&#34;&gt;in&lt;/span&gt; Hexo, it still generated `life/2024-life-xxx.html` (using `-` as a directory separator), when the expected format was `life/xxx.html`. Looking at the &lt;span class=&#34;hljs-built_in&#34;&gt;source&lt;/span&gt; code `name`, it uses `slug` with `&lt;span class=&#34;hljs-built_in&#34;&gt;basename&lt;/span&gt;`, but `slug` generates URLs based on folder paths with hyphens. As a workaround, I had to manually modify filenames.  &lt;br&gt;&lt;br&gt;In Jekyll, the &lt;span class=&#34;hljs-built_in&#34;&gt;date&lt;/span&gt; prefix &lt;span class=&#34;hljs-keyword&#34;&gt;in&lt;/span&gt; filenames (e.g., `2024-02-12-xxx.md`) is ignored, and the title is simply `xxx`. In Hexo, however, the title is &lt;span class=&#34;hljs-built_in&#34;&gt;read&lt;/span&gt; from the `title` field &lt;span class=&#34;hljs-keyword&#34;&gt;in&lt;/span&gt; the post’s front matter. To fix this, I wrote a filter plugin to finalize the permalink:  &lt;br&gt;&lt;br&gt;```javascript&lt;br&gt;/**&lt;br&gt; * permalink 中的 name 不符合预期，对于 _posts/life/2015/xxx.md 来说，在文档中 :name 表示的是 xxx，但是实际是 life-2015-xxx&lt;br&gt; */&lt;br&gt;hexo.extend.filter.register(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;post_permalink&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; (data) &amp;#123;&lt;br&gt;  // 在这里修改 post.name 的值&lt;br&gt;  const arr = data.split(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;/&amp;#x27;&lt;/span&gt;).filter(Boolean);&lt;br&gt;  const categories = arr[0];&lt;br&gt;  const name = arr[1];&lt;br&gt;  &lt;span class=&#34;hljs-built_in&#34;&gt;return&lt;/span&gt; `&lt;span class=&#34;hljs-variable&#34;&gt;$&amp;#123;categories&amp;#125;&lt;/span&gt;/&lt;span class=&#34;hljs-variable&#34;&gt;$&amp;#123;name.split(&amp;#x27;-&amp;#x27;).filter(Boolean).slice(3).join(&amp;#x27;-&amp;#x27;)&amp;#125;&lt;/span&gt;`;&lt;br&gt;&amp;#125;);&lt;br&gt;```  &lt;br&gt;&lt;br&gt;Additionally, permalinks can’t be pure numbers—they must be strings:  &lt;br&gt;&lt;br&gt;&amp;#123;% render_caption caption=&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; img=&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/147800e8-7a61-8006-a660-e8ecfbe7da9a.webp&amp;quot;&lt;/span&gt; %&amp;#125;  &lt;br&gt;![](https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/147800e8-7a61-8006-a660-e8ecfbe7da9a.webp)  &lt;br&gt;&amp;#123;% endrender_caption %&amp;#125;  &lt;br&gt;&lt;br&gt;Otherwise, errors occur (the `.endsWith` method explains why):  &lt;br&gt;&lt;br&gt;&amp;#123;% render_caption caption=&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; img=&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/147800e8-7a61-8032-8e51-d6528f277306.webp&amp;quot;&lt;/span&gt; %&amp;#125;  &lt;br&gt;![](https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/147800e8-7a61-8032-8e51-d6528f277306.webp)  &lt;br&gt;&amp;#123;% endrender_caption %&amp;#125;  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;### EJS Template Syntax Issues  &lt;/span&gt;&lt;br&gt;&lt;br&gt;Unlike Jekyll’s Liquid syntax, EJS templates don’t allow custom front matter within nested templates. Parameters can only flow from outer to inner layers, not the other way around. For instance, &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; the `index` layout is `page` and includes front matter, `page.ejs` can’t &lt;span class=&#34;hljs-built_in&#34;&gt;read&lt;/span&gt; it. As a result, I had to pass parameters explicitly wherever needed.  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;### Markdown Rendering Differences  &lt;/span&gt;&lt;br&gt;&lt;br&gt;Hexo’s built-in `marked` renderer behaves differently from Jekyll’s `kramdown`. For example, `marked` ignores empty lines + paragraphs &lt;span class=&#34;hljs-keyword&#34;&gt;in&lt;/span&gt; cases like `&lt;span class=&#34;hljs-comment&#34;&gt;## h2 (line break + empty line) paragraph`, while `kramdown` preserves them.  &lt;/span&gt;&lt;br&gt;&lt;br&gt;Though minor, this discrepancy affected post excerpts on the homepage, causing slight inconsistencies. To maintain SEO stability and avoid search engines flagging major content changes, I switched to `markdown-it` by installing `hexo-renderer-markdown-it`.  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;### Liquid Sorting Issues  &lt;/span&gt;&lt;br&gt;&lt;br&gt;In Jekyll, I used Liquid sorting like this: `&amp;#123;&amp;#123; tags | &lt;span class=&#34;hljs-built_in&#34;&gt;split&lt;/span&gt;:&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;`**`SEPARATOR`**`&amp;#x27;&lt;/span&gt; | &lt;span class=&#34;hljs-built_in&#34;&gt;sort&lt;/span&gt; &amp;#125;&amp;#125;`:  &lt;br&gt;&lt;br&gt;&amp;#123;% render_caption caption=&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; img=&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/0389dcbb-8bb7-413c-a968-4a77a8b76b71.webp&amp;quot;&lt;/span&gt; %&amp;#125;  &lt;br&gt;![](https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/0389dcbb-8bb7-413c-a968-4a77a8b76b71.webp)  &lt;br&gt;&amp;#123;% endrender_caption %&amp;#125;  &lt;br&gt;&lt;br&gt;The problem is that Liquid sorts identical `&lt;span class=&#34;hljs-built_in&#34;&gt;sort&lt;/span&gt;` values alphabetically by `href`. This behavior carried over:  &lt;br&gt;&lt;br&gt;&amp;#123;% render_caption caption=&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; img=&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/a24e2d73-ab8b-4b5d-a57d-7e0f2f5f0a66.webp&amp;quot;&lt;/span&gt; %&amp;#125;  &lt;br&gt;![](https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/a24e2d73-ab8b-4b5d-a57d-7e0f2f5f0a66.webp)  &lt;br&gt;&amp;#123;% endrender_caption %&amp;#125;  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;### Mermaid Syntax Issues  &lt;/span&gt;&lt;br&gt;&lt;br&gt;Mermaid diagrams couldn’t be highlighted properly, so I excluded them &lt;span class=&#34;hljs-keyword&#34;&gt;in&lt;/span&gt; Hexo’s config:  &lt;br&gt;&lt;br&gt;```yaml&lt;br&gt;exclude_languages:&lt;br&gt; - mermaid&lt;br&gt;```  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;### Markdown Unable to Use EJS Syntax  &lt;/span&gt;&lt;br&gt;&lt;br&gt;I haven’t addressed this yet—found a workaround instead.  &lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;### Pagination Generation Issues&lt;/span&gt;&lt;br&gt;&lt;br&gt;The `hexo-generator-index` generates pagination &lt;span class=&#34;hljs-keyword&#34;&gt;in&lt;/span&gt; the format `page/2`, `page/3`, whereas Jekyll uses `page2`, `page3`. To address this, I copied the plugin&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;s source code locally and modified it (located at `scripts/pagination.js`):&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;```javascript&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&lt;/span&gt;use strict&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;const pagination = require(&amp;#x27;&lt;/span&gt;hexo-pagination&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;hexo.config.index_generator = Object.assign(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    per_page:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;      typeof hexo.config.per_page === &amp;#x27;&lt;/span&gt;undefined&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27; ? 10 : hexo.config.per_page,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    order_by: &amp;#x27;&lt;/span&gt;-&lt;span class=&#34;hljs-built_in&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  hexo.config.index_generator&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;hexo.extend.generator.register(&amp;#x27;&lt;/span&gt;index&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;, function (locals) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  const config = this.config;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  const posts = locals.posts.sort(config.index_generator.order_by);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  posts.data.sort((a, b) =&amp;gt; (b.sticky || 0) - (a.sticky || 0));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  const paginationDir = config.pagination_dir || &amp;#x27;&lt;/span&gt;page&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  const path = config.index_generator.path || &amp;#x27;&lt;/span&gt;&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  return pagination(path, posts, &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    perPage: config.index_generator.per_page,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    layout: [&amp;#x27;&lt;/span&gt;index&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;, &amp;#x27;&lt;/span&gt;archive&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    format: paginationDir + &amp;#x27;&lt;/span&gt;%d/&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    data: &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;      __index: true,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;  &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;strikethrough-implementation-issue&#34;&gt;Strikethrough Implementation Issue&lt;/h3&gt;
&lt;p&gt;The default strikethrough uses the &lt;code&gt;&amp;lt;s&amp;gt;&lt;/code&gt; tag, but Jekyll uses &lt;code&gt;&amp;lt;del&amp;gt;&lt;/code&gt;. I resolved this by directly replacing it via &lt;code&gt;after_render:html&lt;/code&gt; (located at &lt;code&gt;scripts/tag-del.js&lt;/code&gt;):&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;filter&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;register&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;after_render:html&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; (&lt;span class=&#34;hljs-params&#34;&gt;str&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; str.&lt;span class=&#34;hljs-title function_&#34;&gt;replace&lt;/span&gt;(&lt;span class=&#34;hljs-regexp&#34;&gt;/&amp;lt;s&amp;gt;/g&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;lt;del&amp;gt;&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;replace&lt;/span&gt;(&lt;span class=&#34;hljs-regexp&#34;&gt;/&amp;lt;\/s&amp;gt;/g&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;lt;/del&amp;gt;&amp;#x27;&lt;/span&gt;);&lt;br&gt;&amp;#125;);&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;redundant-files-after-build&#34;&gt;Redundant Files After Build&lt;/h3&gt;
&lt;p&gt;Some files are unnecessary and should be deleted after the build:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/147800e8-7a61-8077-911f-f5d55f484628.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;Specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;categories/*&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;i_dont_wanna_use_default_archives/*&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;i_dont_wanna_use_default_tags/*&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;less/*&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;rss-issue&#34;&gt;RSS Issue&lt;/h3&gt;
&lt;p&gt;I used custom tag styles to render Notion Bookmarks, aiming to match Notion’s aesthetic. However, this caused RSS readers like Reeder to fail in rendering the styles correctly. To fix this, I processed it as follows: in Jekyll, I used template syntax and functions to dynamically replace the custom styles during the build:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-variable language_&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-variable language_&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;BookmarkFilter&lt;/span&gt;&lt;br&gt;      def &lt;span class=&#34;hljs-title function_&#34;&gt;bookmark_filter&lt;/span&gt;(input)&lt;br&gt;        input.&lt;span class=&#34;hljs-title function_&#34;&gt;gsub&lt;/span&gt;(&lt;span class=&#34;hljs-regexp&#34;&gt;/^\&amp;lt;p\&amp;gt;\&amp;lt;a\s+class=\&amp;quot;link-bookmark\&amp;quot;\shref=(.*)\starget=\&amp;quot;_blank\&amp;quot;\&amp;gt;\&amp;lt;span\&amp;gt;(.*)\&amp;lt;\/span\&amp;gt;\&amp;lt;span\&amp;gt;\&amp;lt;span\&amp;gt;(.*)\&amp;lt;\/span\&amp;gt;\&amp;lt;span\&amp;gt;\n(.*)\n\&amp;lt;\/span\&amp;gt;\&amp;lt;span\&amp;gt;(.*)\&amp;lt;\/span\&amp;gt;\&amp;lt;\/span\&amp;gt;\&amp;lt;\/a\&amp;gt;\&amp;lt;\/p\&amp;gt;$/&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;lt;p&amp;gt;&amp;lt;a href=\1 target=&amp;quot;_blank&amp;quot;&amp;gt;\3&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;#x27;&lt;/span&gt;);&lt;br&gt;      end&lt;br&gt;    end&lt;br&gt;  end&lt;br&gt;  &lt;br&gt;  &lt;span class=&#34;hljs-title class_&#34;&gt;Liquid&lt;/span&gt;::&lt;span class=&#34;hljs-title class_&#34;&gt;Template&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;register_filter&lt;/span&gt;(&lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;::&lt;span class=&#34;hljs-title class_&#34;&gt;BookmarkFilter&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;In Hexo, I handled this by patching after the build (located at &lt;code&gt;scripts/rss-gene.js&lt;/code&gt;):&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; fs = &lt;span class=&#34;hljs-built_in&#34;&gt;require&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;fs&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; path = &lt;span class=&#34;hljs-built_in&#34;&gt;require&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;path&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; ejs = &lt;span class=&#34;hljs-built_in&#34;&gt;require&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;ejs&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; rootDate = &lt;span class=&#34;hljs-keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Date&lt;/span&gt;();&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;getDate&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;_date&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; date = _date ? &lt;span class=&#34;hljs-keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Date&lt;/span&gt;(_date) : rootDate;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 获取各个部分&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; days = [&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Sun&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Mon&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Tue&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Wed&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Thu&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Fri&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Sat&amp;#x27;&lt;/span&gt;];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; months = [&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Jan&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Feb&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Mar&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Apr&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;May&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Jun&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Jul&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Aug&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Sep&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Oct&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Nov&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Dec&amp;#x27;&lt;/span&gt;,&lt;br&gt;  ];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; dayName = days[date.&lt;span class=&#34;hljs-title function_&#34;&gt;getDay&lt;/span&gt;()];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; day = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(date.&lt;span class=&#34;hljs-title function_&#34;&gt;getDate&lt;/span&gt;()).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; month = months[date.&lt;span class=&#34;hljs-title function_&#34;&gt;getMonth&lt;/span&gt;()];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; year = date.&lt;span class=&#34;hljs-title function_&#34;&gt;getFullYear&lt;/span&gt;();&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; hours = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(date.&lt;span class=&#34;hljs-title function_&#34;&gt;getHours&lt;/span&gt;()).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; minutes = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(date.&lt;span class=&#34;hljs-title function_&#34;&gt;getMinutes&lt;/span&gt;()).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; seconds = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(date.&lt;span class=&#34;hljs-title function_&#34;&gt;getSeconds&lt;/span&gt;()).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 获取时区偏移（以分钟为单位）&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; timezoneOffset = -date.&lt;span class=&#34;hljs-title function_&#34;&gt;getTimezoneOffset&lt;/span&gt;();&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; sign = timezoneOffset &amp;gt;= &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt; ? &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;+&amp;#x27;&lt;/span&gt; : &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;-&amp;#x27;&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; offsetHours = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(&lt;br&gt;    &lt;span class=&#34;hljs-title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;floor&lt;/span&gt;(&lt;span class=&#34;hljs-title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;abs&lt;/span&gt;(timezoneOffset) / &lt;span class=&#34;hljs-number&#34;&gt;60&lt;/span&gt;)&lt;br&gt;  ).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; offsetMinutes = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(&lt;span class=&#34;hljs-title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;abs&lt;/span&gt;(timezoneOffset) % &lt;span class=&#34;hljs-number&#34;&gt;60&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 格式化为 RFC 2822 格式的字符串&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;dayName&amp;#125;&lt;/span&gt;, &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;day&amp;#125;&lt;/span&gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;month&amp;#125;&lt;/span&gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;year&amp;#125;&lt;/span&gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;hours&amp;#125;&lt;/span&gt;:&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;minutes&amp;#125;&lt;/span&gt;:&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;seconds&amp;#125;&lt;/span&gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;sign&amp;#125;&lt;/span&gt;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;offsetHours&amp;#125;&lt;/span&gt;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;offsetMinutes&amp;#125;&lt;/span&gt;`&lt;/span&gt;;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;generator&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;register&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;xml&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; (&lt;span class=&#34;hljs-params&#34;&gt;locals&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 仿照 Liquid 内置的日期格式写法&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 注意如果前面不加这个 \uFEFF 则不会被识别为 xml&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; template =&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;\uFEFF&amp;#x27;&lt;/span&gt; +&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;`&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;lt;rss version=&amp;quot;2.0&amp;quot; xmlns:atom=&amp;quot;http://www.w3.org/2005/Atom&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;lt;channel&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;title&amp;gt;Xheldon Blog&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;description&amp;gt;The Answer to Life, the Universe and Everything is...&amp;lt;/description&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;link&amp;gt;https://www.xheldon.com&amp;lt;/link&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;atom:link href=&amp;quot;https://www.xheldon.com/feed.xml&amp;quot; rel=&amp;quot;self&amp;quot; type=&amp;quot;application/rss+xml&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;pubDate&amp;gt;&amp;lt;%= getDate() %&amp;gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;lastBuildDate&amp;gt;&amp;lt;%= getDate() %&amp;gt;&amp;lt;/lastBuildDate&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;generator&amp;gt;Hexo v&amp;lt;%= version %&amp;gt;&amp;lt;/generator&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;% for (post of posts.sort((a, b) =&amp;gt; (new Date(b.date).getTime()) - (new Date(a.date).getTime())).slice(0, 10)) &amp;#123; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;item&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;title&amp;gt;&amp;lt;%= post.title %&amp;gt;&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;description&amp;gt;&amp;lt;%= bookmark_filter(post.content) %&amp;gt;&amp;lt;/description&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;pubDate&amp;gt;&amp;lt;%= getDate(post.date) %&amp;gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;link&amp;gt;&amp;lt;%= post.permalink %&amp;gt;&amp;lt;/link&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;guid isPermaLink=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;%= post.permalink %&amp;gt;&amp;lt;/guid&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;% for (tag of post.tags.data) &amp;#123; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;                &amp;lt;category&amp;gt;&amp;lt;%= tag.name %&amp;gt;&amp;lt;/category&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;% &amp;#125; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;% for (cat of post.categories.data) &amp;#123; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;                &amp;lt;category&amp;gt;&amp;lt;%- escape_html(cat.name) %&amp;gt;&amp;lt;/category&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;% &amp;#125; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;/item&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;% &amp;#125; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;lt;/channel&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;lt;/rss&amp;gt;`&lt;/span&gt;;&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; bookmark_filter = hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;helper&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;get&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;bookmark_filter&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;bind&lt;/span&gt;(hexo);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; escape_html = hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;helper&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;get&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;escape_html&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;bind&lt;/span&gt;(hexo);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; data = &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;posts&lt;/span&gt;: locals.&lt;span class=&#34;hljs-property&#34;&gt;posts&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;toArray&lt;/span&gt;(),&lt;br&gt;    getDate,&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;version&lt;/span&gt;: hexo.&lt;span class=&#34;hljs-property&#34;&gt;version&lt;/span&gt;,&lt;br&gt;    escape_html,&lt;br&gt;    bookmark_filter,&lt;br&gt;  &amp;#125;;&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; jsonContent = ejs.&lt;span class=&#34;hljs-title function_&#34;&gt;render&lt;/span&gt;(template, data);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; outputPath = path.&lt;span class=&#34;hljs-title function_&#34;&gt;join&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;source/_posts&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;feed.xml&amp;#x27;&lt;/span&gt;);&lt;br&gt;  fs.&lt;span class=&#34;hljs-title function_&#34;&gt;writeFileSync&lt;/span&gt;(outputPath, jsonContent, &amp;#123; &lt;span class=&#34;hljs-attr&#34;&gt;encoding&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;utf8&amp;#x27;&lt;/span&gt; &amp;#125;);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;path&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;feed.xml&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;data&lt;/span&gt;: jsonContent,&lt;br&gt;  &amp;#125;;&lt;br&gt;&amp;#125;);&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;service-worker-issue&#34;&gt;Service-Worker Issue&lt;/h3&gt;
&lt;p&gt;I removed the service worker because, with every build, the tags section of the pages inevitably changes, causing the HTML pages to update. This frequently required manual page refreshes, which became tedious, so I removed it entirely.&lt;/p&gt;
&lt;h3 id=&#34;other-issues&#34;&gt;Other Issues&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Some necessary files were missing and needed to be added after the build, such as &lt;code&gt;ads.txt&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Articles with &lt;code&gt;layout&lt;/code&gt; values of type &lt;code&gt;post&lt;/code&gt;, where the &lt;code&gt;page.path&lt;/code&gt; value does not start with &lt;code&gt;/&lt;/code&gt;, require special attention.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;final-notes&#34;&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;That’s essentially it. After a line-by-line comparison using BeyondCompare, the migration was optimized to minimize changes.&lt;/p&gt;
</description>
            <pubDate>Tue, 07 Jan 2025 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/tech/jekyll-2-hexo.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/tech/jekyll-2-hexo.html</guid>
            
                <category>使用体验</category>
            
                <category>折腾</category>
            
                <category>总结</category>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>JavaScript</category>
            
                <category>技术</category>
            
                <category>框架</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>Cyberpunk 2024: The Tools I&#39;m Currently Using</title>
            <description>&lt;h2 id=&#34;preface&#34;&gt;Preface&lt;/h2&gt;
&lt;p&gt;As 2024 comes to an end, with my WeChat content boost coupons about to expire, I’ve decided to summarize the various efficiency tools I frequently use in my daily development work this year. These include both software and hardware, and are shared here for reference.&lt;/p&gt;
&lt;p caption=&#39;AI 生成图&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80d5-99b3-da02d3eacde4.webp&#39; alt=&#39;AI 生成图&#39; title=&#39;AI 生成图&#39;&gt;&lt;/p&gt;  
&lt;h2 id=&#34;paid-subscription-software&#34;&gt;Paid Subscription Software&lt;/h2&gt;
&lt;h3 id=&#34;cursor&#34;&gt;Cursor&lt;/h3&gt;
&lt;p&gt;When it comes to AI programming, Cursor is the first to come to mind (note: “first to come to mind” is an idiom meaning the first to be considered or encountered—author’s note).&lt;/p&gt;
&lt;p&gt;In comparisons with Windsurf and GitHub Copilot, its ability to generate new code is roughly on par. However, Cursor’s capability to modify/refactor existing code is a whole dimension ahead of other AI coding assistants. You mostly just need to press the Tab key—it automatically positions the cursor where refactoring/modification is needed. You accept with Tab, and pressing Tab again jumps to the next spot requiring changes. Its Composer/Agent is also a pioneer in AI pair programming, with undeniable prowess.&lt;/p&gt;
&lt;p caption=&#39;使用 Cursor 的日常编程界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8053-865f-cbdc451a31ad.webp&#39; alt=&#39;使用 Cursor 的日常编程界面&#39; title=&#39;使用 Cursor 的日常编程界面&#39;&gt;&lt;/p&gt;  
&lt;h3 id=&#34;notion&#34;&gt;Notion&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; The free version meets my needs, while the paid version is more for collaboration, so I won’t be renewing my subscription next year. &lt;/p&gt;&lt;/blockquote&gt;  
&lt;p&gt;The two most important features I look for in note-taking software are “unrestricted import/export” and “cross-platform compatibility.” Notion delivers on both:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It supports API functionality, so there’s no worry about data being trapped in proprietary formats (looking at you, Evernote and other domestic note-taking apps).&lt;/li&gt;
&lt;li&gt;With its API and the Notion-Flow browser extension, you can write static blogs hosted on GitHub anytime, anywhere.&lt;/li&gt;
&lt;li&gt;Its database module can function as a web database with just a bit of server-side code, as I’ve used on my blog page:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&#34;https://www.xheldon.cn/subscribe/&#34; target=&#34;_blank&#34;&gt; 订阅&amp;付费软件 - Xheldon Blog&lt;/a&gt;&lt;/p&gt;  
&lt;p caption=&#39;Notion Flow 插件界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80ed-b18b-fd360fdc9098.webp&#39; alt=&#39;Notion Flow 插件界面&#39; title=&#39;Notion Flow 插件界面&#39;&gt;&lt;/p&gt;  
&lt;h3 id=&#34;ticktick&#34;&gt;TickTick&lt;/h3&gt;
&lt;p&gt;I previously bought Things outright, but its integration with calendars was clunky. A friend recommended TickTick.&lt;/p&gt;
&lt;p&gt;Its calendar, tasks, and list views are incredibly convenient, and task details support rich text. This is likely why it’s subscription-based rather than a one-time purchase—ongoing server storage costs are involved.&lt;/p&gt;
&lt;p caption=&#39;滴答清单&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-807e-aa46-e617dece267c.webp&#39; alt=&#39;滴答清单&#39; title=&#39;滴答清单&#39;&gt;&lt;/p&gt;  
&lt;h3 id=&#34;1password&#34;&gt;1Password&lt;/h3&gt;
&lt;p&gt;The undisputed leader in password management software, it liberates you from typing passwords in various scenarios. It supports storing API tokens, credit card info, SSH details, recovery codes, and even WiFi credentials.&lt;/p&gt;
&lt;p&gt;Its deep system integration takes “no-password entry” to the next level. On iOS, you’ll need to use the system keyboard to activate it, which is the main reason I avoid third-party keyboards on my phone.&lt;/p&gt;
&lt;p&gt;When it comes to security, I trust commercial software more.&lt;/p&gt;
&lt;p caption=&#39;1Password&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80d7-aea0-cecb9be5a8ab.webp&#39; alt=&#39;1Password&#39; title=&#39;1Password&#39;&gt;&lt;/p&gt;  
&lt;h3 id=&#34;youtube-premium&#34;&gt;YouTube Premium&lt;/h3&gt;
&lt;p&gt;I count this as an efficiency tool because I primarily use it to learn new things. Searching for unfamiliar topics usually yields relevant content—recently for AI, earlier for Swift, and even relationship advice (though from self-proclaimed experts, so take it with a grain of salt). Of course, there’s also low-quality content, so surf wisely.&lt;/p&gt;
&lt;p&gt;As a “perk” of Premium, YouTube Music is included. You can import Apple Music playlists (via third-party services) or upload your own music files. Compared to Apple Music, its advantages include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;YouTube Music won’t replace your uploaded tracks with online library versions.&lt;/li&gt;
&lt;li&gt;It allows up to 100,000 uploaded songs.&lt;/li&gt;
&lt;li&gt;Even if you cancel YouTube Premium, you retain access to your uploaded music, essentially making it a music cloud drive.&lt;/li&gt;
&lt;/ol&gt;
&lt;p caption=&#39;我的 YouTube Music&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/161800e8-7a61-8089-b694-c40de53f4c15.webp&#39; alt=&#39;我的 YouTube Music&#39; title=&#39;我的 YouTube Music&#39;&gt;&lt;/p&gt;  
&lt;p&gt;So, embracing frugality, I canceled Apple Music at the end of 2024 and switched to YouTube Premium.&lt;/p&gt;
&lt;p caption=&#39;我的 YouTube 主页&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15f800e8-7a61-801c-bdb9-dade7616324d.webp&#39; alt=&#39;我的 YouTube 主页&#39; title=&#39;我的 YouTube 主页&#39;&gt;&lt;/p&gt;  
&lt;h2 id=&#34;one-time-purchase-software&#34;&gt;One-Time Purchase Software&lt;/h2&gt;
&lt;h3 id=&#34;popclip&#34;&gt;PopClip&lt;/h3&gt;
&lt;p&gt;Enables right-handed, zero-lag searching, translation, and other operations—no left hand or keyboard needed.&lt;/p&gt;
&lt;p&gt;My workflow involves selecting text to trigger OpenAI Translator, where I’ve predefined tasks (more on this later).&lt;/p&gt;
&lt;p caption=&#39;PopClip 主页&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-809a-b407-fedb0270ab0b.webp&#39; alt=&#39;PopClip 主页&#39; title=&#39;PopClip 主页&#39;&gt;&lt;/p&gt;  
&lt;h3 id=&#34;alfred-5&#34;&gt;Alfred 5&lt;/h3&gt;
&lt;p&gt;Upgraded from the buy-once version 4, mainly for app launching and querying AI via OpenAITranslate. I wrote a custom script for this, activated the same way as above.&lt;/p&gt;
&lt;p caption=&#39;Alfred 主界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8013-ba20-fde096aa3bf9.webp&#39; alt=&#39;Alfred 主界面&#39; title=&#39;Alfred 主界面&#39;&gt;&lt;/p&gt;  
&lt;p caption=&#39;Alfred 自定义脚本界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/161800e8-7a61-800e-a495-cd4e85400abd.webp&#39; alt=&#39;Alfred 自定义脚本界面&#39; title=&#39;Alfred 自定义脚本界面&#39;&gt;&lt;/p&gt;  
&lt;h3 id=&#34;bettermouse&#34;&gt;BetterMouse&lt;/h3&gt;
&lt;p&gt;I’ve been using Logitech gaming mice, which come with G HUB software. However, G HUB has numerous issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only black icons appear in the taskbar, which stand out conspicuously among the white icons.&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;特立独行的 G Hub 软件&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80ea-8bc1-f301acb45cdd.webp&#39; alt=&#39;特立独行的 G Hub 软件&#39; title=&#39;特立独行的 G Hub 软件&#39;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The interaction logic is bizarre. To open the software, I need to click the taskbar and then click “Launch G HUB” to start it, whereas most software opens the main window directly upon clicking and displays an interaction menu only when right-clicked. At the very least, provide an optional setting for users to decide the interaction behavior.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The operation is sluggish. Launching takes half a minute, and occasionally it fails to start, getting stuck on the animation screen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The wording is odd. Please tell me, if I want to save my mouse settings onboard, should I click “Enable” for this option now, or leave it as is?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;离谱的文案描述&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-807e-bc82-e1b1057cf7b7.webp&#39; alt=&#39;离谱的文案描述&#39; title=&#39;离谱的文案描述&#39;&gt;&lt;/p&gt;
&lt;p&gt;Bettermouse, in addition to allowing custom mouse movement speed, also supports advanced interactions like smooth scrolling and right-click drag-and-slide (e.g., in Figma, scrolling requires holding cmd + left-clicking and moving, whereas Bettermouse lets you drag the page by simply right-clicking).&lt;/p&gt;
&lt;p caption=&#39;BetterMouse 软件设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80d2-b103-c9c785b85f43.webp&#39; alt=&#39;BetterMouse 软件设置&#39; title=&#39;BetterMouse 软件设置&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;magnet&#34;&gt;Magnet&lt;/h3&gt;
&lt;p&gt;A window management tool I bought many years ago, installed on one Mac after another with my Apple ID. Adjusting window splits with shortcuts is su~per~ con~ve~nient~. There are many similar tools on the market, but this one is more than enough for me. After multiple version iterations, its features have only grown richer, leaving me with zero motivation to switch.&lt;/p&gt;
&lt;p caption=&#39;Magnet 软件设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8051-81d6-d51835aaeb7b.webp&#39; alt=&#39;Magnet 软件设置&#39; title=&#39;Magnet 软件设置&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;cleanshotx&#34;&gt;CleanshotX&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Major version purchase&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The ultimate screenshot/screen recording tool—I even paid for a major version upgrade once. No need for lengthy introductions. My favorite features: one-click copy to clipboard after screenshotting and one-click pin to screen (most frequently used). The use case? When you can’t remember something at a glance (sorry, my brain’s context capacity declines with age) and need to switch back and forth repeatedly, pinning the screenshot eliminates this hassle.&lt;/p&gt;
&lt;p caption=&#39;置顶截图&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-808f-90e9-c31105514fac.webp&#39; alt=&#39;置顶截图&#39; title=&#39;置顶截图&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;surge-quantumultx&#34;&gt;Surge/QuantumultX&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Major version purchase&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I abandoned ClashX (Little Cat) because its core is no longer updated and joined a 5-person Mac Surge group buy with friends. On my phone, I use QuantumultX.&lt;/p&gt;
&lt;p caption=&#39;使用 Surge 当网关&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80f7-8620-c8391a537a25.webp&#39; alt=&#39;使用 Surge 当网关&#39; title=&#39;使用 Surge 当网关&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;xpic-shameless-plug&#34;&gt;xPic (Shameless Plug)&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; This is a personal recommendation—I developed it, haha&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;At work, I often need to compress and convert images, but online tools either fail to meet expectations, are cumbersome to upload/download, or raise privacy concerns. So, I developed xPic, which supports image/video compression, format conversion, image sequence frame compositing, and video-to-GIF conversion.&lt;/p&gt;
&lt;p&gt;Since SVGA is used in my work, I also added convenient SVGA previews. The app still has bugs and is in beta testing.&lt;/p&gt;
&lt;p caption=&#39;xPic SVGA 预览&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8072-a02d-f69887f17f73.webp&#39; alt=&#39;xPic SVGA 预览&#39; title=&#39;xPic SVGA 预览&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;free-open-source-software&#34;&gt;Free/Open-Source Software&lt;/h2&gt;
&lt;h3 id=&#34;openai-translator&#34;&gt;OpenAI Translator&lt;/h3&gt;
&lt;p&gt;It’s just a translation tool, nothing particularly noteworthy, but it supports PopClip invocation (the principle is sending data to a Unix socket registered by the app with the command: &lt;code&gt;curl -d &amp;quot;$1&amp;quot; --unix-socket /tmp/openai-translator.sock http://openai-translator&lt;/code&gt; to trigger it), enabling quick AI queries with PopClip. The same applies to Alfred.&lt;/p&gt;
&lt;p caption=&#39;OpenAI Translator 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8071-9f06-d2abc7033bb2.webp&#39; alt=&#39;OpenAI Translator 界面&#39; title=&#39;OpenAI Translator 界面&#39;&gt;&lt;/p&gt;
&lt;p&gt;I added a new task:&lt;/p&gt;
&lt;p caption=&#39;OpenAI Translator 设置界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80fe-8c06-fdfbe61d1174.webp&#39; alt=&#39;OpenAI Translator 设置界面&#39; title=&#39;OpenAI Translator 设置界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;wechat-input-method&#34;&gt;WeChat Input Method&lt;/h3&gt;
&lt;p&gt;I don’t use WeChat Input Method on my iPhone—only on my Mac at work/home. The shared word library is excellent. I used Sogou Input before, but whether due to intentional dumbing-down post-Tencent acquisition to push users to switch or other reasons, Sogou often suggests words I’ve never typed instead of the most common ones. For example, when I type &lt;code&gt;zhuomian&lt;/code&gt;, the first candidate is “卓面” (Zhuómian) instead of “桌面” (desktop).&lt;/p&gt;
&lt;p&gt;Another awkward issue: I admit I’ve typed the character “屄” (bī) during keyboard battles. Somehow, Sogou seemed to pick up on this “preference.” Even after deliberately typing other homophones of “bi” multiple times to push “屄” down the candidate list via “frequency adjustment,” it kept failing. Sometimes it would temporarily move to the second page, only to reappear as the top candidate later (I swear I didn’t type it during that time).&lt;/p&gt;
&lt;p&gt;A related problem: no matter when (even right after “training”), if I type &lt;code&gt;vi&lt;/code&gt;, the input method might assume I meant &lt;code&gt;bi&lt;/code&gt; (since the keys for &lt;code&gt;v&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are adjacent, this logic makes sense). At this point, it ignores my manual candidate adjustments for &lt;code&gt;bi&lt;/code&gt; and prioritizes showing “屄,” leading to awkward moments during screen sharing.&lt;/p&gt;
&lt;p&gt;The reason I don’t use it on my iPhone is that only the native keyboard supports 1Password autofill/verification code autofill.&lt;/p&gt;
&lt;p caption=&#39;微信输入法设置界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80e8-9e42-c558615951bf.webp&#39; alt=&#39;微信输入法设置界面&#39; title=&#39;微信输入法设置界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;orbstack&#34;&gt;OrbStack&lt;/h3&gt;
&lt;p&gt;A native Docker/Linux/k8s management tool with an elegant interface, user-friendly operation, and low memory usage. The free version is fully functional. It’s leagues ahead of Docker Desktop. Here’s an official comparison:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.orbstack.dev/compare/docker-desktop&#34; target=&#34;_blank&#34;&gt; OrbStack vs. Docker Desktop · OrbStack Docs&lt;/a&gt;&lt;/p&gt;
&lt;p caption=&#39;OrbStack 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8025-bacc-d810a6a81df6.webp&#39; alt=&#39;OrbStack 界面&#39; title=&#39;OrbStack 界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;immersive-translate&#34;&gt;Immersive Translate&lt;/h3&gt;
&lt;p&gt;An AI translation plugin that allows full-page translation with hotkeys or hover-to-translate selected content during web browsing. Supports custom API integration and YouTube subtitle translation—far superior to Google’s built-in machine translation.&lt;/p&gt;
&lt;p&gt;(PS: Why doesn’t Google prioritize AI-translating all its documentation first instead of sticking with painfully unreadable machine translations despite their advancements in large language models?)&lt;/p&gt;
&lt;p caption=&#39;Immersive translate 官网&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8087-8c69-f9aa44bfb9be.webp&#39; alt=&#39;Immersive translate 官网&#39; title=&#39;Immersive translate 官网&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;warp&#34;&gt;Warp&lt;/h3&gt;
&lt;p&gt;Killer feature: Edit bash commands like text.&lt;/p&gt;
&lt;p&gt;Traditional terminals like iTerm2 impose limitations, such as the inability to use &lt;code&gt;vim&lt;/code&gt; shortcuts. Without this feature, I’d never use Warp due to its excessive additions (AI, collaboration tools, etc.) that are unnecessary for me.&lt;/p&gt;
&lt;p caption=&#39;Wrap 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8003-bec7-c767006042fb.webp&#39; alt=&#39;Wrap 界面&#39; title=&#39;Wrap 界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;syntax-highlight&#34;&gt;Syntax Highlight&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Open-source&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Press spacebar to preview files with extensive format support—extremely convenient.&lt;/p&gt;
&lt;p caption=&#39;Syntax Highlight 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8096-af2a-e71c652a4594.webp&#39; alt=&#39;Syntax Highlight 界面&#39; title=&#39;Syntax Highlight 界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;monitorcontrol&#34;&gt;MonitorControl&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Open-source&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;As the name suggests, a display brightness control tool with hotkey support—very handy.&lt;/p&gt;
&lt;p caption=&#39;MonitorControl 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8089-9377-d223950afe0c.webp&#39; alt=&#39;MonitorControl 界面&#39; title=&#39;MonitorControl 界面&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;cracked-software&#34;&gt;Cracked Software&lt;/h2&gt;
&lt;h3 id=&#34;tableplus&#34;&gt;TablePlus&lt;/h3&gt;
&lt;p&gt;Occasionally used for connecting to server databases (low frequency since I’m not a professional DevOps/backend engineer). Features a sleek interface and simple operation.&lt;/p&gt;
&lt;p caption=&#39;TablePlus 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8039-8cd7-c4d3be294f4a.webp&#39; alt=&#39;TablePlus 界面&#39; title=&#39;TablePlus 界面&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;hardware&#34;&gt;Hardware&lt;/h2&gt;
&lt;h3 id=&#34;g309-mouse&#34;&gt;G309 Mouse&lt;/h3&gt;
&lt;p&gt;Four-word review: Absolutely love it.&lt;/p&gt;
&lt;p&gt;Crisp tactile feedback, dual-mode Bluetooth/wireless (Bluetooth is mediocre), pinpoint wireless accuracy (polling rate), slightly arched back, vertical sides, and included anti-slip grips—everything is perfect.&lt;/p&gt;
&lt;p&gt;Note: Previously used asymmetrical mice (G403 and G502 wired, where right-click sits lower). Switching to the symmetrical G309 required adjusting to level-clicking—initially prone to accidental right-clicks until adapting to lifting fingers slightly.&lt;/p&gt;
&lt;p caption=&#39;小手友好的 G309（虽然我是大手）&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8015-b435-d15e54c1bd29.webp&#39; alt=&#39;小手友好的 G309（虽然我是大手）&#39; title=&#39;小手友好的 G309（虽然我是大手）&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;apple-watch-bands&#34;&gt;Apple Watch Bands&lt;/h3&gt;
&lt;p&gt;Purchased from a mysterious Taobao store claiming to sell genuine unpackaged bands at 1/3 to 1/2 retail price. Arrived indistinguishable from OEM. The store also sells loose “99% new” or refurbished items—likely factory overruns/returns. I opted for boxed versions for resale value, but loose bands are highly rated.&lt;/p&gt;
&lt;p caption=&#39;各种表带&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-806d-a1b5-eb2218516b61.webp&#39; alt=&#39;各种表带&#39; title=&#39;各种表带&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;airpods-4-noise-cancelling&#34;&gt;AirPods 4 (Noise Cancelling)&lt;/h3&gt;
&lt;p&gt;Retired AirPods Pro 2 due to discomfort after nearly two years. The noise cancellation on the 4th gen is noticeably weaker but acceptable. Surprised by the lack of stem-slide volume control—otherwise satisfactory.&lt;/p&gt;
&lt;p caption=&#39;AirPods 4 降噪版&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8069-8274-f67ac1401a27.webp&#39; alt=&#39;AirPods 4 降噪版&#39; title=&#39;AirPods 4 降噪版&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;xiaomi-ih-rice-cooker-s1&#34;&gt;&lt;strong&gt;Xiaomi IH Rice Cooker S1&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Replaced an 8-year-old Midea cooker (still functional). Works well, though shocked by its 1.5-hour congee cooking time.&lt;/p&gt;
&lt;p caption=&#39;小米电饭锅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-804b-9e0a-c48863912c40.webp&#39; alt=&#39;小米电饭锅&#39; title=&#39;小米电饭锅&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;merida-explorer-x-flat-bar-road-bike&#34;&gt;Merida Explorer X Flat-Bar Road Bike&lt;/h3&gt;
&lt;p&gt;Not strictly electronics, but part of a programmer’s lifestyle. Perfect for my 20-minute daily commute.&lt;/p&gt;
&lt;p caption=&#39;探索者 X 在雁栖湖&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80ba-bf2e-e14e392280c4.webp&#39; alt=&#39;探索者 X 在雁栖湖&#39; title=&#39;探索者 X 在雁栖湖&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;afterword&#34;&gt;Afterword&lt;/h2&gt;
&lt;p&gt;Share your favorite tools in the comments!&lt;/p&gt;
</description>
            <pubDate>Thu, 12 Dec 2024 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/life/the-tools-i-used-in-2024.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/life/the-tools-i-used-in-2024.html</guid>
            
                <category>生活</category>
            
                <category>经验</category>
            
                <category>工具</category>
            
                <category>AI</category>
            
            
                <category>life</category>
            
        </item>
        
        <item>
            <title>How to Dial-Up Internet After Disabling DHCP on TP-Link</title>
            <description>&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Since the network is already configured, I&#39;ll use text descriptions instead of images for some pages—it would be too troublesome to reset the network just for writing this blog post. &lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;preface&#34;&gt;Preface&lt;/h2&gt;
&lt;p&gt;My TP-Link XDR-5480 has a peculiar design choice: when you disable the router’s DHCP service, all its ports (including the SPF-converted port) become LAN ports. This makes it impossible to use the modem in bridge mode + router PPPoE dial-up for internet access. Additionally, your LAN port IPs can no longer be manually assigned and will instead be obtained from the upstream router (i.e., the modem).&lt;/p&gt;
&lt;p&gt;Older TP-Link routers didn’t have this issue—you could freely disable DHCP without affecting the WAN port. Perhaps the product manager for newer TP-Link models thought this would “waste” a WAN port, especially since all newer routers support WAN/LAN port mixing, which inadvertently exposed this problem.&lt;/p&gt;
&lt;p&gt;With the recent release of the M4 Mac mini—smaller in size and more energy-efficient—the internet has been swept by the “Mac mini as a soft router + Mac Surge managing the home network” trend. My current network setup has been stable for over a year without any tinkering, but the itch to experiment returned. Then it hit me: my Mac Studio is always running anyway, so why not use it as a soft router like the mini? Plus, the hype around Surge (hereafter referring to Mac Surge) taking over DHCP for seamless whole-house proxy access and “elegant” real-time monitoring of device connections got to me. Embracing the mantra “elegance never goes out of style,” I started brainstorming how to solve this TP-Link issue.&lt;/p&gt;
&lt;p&gt;After scouring the web, I found posts like:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cn.v2ex.com/t/1053641&#34; target=&#34;_blank&#34;&gt; tp link 的路由器如何能在关闭 dhcp 服务的同时让 wan 口能够拨号上网？ - V2EX&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.chiphell.com/forum.php?mod=viewthread&amp;tid=2521336&amp;highlight=&amp;mobile=no&#34; target=&#34;_blank&#34;&gt; 请教各位大佬关于华硕和tplink路由器关闭dhcp设置的问题 - 电脑讨论(新) -  Chiphell - 分享与交流用户体验&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;current-situation&#34;&gt;Current Situation&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Note: Before proceeding, back up your TP-Link (here referring to my XDR-5480 model) router settings to avoid losing configurations if you make a mistake and can’t access the router. Simply export the settings as shown below. Note that the exported config won’t include port or network settings—a thoughtful design by TP-Link’s product team to prevent importing faulty configurations after a reset. Kudos to them! &lt;/p&gt;&lt;/blockquote&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-80e6-9e16-eb66a08e2ff7.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;network-topology-diagram&#34;&gt;Network Topology Diagram:&lt;/h3&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-8003-89a9-e3f05024c8b5.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;goal&#34;&gt;Goal&lt;/h3&gt;
&lt;p&gt;Without changing the current connection method or subnet of any device (to avoid complaints), use Mac Studio’s Surge as the gateway to manage DHCP services.&lt;/p&gt;
&lt;h3 id=&#34;challenges&#34;&gt;Challenges&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;After disabling DHCP on the TP-Link, a pop-up warns that all ports (including the SPF port) will become LAN ports, making it impossible to customize port addresses.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Disabling DHCP on the TP-Link also triggers a notification that all LAN port IPs will be obtained from the upstream router (the modem). While logical for a pure AP, this means Surge can still detect the modem’s DHCP service.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If Mac Surge acts as the gateway, it will start its own DHCP service and requires no other DHCP services on the network to avoid conflicts and disconnections.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Combining points 2 and 3: You’d need to disable DHCP on both the modem and the router. But! Without pre-configuring IP-MAC binding on the modem/router, you’d lose WiFi access to both and be forced to reset them (don’t ask how I know). Thus, the modem’s DHCP cannot be disabled.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;solution&#34;&gt;Solution&lt;/h2&gt;
&lt;p&gt;Given the above goals and challenges, “Friends, take notes—here’s my deployment plan”:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-80ce-8790-d3cef3ef7d8f.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;step-1-power-off-the-modem&#34;&gt;Step 1: Power Off the Modem&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; Technically, just disconnecting the modem from the TP-Link suffices. I suggest powering it off to prevent accidentally connecting to the modem’s WiFi. If you’ve forgotten the modem’s WiFi network, simply disconnecting the modem from the TP-Link is enough. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Let’s not configure the optical modem first to prevent its DHCP service from being detected by Surge, which would otherwise prevent Surge from enabling its own DHCP service. Additionally, since the connection between the optical modem and the TP-Link router has already been disabled, when we proceed to turn off the TP-Link’s DHCP service in the next step, the TP-Link won’t automatically detect the DHCP service of the upstream router and change its own LAN port IP anymore.&lt;/p&gt;
&lt;h3 id=&#34;step-2-disable-the-router’s-dhcp-service&#34;&gt;Step 2: Disable the Router’s DHCP Service&lt;/h3&gt;
&lt;p&gt;Simply turn it off in the settings. Here, we select “Disable.” After the setup is successful, we’ll change it back to “Auto” later. The reason for switching to “Auto” is for network robustness, which I’ll explain further on.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-804e-a69d-edb6a6eaabc1.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;step-3-enable-surge-dhcp&#34;&gt;Step 3: Enable Surge DHCP&lt;/h3&gt;
&lt;p&gt;This step can be accessed from either the “Overview” or “Devices” tab in Surge:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-8091-83b9-e144224f35d5.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;It will prompt you that Surge will change your Mac’s IP address to manual and fixed. In my case, it’s &lt;code&gt;192.168.5.100&lt;/code&gt;. There are plenty of tutorials online for this, and since you’ve made it this far, you likely know how to set it up, so I won’t elaborate here.&lt;/p&gt;
&lt;p&gt;One thing to note is that it will display the router’s address as &lt;code&gt;192.168.5.1&lt;/code&gt;, with the address pool ranging from &lt;code&gt;192.168.5.100&lt;/code&gt; to &lt;code&gt;192.168.5.200&lt;/code&gt;. I expanded the address pool to &lt;code&gt;192.168.5.2&lt;/code&gt; to &lt;code&gt;192.168.5.254&lt;/code&gt; for a broader range.&lt;/p&gt;
&lt;h3 id=&#34;step-4-find-the-new-router-address&#34;&gt;Step 4: Find the New Router Address&lt;/h3&gt;
&lt;p&gt;After setting up Surge, if you try to access the router at &lt;code&gt;192.168.5.1&lt;/code&gt;, you’ll find it’s no longer reachable! Don’t panic—because your Mac Studio is now the DHCP server, it will “somehow” assign a new IP address to your TP-Link router (why this happens, I have no idea). Click on “Devices,” look for the one labeled “Hostname Unsuitable for Printing” (it might not be exactly this), and access it via the address bar. You’ll see the familiar router interface again! In my case, the new router IP address is &lt;code&gt;192.168.5.114&lt;/code&gt;.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-809d-894e-fbe831126b06.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;step-5-modify-the-router’s-lan-port-and-enable-pppoe&#34;&gt;Step 5: Modify the Router’s LAN Port and Enable PPPoE&lt;/h3&gt;
&lt;p&gt;At this point, you won’t have internet access because your optical modem isn’t even powered on! Now you can power on the optical modem, wait a few minutes, and you’ll still find that clicking &lt;code&gt;Wan 口已经断开连接&lt;/code&gt; in the router settings does nothing.&lt;/p&gt;
&lt;p&gt;But don’t give up! Think about it: isn’t your Studio’s role now similar to when you were tinkering with a soft router? Back then, the optical modem handled the PPPoE dial-up, while the soft router was plugged into the main router or optical modem and managed the DHCP service. Now, my friend, you’ve moved the dial-up device to the router, and your Studio is connected to the main router, effectively turning your Studio into a soft router that handles the DHCP service itself.&lt;/p&gt;
&lt;p&gt;The key step here is to manually set the TP-Link’s LAN port IP address to &lt;code&gt;192.168.5.1&lt;/code&gt; in the settings (otherwise, there won’t be any device on the network with the &lt;code&gt;192.168.5.1&lt;/code&gt; IP address)!&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-80e0-8bb3-f208b1f9c874.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;After manually setting the LAN port IP (even though it’s called a LAN port, it’s connected to the optical modem and can handle PPPoE dial-up!), go back to the internet settings and click “Connect.” You might need to click it 4 or 5 times because the first dial-up attempt can take a while, but it will eventually succeed.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-8092-a5bc-cc3e8f728305.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;step-6-final-steps&#34;&gt;Step 6: Final Steps&lt;/h3&gt;
&lt;p&gt;At this point, your setup is complete. Now, you need to right-click on connected devices, select “Set Surge as their gateway,” and then reconnect the devices to the network:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-8044-b913-da4aafc3219f.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;You can also enable “Set Surge as the default gateway for new devices”:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-80fc-9142-f5520e0a2756.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;Now, all your devices will treat Surge as the gateway, allowing you to monitor their real-time traffic.&lt;/p&gt;
&lt;p&gt;Remember how I mentioned in Step 2 that we disabled the TP-Link’s DHCP service? Now you can go back to the TP-Link and change the DHCP service to “Auto.” This is a fail-safe: &lt;strong&gt;if Surge’s DHCP service crashes or your Mac fails, you and your household devices can still access the internet, and you, my friend, won’t get yelled at!&lt;/strong&gt; This is because the TP-Link’s “Auto” setting will detect the LAN’s DHCP service—if none exists, it will enable its own DHCP; if one exists, it will disable it:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-805c-98c5-e3895ef7dfe0.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;Note: If you click “Disable” and save, your router’s address will revert to &lt;code&gt;192.168.5.114&lt;/code&gt;, and you won’t be able to modify it (because you’ve connected the optical modem and disabled DHCP, triggering the issues mentioned earlier in the tricky parts). In this case, you’ll need to start over from the beginning (by powering off the optical modem).&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That’s it! Along the way, I encountered some odd issues and made a few failed attempts—like why I thought of powering off the optical modem first, setting up the router, and then turning it back on.&lt;/p&gt;
&lt;p&gt;Wishing you all WiFi freedom!&lt;/p&gt;
</description>
            <pubDate>Mon, 04 Nov 2024 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/tech/how-to-pppoe-after-close-tp-link-dhcp.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/tech/how-to-pppoe-after-close-tp-link-dhcp.html</guid>
            
                <category>折腾</category>
            
                <category>软路由</category>
            
                <category>网络</category>
            
                <category>路由器</category>
            
                <category>技巧</category>
            
                <category>技术</category>
            
                <category>旁路由</category>
            
                <category>千兆</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>How to Use Notion Flow Blocks for Conversion</title>
            <description>&lt;p&gt;A week ago, I built the Notion Flow browser extension:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/_Xheldon/status/1770466495560294583&#34; target=&#34;_blank&#34;&gt; Xheldon on Twitter / X&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And now, the freshly updated 0.4.1 version:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://notion-flow.xheldon.com/blog/2024/03/31/0.4.1&#34; target=&#34;_blank&#34;&gt; 0.4.1 | Notion Flow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Supports self-hosted OSS services compatible with the AWS S3 API, such as Cloudflare R2:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.cloudflare.com/zh-cn/developer-platform/r2/&#34; target=&#34;_blank&#34;&gt; Cloudflare R2 | 零出口费用分布式对象存储 | Cloudflare | Cloudflare&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This post briefly explains how I use this browser extension for my Github Jekyll blog.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Jekyll static blogs are built on Ruby and support plugins. So I wrote a few plugins myself (Jekyll blog plugins are located in the &lt;code&gt;_plugins&lt;/code&gt; directory—just drop the Ruby file there and restart the service) to handle Liquid templating, with content sourced from Notion via Notion Flow conversion. For example, here’s the plugin code for processing bookmarks:&lt;/p&gt;


&lt;figure class=&#34;highlight ruby&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs ruby&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;RenderBookMarkBlock&lt;/span&gt; &amp;lt; &lt;span class=&#34;hljs-title class_ inherited__&#34;&gt;Liquid::Block&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;initialize&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;tag_name, attr, tokens&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-comment&#34;&gt;# 普通的链接没有 yid 和 bid&lt;/span&gt;&lt;br&gt;            attrs = attr.scan(&lt;span class=&#34;hljs-regexp&#34;&gt;/url\=\&amp;quot;(.*)\&amp;quot;\stitle\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;\syid\=\&amp;quot;(.*)\&amp;quot;\sbid\=\&amp;quot;(.*)\&amp;quot;/&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; !attrs.empty?&lt;br&gt;              &lt;span class=&#34;hljs-comment&#34;&gt;# 外部的 video 链接，youtube、bilibili（如本文上一篇博客就是）&lt;/span&gt;&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;3&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;4&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@firstChar&lt;/span&gt; = (&lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;)[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;].upcase&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@error&lt;/span&gt; = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt;&lt;br&gt;             &lt;span class=&#34;hljs-comment&#34;&gt;# 正常和 notion 一样的 bookmark（如本文上面三个链接就是）&lt;/span&gt;&lt;br&gt;                attrs = attr.scan(&lt;span class=&#34;hljs-regexp&#34;&gt;/url\=\&amp;quot;(.*)\&amp;quot;\stitle\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;/&lt;/span&gt;)&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@firstChar&lt;/span&gt; = (&lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;)[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;].upcase&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@error&lt;/span&gt; = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;render&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;context&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable&#34;&gt;@desc&lt;/span&gt; = &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; !&lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt;.&lt;span class=&#34;hljs-literal&#34;&gt;nil&lt;/span&gt;? &amp;amp;&amp;amp; !&lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt;.empty?&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;https://www.youtube.com/embed/&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt;&amp;#125;&lt;/span&gt;?rel=0&amp;#x27; title=&amp;#x27;YouTube video player&amp;#x27; frameborder=&amp;#x27;0&amp;#x27; allow=&amp;#x27;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;elsif&lt;/span&gt; !&lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt;.&lt;span class=&#34;hljs-literal&#34;&gt;nil&lt;/span&gt;? &amp;amp;&amp;amp; !&lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt;.empty?&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27; style=&amp;#x27;border-bottom: 1px solid #ddd;&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;//player.bilibili.com/player.html?bvid=&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;amp;high_quality=1&amp;amp;as_wide=1&amp;#x27; scrolling=&amp;#x27;no&amp;#x27; border=&amp;#x27;0&amp;#x27; frameborder=&amp;#x27;no&amp;#x27; framespacing=&amp;#x27;0&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt;&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p&amp;gt;&amp;lt;a class=&amp;#x27;link-bookmark&amp;#x27; href=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; target=&amp;#x27;_blank&amp;#x27;&amp;gt;&amp;lt;span data-bookmark-img=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; data-bookmark-title=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@firstChar&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27;&amp;gt;&amp;lt;img src=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27;/&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&amp;lt;span&amp;gt;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@desc&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;# 上传的 video&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;RenderVideoBlock&lt;/span&gt; &amp;lt; &lt;span class=&#34;hljs-title class_ inherited__&#34;&gt;Liquid::Block&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;initialize&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;tag_name, attr, tokens&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            attrs = attr.scan(&lt;span class=&#34;hljs-regexp&#34;&gt;/caption\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;\ssuffix\=\&amp;quot;(.*)\&amp;quot;/&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable&#34;&gt;@caption&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;]&lt;br&gt;            &lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;]&lt;br&gt;            &lt;span class=&#34;hljs-variable&#34;&gt;@suffix&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;]&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;render&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;context&lt;/span&gt;)&lt;br&gt;            text = &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p caption=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@caption&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27;&amp;gt;&amp;lt;video controls muted&amp;gt;&amp;lt;source src=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; type=&amp;#x27;video/&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@suffix&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; /&amp;gt;&amp;lt;/video&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-title class_&#34;&gt;Liquid&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:Template&lt;/span&gt;.register_tag(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;render_bookmark&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:RenderBookMarkBlock&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-title class_&#34;&gt;Liquid&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:Template&lt;/span&gt;.register_tag(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;render_video&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:RenderVideoBlock&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;p&gt;The logic here is: if a Notion bookmark module link points to YouTube or Bilibili, it’s converted to embedded video HTML (iframe); otherwise, it’s transformed into HTML resembling Notion bookmarks (requiring CSS for styling).&lt;/p&gt;
&lt;p&gt;Thus, while using Notion Flow to convert Notion content into Markdown format, I customized the conversion rules for modules like bookmarks to ensure the blog displays YouTube, Bilibili, and Notion-style bookmark content, as shown here:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-attr&#34;&gt;video&lt;/span&gt;: &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;video&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;block&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (block.&lt;span class=&#34;hljs-property&#34;&gt;type&lt;/span&gt; === &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;file&amp;#x27;&lt;/span&gt;) &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-comment&#34;&gt;// 用户自己上传的 video 文件，用默认 video 插件处理&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&amp;#123;% render_video caption=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.caption&amp;#125;&lt;/span&gt;&amp;quot; img=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.url&amp;#125;&lt;/span&gt;&amp;quot; suffix=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.suffix&amp;#125;&lt;/span&gt;&amp;quot; %&amp;#125;\n![&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.caption&amp;#125;&lt;/span&gt;](&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.url&amp;#125;&lt;/span&gt;)\n&amp;#123;% endrender_video %&amp;#125;\n`&lt;/span&gt;;&lt;br&gt;  &amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (block.&lt;span class=&#34;hljs-property&#34;&gt;type&lt;/span&gt; === &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;external&amp;#x27;&lt;/span&gt;) &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-comment&#34;&gt;// 外部链接、youtube 和 bilibili 视频链接，用 bookmark 处理&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&amp;#123;% render_bookmark url=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.url&amp;#125;&lt;/span&gt;&amp;quot; title=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;      block.caption || &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;#125;&lt;/span&gt;&amp;quot; img=&amp;quot;&amp;quot; yid=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.yid&amp;#125;&lt;/span&gt;&amp;quot; bid=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;      block.bid&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;#125;&lt;/span&gt;&amp;quot; %&amp;#125;&amp;#123;% endrender_bookmark %&amp;#125;\n`&lt;/span&gt;;&lt;br&gt;  &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;One thing to note (I’m not deeply familiar with Ruby): Liquid template tags must enclose text content (even if unused), or the Ruby plugin won’t generate HTML. For example:&lt;/p&gt;


&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&amp;#123;% render_video  %&amp;#125;这里必须有任意内容！&amp;#123;% endrender_video %&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;p&gt;In the Ruby plugin, the &lt;code&gt;super&lt;/code&gt; variable will receive the phrase “There must be some content here!” (you may choose not to use this variable). If this content is absent, the plugin will not return anything at all.&lt;/p&gt;
</description>
            <pubDate>Sun, 31 Mar 2024 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/tech/how-i-use-notion-flow.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/tech/how-i-use-notion-flow.html</guid>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>工作流</category>
            
                <category>技术</category>
            
                <category>Jekyll</category>
            
                <category>Notion</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>【Video】Streamline Your Blog Publishing Process with Notion Flow</title>
            <description>&lt;p&gt;This article shares a video on using Notion Flow to streamline your blog publishing process. For the official website, see:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://notion-flow.xheldon.com&#34; target=&#34;_blank&#34;&gt; Notion Flow | Notion Flow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;YouTube:&lt;/p&gt;
&lt;p class=&#39;embed-responsive embed-responsive-16by9&#39;&gt;&lt;iframe src=&#39;https://www.youtube.com/embed/aPitTcsruhM?rel=0&#39; title=&#39;YouTube video player&#39; frameborder=&#39;0&#39; allow=&#39;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&#39; allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;Bilibili:&lt;/p&gt;
&lt;p class=&#39;embed-responsive embed-responsive-16by9&#39; style=&#39;border-bottom: 1px solid #ddd;&#39;&gt;&lt;iframe src=&#39;//player.bilibili.com/player.html?bvid=BV1Ar421h7tM&amp;high_quality=1&amp;as_wide=1&#39; scrolling=&#39;no&#39; border=&#39;0&#39; frameborder=&#39;no&#39; framespacing=&#39;0&#39; allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;</description>
            <pubDate>Thu, 21 Mar 2024 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/tech/use-notion-flow.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/tech/use-notion-flow.html</guid>
            
                <category>折腾</category>
            
                <category>教程</category>
            
                <category>工作流</category>
            
                <category>技术</category>
            
                <category>插件</category>
            
                <category>视频</category>
            
                <category>Notion</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>Settings to Make VSCode More Efficient – From a Front-End Development Perspective</title>
            <description>&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(241, 241, 239); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;☝🏻&lt;/span&gt;&lt;span&gt;I plan to create a video tutorial later because some settings&#39; effects require demonstration to show the differences, and I&#39;m too lazy to make animated GIFs for the blog. &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&#34;preface&#34;&gt;Preface&lt;/h2&gt;
&lt;p&gt;When I first started learning front-end development, VSCode didn’t exist yet—I was using WebStorm (pirated version as a student back then). The out-of-the-box experience was incredibly satisfying. Later, due to the declining performance of my office computer, WebStorm’s persistent lag issues with 4K and multi-monitor setups, influence from colleagues, and the final blow being “configuration sync,” I eventually switched to VSCode.&lt;/p&gt;
&lt;p&gt;After adapting to the absence of a dedicated floating search box (an epic-level disconnect), I quickly figured out configurations that work well for me. Below, I’ll detail them. If anyone thinks their setup is better, feel free to leave a comment with reasons and screenshots.&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(241, 241, 239); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;📖&lt;/span&gt;&lt;span&gt;I won&#39;t cover default settings much (unless they&#39;re exceptionally good), focusing instead on my modifications. Most configurations in VSCode can be tweaked—even settings like &#34;whether to show the cursor line in the minimap area&#34;—which is fantastic. &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&#34;style&#34;&gt;Style&lt;/h2&gt;
&lt;h3 id=&#34;theme-font&#34;&gt;Theme/Font&lt;/h3&gt;
&lt;p&gt;Theme: One Dark Pro:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=zhuangtongfa.Material-theme&#34; target=&#34;_blank&#34;&gt; marketplace.visualstudio.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Font: Fira Code:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/tonsky/FiraCode?tab=readme-ov-file#download--install&#34; target=&#34;_blank&#34;&gt; GitHub - tonsky/FiraCode: Free monospaced font with programming ligatures&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Fira Code is the officially recommended font, &lt;a href=&#34;https://code.visualstudio.com/docs/getstarted/tips-and-tricks#:~:text=zoomLevel%22%3A%205-,Font%20ligatures,-%22editor.fontFamily%22&#34;&gt;used internally as well&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Fira Code’s variant support for certain symbols is visually pleasing, such as &lt;code&gt;===&lt;/code&gt; and &lt;code&gt;&amp;lt;=&lt;/code&gt; (some require manual activation of character sets and variants):&lt;/p&gt;
&lt;p caption=&#39;Fira Code 字体&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/3371f847-fdd8-410c-82e1-b1d63cd91035.webp&#39; alt=&#39;Fira Code 字体&#39; title=&#39;Fira Code 字体&#39;&gt;&lt;/p&gt;
&lt;p&gt;Many dislike Fira Code’s default &lt;code&gt;&amp;amp;&lt;/code&gt; symbol, but this can be disabled via configuration. Refer to its GitHub documentation for details. My setting:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs json&#34;&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;workbench.colorTheme&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;One Dark Pro&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;editor.fontFamily&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;#x27;Fira Code&amp;#x27;, Monaco, &amp;#x27;Courier New&amp;#x27;, monospace&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;editor.fontLigatures&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;#x27;ss01&amp;#x27;, &amp;#x27;ss02&amp;#x27; off, &amp;#x27;ss03&amp;#x27;, &amp;#x27;ss04&amp;#x27;, &amp;#x27;ss05&amp;#x27;, &amp;#x27;ss07&amp;#x27;, &amp;#x27;cv29&amp;#x27;, &amp;#x27;cv28&amp;#x27;, &amp;#x27;cv13&amp;#x27;&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Additionally, line height is 1.4, and font size is 13.&lt;/p&gt;
&lt;h2 id=&#34;editor&#34;&gt;Editor&lt;/h2&gt;
&lt;p&gt;The most crucial part is editor settings—good configurations boost coding efficiency. Let’s break it down.&lt;/p&gt;
&lt;h3 id=&#34;render-whitespace-characters&#34;&gt;Render Whitespace Characters&lt;/h3&gt;
&lt;p caption=&#39;Editor: Render Whitespace&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/9175365e-0357-4b2f-abf7-cdf20062b2ca.webp&#39; alt=&#39;Editor: Render Whitespace&#39; title=&#39;Editor: Render Whitespace&#39;&gt;&lt;/p&gt;
&lt;p&gt;I use the default “selection” setting: whitespace characters (like spaces) only appear when selected, otherwise hidden to avoid visual clutter. &lt;code&gt;boundary&lt;/code&gt;’s setting forces them to always show, which looks messy:&lt;/p&gt;
&lt;p caption=&#39;选区渲染空白符号&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/12c4e471-e6fc-4b6d-aed7-61d4db16cd18.webp&#39; alt=&#39;选区渲染空白符号&#39; title=&#39;选区渲染空白符号&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;auto-pairing-deleting-brackets&#34;&gt;Auto-Pairing/Deleting Brackets&lt;/h3&gt;
&lt;p caption=&#39;Auto Closing 设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/49d09c46-5456-441a-9f72-fccc3a5d761e.webp&#39; alt=&#39;Auto Closing 设置&#39; title=&#39;Auto Closing 设置&#39;&gt;&lt;/p&gt;
&lt;p&gt;These settings handle bracket pairing. For example, typing an opening bracket like &lt;code&gt;(&amp;#123;[&lt;/code&gt; automatically adds a closing &lt;code&gt;)&amp;#125;]&lt;/code&gt;. The deletion logic works similarly. By default, pairing occurs during both insertion and deletion.&lt;/p&gt;
&lt;h3 id=&#34;bracket-coloring-pool&#34;&gt;Bracket Coloring (Pool)&lt;/h3&gt;
&lt;p caption=&#39;Bracket Pair Colorization&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/899eeaba-8737-4f07-8e22-9480f915fcbc.webp&#39; alt=&#39;Bracket Pair Colorization&#39; title=&#39;Bracket Pair Colorization&#39;&gt;&lt;/p&gt;
&lt;p&gt;The first setting adds colors to brackets (instead of plain white). The second assigns each bracket type its own color scheme (though duplicates may occur—my understanding is that coloring follows bracket type order rather than display sequence).&lt;/p&gt;
&lt;h3 id=&#34;rectangular-selection&#34;&gt;Rectangular Selection&lt;/h3&gt;
&lt;p caption=&#39;Column Selection&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/c64604e0-b420-4845-86e2-bac8d40aaa3d.webp&#39; alt=&#39;Column Selection&#39; title=&#39;Column Selection&#39;&gt;&lt;/p&gt;
&lt;p&gt;By default, vertical selection from top to bottom highlights entire lines if spanning line starts/ends:&lt;/p&gt;
&lt;p caption=&#39;默认选中效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/d97e73d0-91ae-4e0a-befb-c708ceae1bc0.webp&#39; alt=&#39;默认选中效果&#39; title=&#39;默认选中效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;Enabling this turns selection into a rectangular area (based on mouse position, not code structure). Useful for editing similarly indented lines, like JSON keys:&lt;/p&gt;
&lt;p caption=&#39;列选择&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/99dcc5e1-65bd-4048-bf8a-b06443bc7745.webp&#39; alt=&#39;列选择&#39; title=&#39;列选择&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;列选择的一个应用场景&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/85a0ac88-34a6-4843-adfd-ea95f01c1806.gif&#39; alt=&#39;列选择的一个应用场景&#39; title=&#39;列选择的一个应用场景&#39;&gt;&lt;/p&gt;
&lt;p&gt;Side note: Holding Opt during terminal selection achieves the same effect.&lt;/p&gt;
&lt;h3 id=&#34;copy-with-syntax-highlighting&#34;&gt;Copy with Syntax Highlighting&lt;/h3&gt;
&lt;p caption=&#39;Copy With Syntax Highlighting&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/2e507e99-524a-41c0-978c-42cb1bcbebca.webp&#39; alt=&#39;Copy With Syntax Highlighting&#39; title=&#39;Copy With Syntax Highlighting&#39;&gt;&lt;/p&gt;
&lt;p&gt;Some rich-text editors don’t handle this well—copying code from VSCode might carry over colors unintentionally. This setting ensures copied content is plain.&lt;/p&gt;
&lt;h3 id=&#34;drag-and-drop&#34;&gt;Drag-and-Drop&lt;/h3&gt;
&lt;p caption=&#39;Drag And Drop&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/319dae0d-0369-4469-abd9-ed2e0c36649c.webp&#39; alt=&#39;Drag And Drop&#39; title=&#39;Drag And Drop&#39;&gt;&lt;/p&gt;
&lt;p&gt;In years of coding, I’ve rarely used drag-and-drop to move code blocks, so I recommend disabling it. The second setting: holding Shift while dragging media files into VSCode only shows filenames; releasing without Shift opens the files—a handy fallback (enabled by default).&lt;/p&gt;
&lt;h3 id=&#34;copy-current-line-on-empty-selection&#34;&gt;Copy Current Line on Empty Selection&lt;/h3&gt;
&lt;p caption=&#39;Empty Selection Clipboard&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/32f95652-6320-4347-8c9e-b7f03dbecd79.webp&#39; alt=&#39;Empty Selection Clipboard&#39; title=&#39;Empty Selection Clipboard&#39;&gt;&lt;/p&gt;
&lt;p&gt;If nothing is selected (just a cursor), copying grabs the entire current line—simpler workflow (enabled by default).&lt;/p&gt;
&lt;h3 id=&#34;auto-folding&#34;&gt;Auto-Folding&lt;/h3&gt;
&lt;p caption=&#39;Folding&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/96927f79-74ed-4949-b3e0-7cff488269ed.webp&#39; alt=&#39;Folding&#39; title=&#39;Folding&#39;&gt;&lt;/p&gt;
&lt;p&gt;Code folding is definitely necessary. Highlighting the folded range is also needed (it will follow the mouse hover effect with line-by-line highlighting of the current line), otherwise, it’s unclear whether the current line is folded. The last feature, auto-folding the import section, seems unnecessary to me.&lt;/p&gt;
&lt;p&gt;For folding, I personally prefer it to always be visible because this feature is used so frequently. I often need to hover over the area to see which lines are folded before clicking to unfold them, which is inefficient. I like to see at a glance where the folds are, so I set it to “always”:&lt;/p&gt;
&lt;p caption=&#39;Show Folding Controls&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/0a382e11-522d-490a-a086-703b291ef90e.webp&#39; alt=&#39;Show Folding Controls&#39; title=&#39;Show Folding Controls&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;bracket-indentation-guides&#34;&gt;Bracket/Indentation Guides&lt;/h3&gt;
&lt;p caption=&#39;（缩进/括号）参考线&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b537a5b7-b989-4346-837b-919a4705599c.webp&#39; alt=&#39;（缩进/括号）参考线&#39; title=&#39;（缩进/括号）参考线&#39;&gt;&lt;/p&gt;
&lt;p&gt;As shown in the image below, though I haven’t figured out what “indentation guides” are—I’ll enable them for now.&lt;/p&gt;
&lt;p caption=&#39;图中高亮的括号&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/f8b0495e-2bd6-4dd5-9eb1-36a33821f1e8.webp&#39; alt=&#39;图中高亮的括号&#39; title=&#39;图中高亮的括号&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;hover-tooltip-position&#34;&gt;Hover Tooltip Position&lt;/h3&gt;
&lt;p caption=&#39;Hover 位置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/da26e746-9e8b-4a3a-9b24-2a38abae431d.webp&#39; alt=&#39;Hover 位置&#39; title=&#39;Hover 位置&#39;&gt;&lt;/p&gt;
&lt;p&gt;Generally, we read code from top to bottom. With this setting, hovering over code makes the tooltip appear above, blocking the content. You then have to move the mouse to dismiss it and hover again to see it. I recommend disabling this.&lt;/p&gt;
&lt;p caption=&#39;始终显示提示在下方更合适&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/6df3c907-16b9-4cba-b7f3-daa4a5fd4532.webp&#39; alt=&#39;始终显示提示在下方更合适&#39; title=&#39;始终显示提示在下方更合适&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;hover-tooltip-delay&#34;&gt;Hover Tooltip Delay&lt;/h3&gt;
&lt;p caption=&#39;消失延迟其实不需要&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/6b18c5da-1193-4e89-b49f-b7775dcdb192.webp&#39; alt=&#39;消失延迟其实不需要&#39; title=&#39;消失延迟其实不需要&#39;&gt;&lt;/p&gt;
&lt;p&gt;Moving the mouse away usually means you don’t want it to display anymore, so set it directly to 0.&lt;/p&gt;
&lt;h3 id=&#34;mouse-wheel-font-scaling&#34;&gt;Mouse Wheel Font Scaling&lt;/h3&gt;
&lt;p caption=&#39;完全没用的功能…&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/2e05860a-01cb-45ff-9d3b-230d8871ebd6.webp&#39; alt=&#39;完全没用的功能…&#39; title=&#39;完全没用的功能…&#39;&gt;&lt;/p&gt;
&lt;p&gt;Frequently triggered accidentally—turn it off.&lt;/p&gt;
&lt;h3 id=&#34;editor-top-padding&#34;&gt;Editor Top Padding&lt;/h3&gt;
&lt;p caption=&#39;统一视觉间隔&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/07183c4a-3dcc-412d-a7bb-f5167443874d.webp&#39; alt=&#39;统一视觉间隔&#39; title=&#39;统一视觉间隔&#39;&gt;&lt;/p&gt;
&lt;p&gt;I set it to 2. Bottom padding isn’t necessary.&lt;/p&gt;
&lt;p caption=&#39;优雅，永不过时&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/9f1cd71d-f579-44c6-8b23-58f558dfeeaf.webp&#39; alt=&#39;优雅，永不过时&#39; title=&#39;优雅，永不过时&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;scrollbars&#34;&gt;Scrollbars&lt;/h3&gt;
&lt;p&gt;Horizontal scrollbar width: 6; vertical: 25 (default is 12 horizontal, 14 vertical):&lt;/p&gt;
&lt;p caption=&#39;Scrollbar&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/2b7e8382-836f-4fe4-a7b5-c88e24332838.webp&#39; alt=&#39;Scrollbar&#39; title=&#39;Scrollbar&#39;&gt;&lt;/p&gt;
&lt;p&gt;Personally, I dislike scrolling beyond the content range, as it causes scrollbars to appear even when the content fits entirely on one screen. So, I disable “Scroll Beyond Last Line.”&lt;/p&gt;
&lt;p caption=&#39;滚动条显示信息&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/361fa784-0d98-4055-a56e-58678f0c3a31.webp&#39; alt=&#39;滚动条显示信息&#39; title=&#39;滚动条显示信息&#39;&gt;&lt;/p&gt;
&lt;p&gt;Here’s why I increased the vertical scrollbar width to 20: that area isn’t just for scrolling—it also displays three types of information:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Bright yellow on the right side of the scrollbar indicates editor warnings.&lt;/li&gt;
&lt;li&gt;Dark yellow blocks in the middle represent matched search terms (including global and editor searches). These blocks can also be gray (for cursor-selected content and similar matches) or light pink (for declarations of the selected content).&lt;/li&gt;
&lt;li&gt;A blue line spanning the entire scrollbar row marks the cursor’s current line.&lt;/li&gt;
&lt;li&gt;Green sections on the left side of the scrollbar show code changes. If Git is enabled, these may appear as light yellow for modified sections.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Given the richness of this information display, a wider scrollbar is justified.&lt;/p&gt;
&lt;h3 id=&#34;smooth-scrolling&#34;&gt;Smooth Scrolling&lt;/h3&gt;
&lt;p caption=&#39;动画，优雅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/190452c5-f985-47b4-b238-000edce28c4b.webp&#39; alt=&#39;动画，优雅&#39; title=&#39;动画，优雅&#39;&gt;&lt;/p&gt;
&lt;p&gt;Highly recommended. It lets you track how many lines you’ve scrolled instead of abruptly jumping to a new position and losing your place.&lt;/p&gt;
&lt;h3 id=&#34;scroll-sticky-headers&#34;&gt;Scroll Sticky Headers&lt;/h3&gt;
&lt;p caption=&#39;吸顶，好用&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/37741a88-7ba7-481a-9fe5-06ed276af9d3.webp&#39; alt=&#39;吸顶，好用&#39; title=&#39;吸顶，好用&#39;&gt;&lt;/p&gt;
&lt;p&gt;When scrolling, you might need to check content outside the current screen’s scope—enable this option. However, horizontal scrolling can move sticky functions out of view. I prefer to keep them fixed, so I disable the last option.&lt;/p&gt;
&lt;p caption=&#39;左右滚动不跟随&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b7b7e17c-ac89-4d69-b1c2-648f0e582a40.webp&#39; alt=&#39;左右滚动不跟随&#39; title=&#39;左右滚动不跟随&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;cursor&#34;&gt;Cursor&lt;/h3&gt;
&lt;p caption=&#39;Cursor Blinking&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/5736c4cc-e07c-40f1-ae31-e1c16e458a00.webp&#39; alt=&#39;Cursor Blinking&#39; title=&#39;Cursor Blinking&#39;&gt;&lt;/p&gt;
&lt;p&gt;The first setting enables a fade-in/fade-out effect for cursor blinking. The second adds an animation when clicking different positions, showing the cursor’s movement from its previous location. This provides more context about where the new edit position is relative to the last one.&lt;/p&gt;
&lt;h3 id=&#34;find-widget&#34;&gt;Find Widget&lt;/h3&gt;
&lt;p caption=&#39;编辑器右上角查找小部件&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/eb174538-47bb-44b8-ba84-54df4a37555e.webp&#39; alt=&#39;编辑器右上角查找小部件&#39; title=&#39;编辑器右上角查找小部件&#39;&gt;&lt;/p&gt;
&lt;p&gt;Recommend disabling this. During searches, if left on, it creates unnecessary space at the file’s top, causing the editor to jump when closing the search box—annoying.&lt;/p&gt;
&lt;p caption=&#39;空白，不优雅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/83dcf800-6ec9-4a8a-8b49-2e69e419bd72.webp&#39; alt=&#39;空白，不优雅&#39; title=&#39;空白，不优雅&#39;&gt;&lt;/p&gt;
&lt;p&gt;Enabling this might obscure editor content, though it’s usually just import sections or blank lines at the top, so it’s tolerable.&lt;/p&gt;
&lt;p caption=&#39;没空白，优雅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/6878a013-d547-4ccb-9792-32e03a97e9d4.webp&#39; alt=&#39;没空白，优雅&#39; title=&#39;没空白，优雅&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;auto-populate-search-widget&#34;&gt;Auto-Populate Search Widget&lt;/h3&gt;
&lt;p caption=&#39;自动带入，优雅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/f671a305-d7c8-4ae0-b91c-5aa094bbd3a6.webp&#39; alt=&#39;自动带入，优雅&#39; title=&#39;自动带入，优雅&#39;&gt;&lt;/p&gt;
&lt;p&gt;Recommend turning this off. I often search, select something, then search again (for non-selected content). The editor “helpfully” auto-fills the search box with my selection, erasing my previous search—frustrating.&lt;/p&gt;
&lt;h3 id=&#34;minimap&#34;&gt;Minimap&lt;/h3&gt;
&lt;p caption=&#39;右侧小地图&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b8986040-1b86-4bbd-a99b-1b7d551c8b28.webp&#39; alt=&#39;右侧小地图&#39; title=&#39;右侧小地图&#39;&gt;&lt;/p&gt;
&lt;p&gt;I always keep the minimap on the editor’s right side visible. It helps me track my current position in the file and relative to functions/components. I prefer the minimap to stay static and only render color blocks, not every line.&lt;/p&gt;
&lt;h3 id=&#34;suggestions&#34;&gt;Suggestions&lt;/h3&gt;
&lt;p caption=&#39;建议预览&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/1cbc255e-2784-461d-a723-ce5f325130a2.webp&#39; alt=&#39;建议预览&#39; title=&#39;建议预览&#39;&gt;&lt;/p&gt;
&lt;p&gt;Recommend keeping this off (default). It might conflict with Copilot suggestions. Here’s a Copilot suggestion:&lt;/p&gt;
&lt;p caption=&#39;copilot 建议&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/89587ee7-0329-43e7-9a68-814e55bd8e07.webp&#39; alt=&#39;copilot 建议&#39; title=&#39;copilot 建议&#39;&gt;&lt;/p&gt;
&lt;p&gt;And here’s a preview suggestion:&lt;/p&gt;
&lt;p caption=&#39;整个一没必要咱就是说&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/26edace2-f059-4f51-a873-47a743946229.webp&#39; alt=&#39;整个一没必要咱就是说&#39; title=&#39;整个一没必要咱就是说&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;最近建议&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/203628d4-4e0c-4f9a-8289-31ab679750dc.webp&#39; alt=&#39;最近建议&#39; title=&#39;最近建议&#39;&gt;&lt;/p&gt;
&lt;p&gt;This option defaults to “first,” meaning it always uses the default first suggestion. However, I often encounter the issue that when typing &lt;code&gt;wid&lt;/code&gt; in CSS, I naturally expect &lt;code&gt;width&lt;/code&gt;, but it suggests &lt;code&gt;widow&lt;/code&gt;, which I never use. Yet, it consistently ranks first, forcing me to toggle with arrow keys each time. Therefore, I suggest changing this to “recently used,” similar to the “dynamic frequency adjustment” in input methods:&lt;/p&gt;
&lt;p caption=&#39;css 最近建议&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/a1edc147-031b-4347-be78-8c6ec3c71bd7.webp&#39; alt=&#39;css 最近建议&#39; title=&#39;css 最近建议&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;workspace&#34;&gt;Workspace&lt;/h2&gt;
&lt;h3 id=&#34;command-prompt-box&#34;&gt;Command Prompt Box&lt;/h3&gt;
&lt;p caption=&#39;命令建议&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/a62c1989-314d-4675-abbb-cb9f09534480.webp&#39; alt=&#39;命令建议&#39; title=&#39;命令建议&#39;&gt;&lt;/p&gt;
&lt;p&gt;Sometimes, I frequently repeat a command, so opening this command history list is very useful. Additionally, retaining input content is helpful, such as commands starting with “toggle” (e.g., Toggle Screen Capture Mode).&lt;/p&gt;
&lt;p&gt;Note: If the input box disappears after pressing Esc, the content will not be retained the next time it is invoked. Only after selecting and executing a command will the previous input content be retained upon the next invocation.&lt;/p&gt;
&lt;h3 id=&#34;directory-tree&#34;&gt;Directory Tree&lt;/h3&gt;
&lt;p caption=&#39;目录树滚动&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/ff187f9a-5588-40fb-927d-89e701495d94.webp&#39; alt=&#39;目录树滚动&#39; title=&#39;目录树滚动&#39;&gt;&lt;/p&gt;
&lt;p&gt;I usually enable animations because “elegance never goes out of style.” This setting also affects scrolling in the “Settings” interface (previously, enabling smooth scrolling in the editor did not affect the scrolling behavior in the “Settings” interface or the directory tree).&lt;/p&gt;
&lt;h3 id=&#34;quick-open-history&#34;&gt;Quick Open History&lt;/h3&gt;
&lt;p caption=&#39;快速打开带入上次记录&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b4633d53-e671-437c-a4e9-241fe722c953.webp&#39; alt=&#39;快速打开带入上次记录&#39; title=&#39;快速打开带入上次记录&#39;&gt;&lt;/p&gt;
&lt;p&gt;Pressing Cmd + P brings up the Quick Open input box, and remembering history is great. There’s also an option for whether the box automatically disappears when losing focus. In most scenarios, auto-disappearance is preferred, though occasionally it isn’t. For now, I’ll keep the default auto-disappearance.&lt;/p&gt;
&lt;h3 id=&#34;reduce-animation-effects-in-workspace&#34;&gt;Reduce Animation Effects in Workspace&lt;/h3&gt;
&lt;p caption=&#39;绝不减少动画&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/24d1bc51-76bf-415b-9c79-561bc6eb7caf.webp&#39; alt=&#39;绝不减少动画&#39; title=&#39;绝不减少动画&#39;&gt;&lt;/p&gt;
&lt;p&gt;With my 64GB M1 Max, reducing animations is unnecessary (the default is “auto,” which adapts based on system configuration, useful for syncing settings across multiple computers).&lt;/p&gt;
&lt;h3 id=&#34;font-smoothing&#34;&gt;Font Smoothing&lt;/h3&gt;
&lt;p caption=&#39;字体平滑&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/093ce91b-5349-48a0-b563-2a44726f3bf2.webp&#39; alt=&#39;字体平滑&#39; title=&#39;字体平滑&#39;&gt;&lt;/p&gt;
&lt;p&gt;Similar to &lt;code&gt;-webkit-font-smoothing: antialiased;&lt;/code&gt; in CSS, “default” displays the sharpest text on most non-retina screens (subpixel level), while “antialiased” provides pixel-level smoothing, which may make fonts appear thinner. See the comparison:&lt;/p&gt;
&lt;p caption=&#39;default 设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/81378425-4fd6-4954-8d27-5317c822b237.webp&#39; alt=&#39;default 设置&#39; title=&#39;default 设置&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;antialiased 设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/e24ea63a-7536-48bd-8dec-302b335224a9.webp&#39; alt=&#39;antialiased 设置&#39; title=&#39;antialiased 设置&#39;&gt;&lt;/p&gt;
&lt;p&gt;Although this setting is under the “Workspace” section, it also affects the editor area. Enabling “antialiased” makes fonts darker (lower contrast) and thinner in both the editor and workspace areas. I prefer the latter, so I enable it.&lt;/p&gt;
&lt;p&gt;Note: “Subpixel level” does not mean finer than pixels but rather “below pixel level,” indicating a lower level, not a higher one.&lt;/p&gt;
&lt;h3 id=&#34;directory-tree-sticky&#34;&gt;Directory Tree Sticky&lt;/h3&gt;
&lt;p caption=&#39;目录树滚动吸顶&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/ed8074f7-0e31-4a1f-80f8-1618d2264e73.webp&#39; alt=&#39;目录树滚动吸顶&#39; title=&#39;目录树滚动吸顶&#39;&gt;&lt;/p&gt;
&lt;p&gt;Extremely useful—it shows the current scroll path while scrolling. The only drawback is that adding a box-shadow would improve visual distinction:&lt;/p&gt;
&lt;p caption=&#39;目录树吸顶效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/5a7c2102-2730-47a0-b225-738a42d86342.webp&#39; alt=&#39;目录树吸顶效果&#39; title=&#39;目录树吸顶效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;The maximum sticky level can also be adjusted. The default is 7, which is sufficient (the editor’s default sticky level is 5).&lt;/p&gt;
&lt;p&gt;Note: This setting also applies to the “Settings” interface (originally, the settings interface belongs to the workspace, not the editor):&lt;/p&gt;
&lt;p caption=&#39;设置项界面也归它管&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/031c3170-cd43-4775-8401-a95e6c474a75.webp&#39; alt=&#39;设置项界面也归它管&#39; title=&#39;设置项界面也归它管&#39;&gt;&lt;/p&gt;
&lt;p&gt;I changed the directory tree indentation to 14 and prefer always showing the reference lines, as it helps locate files among many siblings:&lt;/p&gt;
&lt;p caption=&#39;目录树缩进&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/7a67020e-c5a4-4114-9ac0-ff3afcb5cb61.webp&#39; alt=&#39;目录树缩进&#39; title=&#39;目录树缩进&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;directory-navigation&#34;&gt;Directory Navigation&lt;/h3&gt;
&lt;p caption=&#39;目录导航显示 icon&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/eae4d00a-68d6-4c64-bab8-34df8fc7f458.webp&#39; alt=&#39;目录导航显示 icon&#39; title=&#39;目录导航显示 icon&#39;&gt;&lt;/p&gt;
&lt;p&gt;Directory navigation is necessary, but file/folder icons are not needed. This significantly distinguishes them from arrays and classes within files, making it very useful:&lt;/p&gt;
&lt;p caption=&#39;面包屑显示效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/57e4fefd-4c76-42cd-b79c-d3d49eb2d1df.webp&#39; alt=&#39;面包屑显示效果&#39; title=&#39;面包屑显示效果&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;modified-tabs&#34;&gt;Modified Tabs&lt;/h3&gt;
&lt;p&gt;Several related settings exist, such as displaying a highlight line above unsaved modified files:&lt;/p&gt;
&lt;p caption=&#39;高亮修改的 tab&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/e4a60c98-ec6f-452d-84ea-e0835ed963bf.webp&#39; alt=&#39;高亮修改的 tab&#39; title=&#39;高亮修改的 tab&#39;&gt;&lt;/p&gt;
&lt;p&gt;By default, a dot is shown. Enabling this option displays both the dot and the line. After restarting the editor, only the blue line above remains (possibly a bug—it should work without restarting).&lt;/p&gt;
&lt;p&gt;Effect:&lt;/p&gt;
&lt;p caption=&#39;修改过的 tab 效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/aea54cb4-158e-4662-a6b5-285fc56fd836.webp&#39; alt=&#39;修改过的 tab 效果&#39; title=&#39;修改过的 tab 效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;Since the “dot” occupies part of the tab space, it reduces the room for tab content. Thus, enabling this option is recommended.&lt;/p&gt;
&lt;h3 id=&#34;mouse-navigation&#34;&gt;Mouse Navigation&lt;/h3&gt;
&lt;p caption=&#39;鼠标前进后退&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/254a8290-8b0a-4ebc-ac36-d453a855719d.webp&#39; alt=&#39;鼠标前进后退&#39; title=&#39;鼠标前进后退&#39;&gt;&lt;/p&gt;
&lt;p&gt;This is a default option, but I’ll mention it: For mice with left-side buttons (for right-handed users, i.e., buttons 4 and 5), navigation becomes effortless and highly convenient.&lt;/p&gt;
&lt;h3 id=&#34;tab-pinning&#34;&gt;Tab Pinning&lt;/h3&gt;
&lt;p caption=&#39;允许 tab 固定，好用&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b8c68b52-f2ec-4fb4-ac39-1fd1af2c9ebd.webp&#39; alt=&#39;允许 tab 固定，好用&#39; title=&#39;允许 tab 固定，好用&#39;&gt;&lt;/p&gt;
&lt;p&gt;Pinned tabs default to the left side of the editor group. However, placing them in a separate row would visually distinguish them from unpinned tabs, as shown here:&lt;/p&gt;
&lt;p caption=&#39;tab 固定效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b6868fe5-65b6-4c6b-bd81-3bd15fada494.webp&#39; alt=&#39;tab 固定效果&#39; title=&#39;tab 固定效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;Note: By default, pinned tabs cannot be closed with the middle mouse button or Cmd + W (pressing these opens an unpinned tab instead of closing the pinned one). This behavior can be modified.&lt;/p&gt;
&lt;p caption=&#39;cmd + w 效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/0259919c-dd23-44f5-89e6-026c2140a424.webp&#39; alt=&#39;cmd + w 效果&#39; title=&#39;cmd + w 效果&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;tab-close-button&#34;&gt;Tab Close Button&lt;/h3&gt;
&lt;p caption=&#39;隐藏关闭按钮&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/c1e66c49-5cab-4d42-9c04-713a6530bb50.webp&#39; alt=&#39;隐藏关闭按钮&#39; title=&#39;隐藏关闭按钮&#39;&gt;&lt;/p&gt;
&lt;p&gt;I’ve always used the left-hand cmd + w shortcut to close tabs, so this option can be disabled. Additionally, I’m more accustomed to double-clicking tabs to close them, but the official response indicates this feature won’t be implemented, as seen here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/Microsoft/vscode/issues/52628#issuecomment-420887497&#34; target=&#34;_blank&#34;&gt; Allow to double click on a tab to close it · Issue #52628 · microsoft/vscode&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;tab-wrap&#34;&gt;Tab Wrap&lt;/h3&gt;
&lt;p caption=&#39;tab wrap&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b72dfdff-c447-457d-89de-8ca3a682281d.webp&#39; alt=&#39;tab wrap&#39; title=&#39;tab wrap&#39;&gt;&lt;/p&gt;
&lt;p&gt;When many tabs are open, scrolling through them becomes cumbersome and makes it hard to maintain an overview. I prefer wrapping tabs, which looks like this:&lt;/p&gt;
&lt;p caption=&#39;wrap 效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/3738c007-8e6d-4397-a3fb-0a5210402ed2.webp&#39; alt=&#39;wrap 效果&#39; title=&#39;wrap 效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;One awkward aspect is that the multi-line tabs created by wrapping might conflict with the “modified tab blue highlight” mentioned earlier (it’s unclear whether the blue line belongs to the tab above or below, requiring a moment to process, which isn’t intuitive). It’s a trade-off between displaying more tab content and maintaining clarity:&lt;/p&gt;
&lt;p caption=&#39;高亮修改 + tab 高亮效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/bba12f32-b355-4550-aba5-c59950ed66fc.webp&#39; alt=&#39;高亮修改 + tab 高亮效果&#39; title=&#39;高亮修改 + tab 高亮效果&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;tab-height&#34;&gt;Tab Height&lt;/h3&gt;
&lt;p caption=&#39;紧凑 tab 布局&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/3224c016-4adb-41d2-82f8-83391960259d.webp&#39; alt=&#39;紧凑 tab 布局&#39; title=&#39;紧凑 tab 布局&#39;&gt;&lt;/p&gt;
&lt;p&gt;A compact layout helps with maintaining an overview and saving space.&lt;/p&gt;
&lt;h3 id=&#34;double-click-to-close-tab&#34;&gt;&lt;s&gt;Double-Click to Close Tab (?)&lt;/s&gt;&lt;/h3&gt;
&lt;p caption=&#39;没懂这个设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/3385a990-b192-4aec-963a-ee5996faf19d.webp&#39; alt=&#39;没懂这个设置&#39; title=&#39;没懂这个设置&#39;&gt;&lt;/p&gt;
&lt;p&gt;This option appears to be the “double-click to close tab” feature the official team claimed they wouldn’t implement (as mentioned above). Even after disabling the potentially conflicting “double-click to auto-expand editor groups,” this setting still doesn’t work. It’s unclear whether I misunderstood it or if it’s a bug.&lt;/p&gt;
&lt;h3 id=&#34;native-tabs&#34;&gt;Native Tabs&lt;/h3&gt;
&lt;p&gt;There are two related settings:&lt;/p&gt;
&lt;p caption=&#39;原生 tab&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/09ad718f-b4c3-40a3-8c92-8e399613540a.webp&#39; alt=&#39;原生 tab&#39; title=&#39;原生 tab&#39;&gt;&lt;/p&gt;
&lt;p&gt;The first setting, when enabled, allows merging multiple project windows into a single window. The “Window” menu will then include a “Merge All Windows” option, making it convenient to switch between multiple projects within one window:&lt;/p&gt;
&lt;p caption=&#39;合并所有窗口&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/8d13f532-cc6a-49a6-9d2c-88aabf5904e3.webp&#39; alt=&#39;合并所有窗口&#39; title=&#39;合并所有窗口&#39;&gt;&lt;/p&gt;
&lt;p&gt;However, this disables the ability to use custom titles (though I don’t find this particularly useful). A custom title looks like this:&lt;/p&gt;
&lt;p caption=&#39;自定义标题栏效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/1d16fa15-9ee4-4647-8458-1103a659a165.webp&#39; alt=&#39;自定义标题栏效果&#39; title=&#39;自定义标题栏效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;If the first setting is enabled, the second becomes ineffective, regardless of whether it’s set to “native” or “custom.” If the first setting is disabled and the second is set to “native,” neither “Merge All Windows” nor “Custom Title Bar” will be available (making the purpose of this setting unclear).&lt;/p&gt;
&lt;h3 id=&#34;file-tree-drag-and-drop&#34;&gt;File Tree Drag-and-Drop&lt;/h3&gt;
&lt;p caption=&#39;最好禁用拖拽文件&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/f4f866b8-c0be-456f-8336-2a229c532f61.webp&#39; alt=&#39;最好禁用拖拽文件&#39; title=&#39;最好禁用拖拽文件&#39;&gt;&lt;/p&gt;
&lt;p&gt;I often accidentally trigger this, leading to hundreds of unintended changes… so I’ve turned it off.&lt;/p&gt;
&lt;h3 id=&#34;auto-collapse-search-results&#34;&gt;Auto-Collapse Search Results&lt;/h3&gt;
&lt;p caption=&#39;少于 10 个的文件夹展开&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/5aab9aef-806d-42a0-ab7c-7d58ae4b4849.webp&#39; alt=&#39;少于 10 个的文件夹展开&#39; title=&#39;少于 10 个的文件夹展开&#39;&gt;&lt;/p&gt;
&lt;p&gt;By default, results are always expanded. However, if there are too many results (often because the search query isn’t fully entered yet), expanding them is unnecessary and can hinder the overview.&lt;/p&gt;
&lt;p&gt;Additionally, if you haven’t added “excluded files” to the search bar, you might encounter an overwhelming number of results (e.g., the &lt;code&gt;.next&lt;/code&gt; directory in a NextJS project), making this setting essential.&lt;/p&gt;
&lt;p&gt;Note that the “expand”/“collapse” threshold of 10 files refers to the number of matching files within a single folder in the search results, not the total number of folders in the entire search:&lt;/p&gt;
&lt;p caption=&#39;多余 10 个的文件夹折叠&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/67c50309-319c-474a-addf-bbd33925d827.webp&#39; alt=&#39;多余 10 个的文件夹折叠&#39; title=&#39;多余 10 个的文件夹折叠&#39;&gt;&lt;/p&gt;
&lt;p&gt;Thus, if a folder contains too many matching files (causing it to collapse), it usually indicates the need to refine the search query.&lt;/p&gt;
&lt;h3 id=&#34;auto-fill-search-box-with-selected-text&#34;&gt;Auto-Fill Search Box with Selected Text&lt;/h3&gt;
&lt;p caption=&#39;全局搜索自动带入选择内容&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/42995472-6453-4e61-881e-d7e0ae5da443.webp&#39; alt=&#39;全局搜索自动带入选择内容&#39; title=&#39;全局搜索自动带入选择内容&#39;&gt;&lt;/p&gt;
&lt;p&gt;Typically, when you select text, you want to search for it. The “Seed On Focus” option saves you a cmd + v step.&lt;/p&gt;
&lt;p&gt;Note that this differs from the “search widget” behavior where selected text is automatically inserted when focusing on the search bar. In the editor, selecting text and then focusing on the search widget might not always be for searching—it could simply be to highlight matching text in the current editor for reference. Automatically replacing the search query in this case often doesn’t align with expectations.&lt;/p&gt;
&lt;p&gt;However, if you select text and then focus on the search view (right panel), it’s likely for searching purposes.&lt;/p&gt;
&lt;p&gt;Additionally, I find it helpful to see line numbers in search results to locate matches within their documents, so displaying line numbers is also necessary.&lt;/p&gt;
&lt;p&gt;The “Smart Case” feature is a handy trick: if the query is all lowercase, it assumes you’re unsure about the exact name; if it includes uppercase letters (e.g., camelCase function names), it performs a case-sensitive search. Very useful.&lt;/p&gt;
&lt;p&gt;Lastly, if the search box remembers the last input, it’s ideal for it to remain selected. If it doesn’t match your current intent, you can simply type over it. If the previous search is still relevant, even better: ↓&lt;/p&gt;
&lt;p caption=&#39;注意与搜索小组件的差别&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b84b5d58-94cb-448f-bd1a-cc129aac13bd.webp&#39; alt=&#39;注意与搜索小组件的差别&#39; title=&#39;注意与搜索小组件的差别&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;ignore-global-ignore-files-in-search&#34;&gt;Ignore Global .ignore Files in Search&lt;/h3&gt;
&lt;p caption=&#39;全局忽略设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/21a936ed-29c9-4613-b6d4-5ad910fc04f0.webp&#39; alt=&#39;全局忽略设置&#39; title=&#39;全局忽略设置&#39;&gt;&lt;/p&gt;
&lt;p&gt;Git has a global default ignore feature. Enabling this option allows files and folders listed in it to be ignored during searches, which is usually necessary.&lt;/p&gt;
&lt;p&gt;There’s also an option to enable ignore in parent directories. I’m not entirely sure what it means—possibly related to multi-level Git management—but I checked it anyway. Since we’re ignoring things, might as well:&lt;/p&gt;
&lt;p caption=&#39;统统勾上&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/fa5ad841-87ee-460f-ad01-d1c7a34f2553.webp&#39; alt=&#39;统统勾上&#39; title=&#39;统统勾上&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;debugging-and-testing&#34;&gt;Debugging and Testing&lt;/h3&gt;
&lt;p&gt;Admittedly, my technical skills are limited, so I rarely use VSCode’s debugging and testing features. I’ve only used them for debugging NodeJS applications like NextJS, where the experience is similar to Chrome. Since I don’t use these features often, I haven’t encountered any pain points or found any optimizations worth mentioning here.&lt;/p&gt;
&lt;h3 id=&#34;file-modification-effects&#34;&gt;File Modification Effects&lt;/h3&gt;
&lt;p caption=&#39;实线比「装订线」好看&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/ef52ba2e-5ff7-4f8f-963d-32da4f899fe6.webp&#39; alt=&#39;实线比「装订线」好看&#39; title=&#39;实线比「装订线」好看&#39;&gt;&lt;/p&gt;
&lt;p&gt;In the line number column, you can choose to display differences as solid lines or “gutters,” as shown here:&lt;/p&gt;
&lt;p caption=&#39;实在不知道装订线存在的意义&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/4c009676-4e9a-4c90-8eab-5ceffae9593d.webp&#39; alt=&#39;实在不知道装订线存在的意义&#39; title=&#39;实在不知道装订线存在的意义&#39;&gt;&lt;/p&gt;
&lt;p&gt;I prefer solid lines, so I unchecked both of these options.&lt;/p&gt;
&lt;h3 id=&#34;disable-git-commit-button&#34;&gt;Disable Git Commit Button&lt;/h3&gt;
&lt;p caption=&#39;移除多余的 UI 按钮&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/6343ab7e-7fc4-4cdc-8b0d-b65eae20b0b7.webp&#39; alt=&#39;移除多余的 UI 按钮&#39; title=&#39;移除多余的 UI 按钮&#39;&gt;&lt;/p&gt;
&lt;p&gt;To be honest, I’ve never used the commit button on the left—I always use the command line for Git operations—so I disabled this option.&lt;/p&gt;
&lt;p&gt;Similarly, this button (which appears to be for GitHub Copilot, automatically generating commit messages) is also disabled. Especially for company projects where we’re required to include task/bug card numbers in commit messages, this “smart” feature becomes even more useless:&lt;/p&gt;
&lt;p caption=&#39;移除自动写提交信息&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/111f48be-42e0-4f6a-b490-7dc832b15045.webp&#39; alt=&#39;移除自动写提交信息&#39; title=&#39;移除自动写提交信息&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;extensions&#34;&gt;Extensions&lt;/h2&gt;
&lt;h3 id=&#34;disable-notifications&#34;&gt;Disable Notifications&lt;/h3&gt;
&lt;p caption=&#39;取消全部扩展通知&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/caa23ff0-089b-4035-90e1-c914e44c61ac.webp&#39; alt=&#39;取消全部扩展通知&#39; title=&#39;取消全部扩展通知&#39;&gt;&lt;/p&gt;
&lt;p&gt;I don’t need any extensions telling me what to do. If I need something, I’ll seek it out myself.&lt;/p&gt;
&lt;h2 id=&#34;terminal&#34;&gt;Terminal&lt;/h2&gt;
&lt;h3 id=&#34;right-click-behavior&#34;&gt;Right-Click Behavior&lt;/h3&gt;
&lt;p caption=&#39;终端邮件默认居然是选中+菜单&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/8c75e65f-ae00-49f1-982f-82c682b4a2f9.webp&#39; alt=&#39;终端邮件默认居然是选中+菜单&#39; title=&#39;终端邮件默认居然是选中+菜单&#39;&gt;&lt;/p&gt;
&lt;p&gt;Normally, you select text with the left mouse button and then right-click for context actions. But VSCode’s default behavior is to show the right-click menu after selecting content (a word). It works, but it’s unnecessary.&lt;/p&gt;
&lt;h3 id=&#34;terminal-maximum-lines&#34;&gt;Terminal Maximum Lines&lt;/h3&gt;
&lt;p caption=&#39;最大记录行&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/67d6cb52-ce20-40d2-ab50-548b7a4aab41.webp&#39; alt=&#39;最大记录行&#39; title=&#39;最大记录行&#39;&gt;&lt;/p&gt;
&lt;p&gt;This isn’t a critical setting, but I occasionally need to review very old log information. With my 64GB of RAM, increasing this value doesn’t hurt.&lt;/p&gt;
&lt;h3 id=&#34;terminal-scroll-animation&#34;&gt;Terminal Scroll Animation&lt;/h3&gt;
&lt;p caption=&#39;奇怪的动画，关了&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/7bd7b784-c3e5-456a-9aea-f0374dfa62b0.webp&#39; alt=&#39;奇怪的动画，关了&#39; title=&#39;奇怪的动画，关了&#39;&gt;&lt;/p&gt;
&lt;p&gt;While I love animations (elegant!), the terminal’s scroll animation seems to have some inertia, making it hard to control the scroll amount. It feels very different from the scrolling behavior in the editor or workspace, so I turned it off.&lt;/p&gt;
&lt;h2 id=&#34;css-less-sass&#34;&gt;CSS/Less/Sass&lt;/h2&gt;
&lt;h3 id=&#34;lint-duplicate-property-warnings&#34;&gt;Lint Duplicate Property Warnings&lt;/h3&gt;
&lt;p caption=&#39;需要设置三次&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/067e3056-d82f-4910-9e47-519259d54577.webp&#39; alt=&#39;需要设置三次&#39; title=&#39;需要设置三次&#39;&gt;&lt;/p&gt;
&lt;p&gt;This is very useful. Sometimes you copy multiple property values (e.g., from browser-inspected styles) and then remove duplicates.&lt;/p&gt;
&lt;h2 id=&#34;git&#34;&gt;Git&lt;/h2&gt;
&lt;h3 id=&#34;auto-stash&#34;&gt;Auto Stash&lt;/h3&gt;
&lt;p caption=&#39;少操作一次&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/4fee6e57-9088-4a39-bbfe-c86ea5c9beb2.webp&#39; alt=&#39;少操作一次&#39; title=&#39;少操作一次&#39;&gt;&lt;/p&gt;
&lt;p&gt;As shown in the image, the description is clear. It’s recommended to enable this to save a step in your workflow.&lt;/p&gt;
&lt;h2 id=&#34;third-party-extensions&#34;&gt;Third-Party Extensions&lt;/h2&gt;
&lt;p&gt;There’s not much to say here. If you’ve installed an extension, it’s because you have a specific need for it, so configure it accordingly.&lt;/p&gt;
&lt;h3 id=&#34;gitlens&#34;&gt;GitLens&lt;/h3&gt;
&lt;p&gt;However, some plugins, like &lt;code&gt;GitLens&lt;/code&gt;, can disable paid feature prompts. For example, when I (accidentally) check Git branch merge status, I might trigger a paid feature prompt. Thankfully, this can be turned off (kudos to the plugin developer for being considerate):&lt;/p&gt;
&lt;p caption=&#39;关掉 GitLens 付费功能提醒&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/28974458-b244-4ed9-919e-affe90c409fe.webp&#39; alt=&#39;关掉 GitLens 付费功能提醒&#39; title=&#39;关掉 GitLens 付费功能提醒&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;one-dark-pro&#34;&gt;One Dark Pro&lt;/h3&gt;
&lt;p caption=&#39;高亮部分代码&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/a1ac82b3-7b1c-4fc4-9327-2cd04796bbee.webp&#39; alt=&#39;高亮部分代码&#39; title=&#39;高亮部分代码&#39;&gt;&lt;/p&gt;
&lt;p&gt;I love this feature—it makes method and function names stand out more clearly:&lt;/p&gt;
&lt;p caption=&#39;效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/d6235ab1-a532-4188-be52-2f29908b31e5.webp&#39; alt=&#39;效果&#39; title=&#39;效果&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;closing-thoughts&#34;&gt;Closing Thoughts&lt;/h2&gt;
&lt;p&gt;After all these settings, remember: what works for you is what matters most. Here’s to efficient work and leaving the office early!&lt;/p&gt;
</description>
            <pubDate>Thu, 21 Dec 2023 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/life/make-vscode-great-forever.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/life/make-vscode-great-forever.html</guid>
            
                <category>生活</category>
            
                <category>折腾</category>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>JavaScript</category>
            
                <category>工作流</category>
            
                <category>VSCode</category>
            
                <category>设置</category>
            
            
                <category>life</category>
            
        </item>
        
        <item>
            <title>Watching Beijing Unicom IPTV on Apple TV</title>
            <description>&lt;h2 id=&#34;preface&#34;&gt;Preface&lt;/h2&gt;
&lt;p&gt;In a &lt;a href=&#34;/life/the-way-to-watching-tv.html&#34;&gt;previous blog post&lt;/a&gt;, I discussed home theater solutions, mentioning the use of IPTV program addresses (with .m3u extension) scraped by others online, which can be directly played in iPlayTV. However, these addresses often expire after some time because China Unicom’s IPTV servers periodically update the program streaming URLs, while the address entered on Apple TV remains static and cannot update in time. This article aims to solve this issue.&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(253, 235, 236); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;**Important Notes Before Proceeding:** I use Beijing Unicom broadband, with the **optical modem in bridge mode, the router handling PPPoE, and a soft router R4S connected as a side router**. This tutorial is tailored to this network topology. Other setups, such as &#34;optical modem in bridge mode + R4S as the main router for PPPoE&#34; or &#34;optical modem handling PPPoE + R4S as a side router,&#34; may also work, but the key R4S settings might differ slightly from this guide. I recommend researching further to grasp the core principles rather than copying my setup exactly. &lt;/span&gt;&lt;/p&gt;  
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 243, 219); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;Similarly, if your optical modem handles PPPoE directly, you can connect a cable from the modem to a side/main router to convert multicast to unicast. Alternatively, you can play multicast streams directly on supported devices, avoiding the complexity of my method. The approach depends on your home network topology. &lt;/span&gt;&lt;/p&gt;  
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 236, 221); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;This tutorial has only been tested successfully with Beijing Unicom IPTV. Other regions may differ. If you encounter content strongly tied to a specific region, replace it with the appropriate details for your location. &lt;/span&gt;&lt;/p&gt;  
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(244, 238, 238); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;Before making any changes, back up all router and device settings as a precaution. &lt;/span&gt;&lt;/p&gt;  
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 243, 219); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;I use a soft router R4S with two network ports. If your network topology matches mine, ensure your soft router has at least two ports—one to connect to the main router and another to the optical modem. &lt;/span&gt;&lt;/p&gt;  
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgba(244, 240, 247, 0.8); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;My home network setup:  
- Optical modem subnet: 192.168.1.x  
- Main router subnet: 192.168.5.x  
- Optical modem IP: 192.168.1.1  
- R4S IP: 192.168.5.2  
- Main router LAN IP: 192.168.5.1  
- Router WAN IP: 192.168.1.2  &lt;/span&gt;&lt;/p&gt;  
&lt;h2 id=&#34;verifying-free-access-support&#34;&gt;Verifying Free Access Support&lt;/h2&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(231, 243, 248); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;Currently, Beijing Unicom IPTV does not enforce authentication. However, based on my research, some carriers in other regions encrypt or authenticate IPTV, requiring the use of the MAC address from the carrier-provided IPTV box (which handles decryption) to enable playback on any LAN device via a soft router without the box. Implementing this is complex and beyond the scope of this tutorial. &lt;/span&gt;&lt;/p&gt;  
&lt;h3 id=&#34;download-vlc-in-advance&#34;&gt;Download VLC in Advance&lt;/h3&gt;
&lt;p&gt;When using the optical modem in bridge mode with the router handling PPPoE, connecting directly to the modem won’t provide internet access. Therefore, download the VLC media player on your computer beforehand for testing. Download VLC here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.videolan.org/vlc/&#34; target=&#34;_blank&#34;&gt; Official download of VLC media player, the best Open Source player - VideoLAN&lt;/a&gt;&lt;/p&gt;  
&lt;h3 id=&#34;connecting-to-the-optical-modem&#34;&gt;Connecting to the Optical Modem&lt;/h3&gt;
&lt;p&gt;Connect your &lt;strong&gt;computer to the IPTV port of the optical modem using a wired connection&lt;/strong&gt; (if no IPTV port is found on the modem, it means the modem supports hybrid insertion, meaning the ports are not differentiated between broadband and IPTV—any port will work). Then, in the VLC software:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Go to File &amp;gt; Open Network.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on Open RTP/UDP Stream below.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select RTP for Protocol, Multicast for Mode, and enter &lt;code&gt;239.3.1.241&lt;/code&gt; (or &lt;code&gt;rtp://239.3.1.241&lt;/code&gt;—can’t recall which one exactly) for IP Address, and &lt;code&gt;8000&lt;/code&gt; for the port.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After clicking Open, if you can see Beijing TV, it means you can enjoy it for free.&lt;/p&gt;
&lt;p caption=&#39;VLC RTP 播放&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/ea9168ee-6086-42df-b6b4-269900eb5592.webp&#39; alt=&#39;VLC RTP 播放&#39; title=&#39;VLC RTP 播放&#39;&gt;&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 243, 219); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;💡&lt;/span&gt;&lt;span&gt;Here, `rtp://239.3.1.241:8000` is the multicast address for Beijing TV. This address may change in the future. For the most accurate address, you can go to https://raw.githubusercontent.com/qwerttvv/Beijing-IPTV/master/IPTV-Unicom.m3u` and test any IP address following an rtp path. &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&#34;explanation-of-basic-concepts&#34;&gt;Explanation of Basic Concepts&lt;/h2&gt;
&lt;p&gt;Skip to the next section if you’re not interested in the details.&lt;/p&gt;
&lt;h3 id=&#34;iptv&#34;&gt;IPTV&lt;/h3&gt;
&lt;p&gt;According to Wikipedia’s definition of IPTV:&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(241, 241, 239); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;📖&lt;/span&gt;&lt;span&gt;Internet Protocol Television (abbreviated as IPTV) is a form of broadband television. IPTV is a system that delivers television content via broadband networks, transmitting broadcast programs through the Internet Protocol (IP) to provide digital TV services to subscribers. Since it requires internet access, IPTV service providers often bundle related services such as internet connectivity and IP telephony, also known as &#34;Triple Play&#34; services. As a type of digital television, conventional TVs need a corresponding set-top box to receive channels, which is why providers typically offer video-on-demand services alongside. Although it uses broadband networks and the Internet Protocol, IPTV does not necessarily rely on the public internet—it often transmits via local area networks to ensure quality. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;From this, we can see that IPTV is generally a service provided by broadband providers, allowing users to watch TV through it.&lt;/p&gt;
&lt;h3 id=&#34;multicast&#34;&gt;Multicast&lt;/h3&gt;
&lt;p&gt;A technical method for implementing IPTV playback, known as “multicast” in English. The exact concept isn’t crucial to grasp, but it’s helpful to understand its advantages over unicast:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Advantages include:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Does not consume internet bandwidth.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The IPTV box serves as an authentication device. Since IPTV operators broadcast to a group, the load on their servers is relatively light.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Disadvantages include:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Must be connected via cable to the IPTV port of the optical modem (some modems support mixed ports, meaning they don’t distinguish between IPTV and broadband ports). This restricts usage to devices connected to the IPTV box, preventing WiFi-enabled devices from watching network TV.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;unicast&#34;&gt;Unicast&lt;/h3&gt;
&lt;p&gt;An older technical method for implementing IPTV playback, used when IPTV had fewer users. Compared to multicast, its disadvantages are the other’s advantages, and vice versa:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Advantages include:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Once connected, the local network supports WiFi, allowing any device to play the stream.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Disadvantages include:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Requires a one-to-one connection with the server, increasing server load. Playback may lag when there are many users.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Consumes broadband bandwidth, as playback is directly conducted over the internet (similar to watching live streams today).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;udpxy&#34;&gt;udpxy&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; udpxy is **a proxy server that converts UDP streams into HTTP streams**, enabling IPTV streams to be played on various devices. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;When multicast addresses cannot be obtained directly, they can be converted to unicast addresses. For example, if the multicast addresses are &lt;code&gt;a:b&lt;/code&gt; and &lt;code&gt;d:e&lt;/code&gt; (where &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;d&lt;/code&gt; are IP addresses, and &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;e&lt;/code&gt; are ports), they can be converted into uniform unicast addresses like &lt;code&gt;z/a/b&lt;/code&gt; and &lt;code&gt;z/d/e&lt;/code&gt;. The player simply needs to listen to the address &lt;code&gt;z&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;m3u&#34;&gt;M3U&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; **M3U** (short for MP3 URL) is a file format designed for multimedia playlists. Initially created for audio files like MP3, it is now increasingly used by software to play video file lists. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;An M3U file is a plain text file containing all the multicast addresses for playback. By reading this file on an Apple TV or computer, the player can access and stream the video addresses listed within.&lt;/p&gt;
&lt;h3 id=&#34;epg&#34;&gt;EPG&lt;/h3&gt;
&lt;p&gt;EPG (Electronic Program Guide) contains program schedule information. After obtaining the M3U address in the previous step, which consists of individual IP addresses, how does one identify which channel each IP address corresponds to? This is where EPG comes into play—it pairs each address with program details, including brief channel descriptions. EPG data is typically broadcast alongside the video signal.&lt;/p&gt;
&lt;h2 id=&#34;network-topology&#34;&gt;Network Topology&lt;/h2&gt;
&lt;p caption=&#39;网络拓扑-主路由拨号&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/0349a1d5-caeb-4233-b36b-8bc1c7db7565.webp&#39; alt=&#39;网络拓扑-主路由拨号&#39; title=&#39;网络拓扑-主路由拨号&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;hands-on-practice&#34;&gt;Hands-on Practice&lt;/h2&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 243, 219); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;Before starting, check if free usage is supported as mentioned at the beginning. &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&#34;setting-up-a-soft-router&#34;&gt;Setting Up a Soft Router&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Installing udpxy and luci-udpxy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This step involves standard operations. The UI installation is the most convenient method, as shown in the image (do not enable it immediately after installation; enable it in the final step):&lt;/p&gt;
&lt;p caption=&#39;软件安装界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/2be1ca35-90a8-4936-89cd-5458557ac064.webp&#39; alt=&#39;软件安装界面&#39; title=&#39;软件安装界面&#39;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Creating/Configuring Network Interfaces&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This step ensures the software router can recognize data coming from the optical modem.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Under &lt;code&gt;Network &amp;gt; Interfaces&lt;/code&gt;, create a new interface and name it arbitrarily, such as &lt;code&gt;IPTV&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;新建 IPTV 接口&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/a938e2a7-9723-40f5-8b86-3ed151ba12c5.webp&#39; alt=&#39;新建 IPTV 接口&#39; title=&#39;新建 IPTV 接口&#39;&gt;&lt;/p&gt;
&lt;p&gt;Pay attention to the arrow-marked part—I’ve already created it, so it shows “IPTV” in parentheses (it wasn’t there when first created). Here, eth1 is my LAN port connected to the main router, while eth0 is another interface connected to the optical modem (IPTV port). I modified this earlier—by default, eth0 is the LAN port and eth1 is the WAN port, but that’s irrelevant. This step repurposes the WAN port as the IPTV port.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure the gateway hop and firewall settings for the “IPTV” interface:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;配置网关跃点&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/d1df1f84-7240-4eb9-872b-6871f9c895a6.webp&#39; alt=&#39;配置网关跃点&#39; title=&#39;配置网关跃点&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;防火墙配置到 wan 上&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/c0e4032b-f066-4fcf-b906-dae85a37d070.webp&#39; alt=&#39;防火墙配置到 wan 上&#39; title=&#39;防火墙配置到 wan 上&#39;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure the gateway hop count for the WAN port:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;Wan 口网关跃点&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/21347824-ca57-4715-9fe9-cd7b6be0f282.webp&#39; alt=&#39;Wan 口网关跃点&#39; title=&#39;Wan 口网关跃点&#39;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure LAN Port IGMP Snooping:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;IGMP 嗅探&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/b075aaec-042b-4f9d-9437-27ea4d17211e.webp&#39; alt=&#39;IGMP 嗅探&#39; title=&#39;IGMP 嗅探&#39;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure network firewall&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;网络防火墙配置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/577418eb-c3f6-43be-b73b-b1cf3ab5dcea.webp&#39; alt=&#39;网络防火墙配置&#39; title=&#39;网络防火墙配置&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;防火墙配置2&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/b3ce719b-a05d-41ef-ab4a-12eb866178a8.webp&#39; alt=&#39;防火墙配置2&#39; title=&#39;防火墙配置2&#39;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Configuring the udpxy Service&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure as shown in the diagram, noting that eth0 here refers to the WAN port—don’t mix it up:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;打开 UDPXY&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/bcd40c05-ed7f-43fd-b833-4cee94948150.webp&#39; alt=&#39;打开 UDPXY&#39; title=&#39;打开 UDPXY&#39;&gt;&lt;/p&gt;
&lt;p&gt;Finally, attempt to open [&lt;a href=&#34;http://192.168.5.2:4022/status&#34;&gt;http://192.168.5.2:4022/status&lt;/a&gt;]([object Object]) to verify whether the udpxy service has started successfully:&lt;/p&gt;
&lt;p caption=&#39;看到这个就表示成功了&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/189e9cb4-80c9-4b5d-8f57-722660e66145.webp&#39; alt=&#39;看到这个就表示成功了&#39; title=&#39;看到这个就表示成功了&#39;&gt;&lt;/p&gt;
&lt;p&gt;Afterwards, change the address &lt;code&gt;rtp://239.3.1.241:8000&lt;/code&gt; that was previously opened with VLC to `&lt;a href=&#34;http://192.168.2.1:4022/rtp/239.3.1.241:8000%60%60&#34;&gt;http://192.168.2.1:4022/rtp/239.3.1.241:8000``&lt;/a&gt; and try opening it again (no need to click the “Open RTP/UDP Stream” below; just open it directly in the URL).&lt;/p&gt;
&lt;p caption=&#39;尝试将 RTP 变 HTTP 播放&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/582e015c-4260-4181-a81c-5eafc97a1132.webp&#39; alt=&#39;尝试将 RTP 变 HTTP 播放&#39; title=&#39;尝试将 RTP 变 HTTP 播放&#39;&gt;&lt;/p&gt;
&lt;p&gt;Then simply double-click to play:&lt;/p&gt;
&lt;p caption=&#39;成功画面↑&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/215aa895-e673-419a-a4fc-b94603af8baa.webp&#39; alt=&#39;成功画面↑&#39; title=&#39;成功画面↑&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;using-playback-software&#34;&gt;Using Playback Software&lt;/h3&gt;
&lt;p&gt;I watch TV on Apple TV 4K and have tried several playback apps. Here’s a brief overview:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;iPlayTV couldn’t play the streams—no idea why.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fileball allowed channel switching up and down, but crashed immediately when selecting a channel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Eventually, I chose “TV+” by the same developer as IIVA, priced at 38 HKD in the Hong Kong region (awkwardly, it went free the day after I bought it).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-to-automatically-update-addresses&#34;&gt;How to Automatically Update Addresses&lt;/h2&gt;
&lt;p&gt;The playback addresses for China Unicom’s IPTV change periodically—sometimes the port changes, sometimes the IP address. When this happens, the playback method stops working. So, what’s the solution?&lt;/p&gt;
&lt;p&gt;Some kind folks online have performed complex monitoring operations, such as this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.friskit.me/2020/05/31/bjunicom-network.html&#34; target=&#34;_blank&#34;&gt; 光纤入户光猫改桥接+内网转发IPTV=任意设备看电视直播 - Botian&#39;s Blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;They captured the communication data between the IPTV set-top box and China Unicom’s servers, extracting the addresses. Thus, we can directly use them, such as:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/qwerttvv/Beijing-IPTV/blob/master/README.md&#34; target=&#34;_blank&#34;&gt; github.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, there’s one key point here: your home’s soft router address needs to match this kind person’s address (192.168.123.1), and then change the udpxy port to 23234. This way, you can directly input this address into TV+:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://raw.githubusercontent.com/qwerttvv/Beijing-IPTV/master/IPTV-Unicom.m3u&#34; target=&#34;_blank&#34;&gt; raw.githubusercontent.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Otherwise, you’ll have to actively monitor changes to this file and update accordingly.&lt;/p&gt;
&lt;p&gt;Here, I spent ten minutes deploying an API service on Vercel while also enabling Vercel’s Cron Jobs service to periodically execute functions for detecting changes. The code is as follows—you can deploy your own setup as well:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;import&lt;/span&gt; type &amp;#123; &lt;span class=&#34;hljs-title class_&#34;&gt;NextApiRequest&lt;/span&gt;, &lt;span class=&#34;hljs-title class_&#34;&gt;NextApiResponse&lt;/span&gt; &amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;next&amp;#x27;&lt;/span&gt;;&lt;br&gt;&lt;br&gt;type &lt;span class=&#34;hljs-title class_&#34;&gt;ResponseData&lt;/span&gt; = &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: string,&lt;br&gt;&amp;#125;;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; * 该函数用来将网友通过 IPTV 盒子抓包获取的联通单播地址，转成自己的单播地址&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; * 该函数每天 3 点触发一次，定时检测网友的单播地址是否有更新，使用 vercel corn 任务进行&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; * &lt;span class=&#34;hljs-doctag&#34;&gt;TODO:&lt;/span&gt; 该函数未做鉴权，任何人都可以手动触发检测，为了防止滥用可以加上鉴权，但是 corn 似乎没这个功能&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;handler&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-params&#34;&gt;  request: NextApiRequest,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-params&#34;&gt;  response: NextApiResponse&amp;lt;ResponseData&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 步骤&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 1. 获取网友通过监听盒子数据包抓取的（自己搞比较费劲，直接用现成的了）联通 IPTV 永久地址（rtp 协议的组播地址，多用户通用），获取其内容&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 2. 添加本地 udpxy 转发地址&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 3. 获取之前的 github gist 内容以对比二者&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 4. 有差异，则更新 github gist 内容&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 5. 没有，则不做操作&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 环境变量，自己在 Vercel 中设置好&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; token = process.&lt;span class=&#34;hljs-property&#34;&gt;env&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;GITHUB_TOKEN&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; gist = process.&lt;span class=&#34;hljs-property&#34;&gt;env&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;GIST_URL&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; id = process.&lt;span class=&#34;hljs-property&#34;&gt;env&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;GIST_ID&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;fetch&lt;/span&gt;(&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;https://raw.githubusercontent.com/qwerttvv/Beijing-IPTV/master/IPTV-Unicom.m3u&amp;#x27;&lt;/span&gt;&lt;br&gt;  )&lt;br&gt;    .&lt;span class=&#34;hljs-title function_&#34;&gt;then&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;async&lt;/span&gt; (res) =&amp;gt; &amp;#123;&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (!res.&lt;span class=&#34;hljs-property&#34;&gt;ok&lt;/span&gt;) &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;获取源地址异常&amp;#x27;&lt;/span&gt;);&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;          &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;获取源地址异常&amp;#x27;&lt;/span&gt;,&lt;br&gt;        &amp;#125;);&lt;br&gt;      &amp;#125;&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; src = &lt;span class=&#34;hljs-keyword&#34;&gt;await&lt;/span&gt; res.&lt;span class=&#34;hljs-title function_&#34;&gt;text&lt;/span&gt;();&lt;br&gt;      &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 替换网友的本地单播地址为我的，其实你也可以将自己家的路由器网段设置成跟网友的一样（192.168.123.x），udpxy 端口转发设置成跟网友一样（23234），你就可以直接使用该地址了&lt;/span&gt;&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; newGist = src.&lt;span class=&#34;hljs-title function_&#34;&gt;replace&lt;/span&gt;(&lt;br&gt;        &lt;span class=&#34;hljs-regexp&#34;&gt;/http\:\/\/192\.168\.123\.1\:23234/g&lt;/span&gt;,&lt;br&gt;        &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;http://192.168.5.2:4022&amp;#x27;&lt;/span&gt;&lt;br&gt;      );&lt;br&gt;      response.&lt;span class=&#34;hljs-title function_&#34;&gt;setHeader&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Content-Type&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;text/html; charset=utf-8&amp;#x27;&lt;/span&gt;);&lt;br&gt;      &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 获取 gist 的 raw 内容，需要加个 cache-bust 否则每次请求会被缓存&lt;/span&gt;&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;fetch&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;100000&lt;/span&gt;)&amp;#125;&lt;/span&gt;`&lt;/span&gt;, &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-attr&#34;&gt;headers&lt;/span&gt;: &amp;#123;&lt;br&gt;          &lt;span class=&#34;hljs-title class_&#34;&gt;Authorization&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`Bearer &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;token&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;        &amp;#125;,&lt;br&gt;      &amp;#125;)&lt;br&gt;        .&lt;span class=&#34;hljs-title function_&#34;&gt;then&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;async&lt;/span&gt; (pre) =&amp;gt; &amp;#123;&lt;br&gt;          &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; preGist = &lt;span class=&#34;hljs-keyword&#34;&gt;await&lt;/span&gt; pre.&lt;span class=&#34;hljs-title function_&#34;&gt;text&lt;/span&gt;();&lt;br&gt;          &lt;span class=&#34;hljs-comment&#34;&gt;// console.log(&amp;#x27;preGist:&amp;#x27;, preGist);&lt;/span&gt;&lt;br&gt;          &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (&lt;span class=&#34;hljs-title class_&#34;&gt;JSON&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;stringify&lt;/span&gt;(newGist) !== &lt;span class=&#34;hljs-title class_&#34;&gt;JSON&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;stringify&lt;/span&gt;(preGist)) &amp;#123;&lt;br&gt;            &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 更新 Gist&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; files = &amp;#123;&lt;br&gt;              &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;IPTV.m3u&amp;#x27;&lt;/span&gt;: &amp;#123;&lt;br&gt;                &lt;span class=&#34;hljs-attr&#34;&gt;content&lt;/span&gt;: newGist,&lt;br&gt;              &amp;#125;,&lt;br&gt;            &amp;#125;;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;fetch&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`https://api.github.com/gists/&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;id&amp;#125;&lt;/span&gt;`&lt;/span&gt;, &amp;#123;&lt;br&gt;              &lt;span class=&#34;hljs-attr&#34;&gt;method&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;PATCH&amp;#x27;&lt;/span&gt;,&lt;br&gt;              &lt;span class=&#34;hljs-attr&#34;&gt;headers&lt;/span&gt;: &amp;#123;&lt;br&gt;                &lt;span class=&#34;hljs-title class_&#34;&gt;Authorization&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`Bearer &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;token&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Content-Type&amp;#x27;&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;text/plain&amp;#x27;&lt;/span&gt;,&lt;br&gt;              &amp;#125;,&lt;br&gt;              &lt;span class=&#34;hljs-attr&#34;&gt;body&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;JSON&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;stringify&lt;/span&gt;(&amp;#123; files &amp;#125;),&lt;br&gt;            &amp;#125;)&lt;br&gt;              .&lt;span class=&#34;hljs-title function_&#34;&gt;then&lt;/span&gt;(&lt;span class=&#34;hljs-function&#34;&gt;(&lt;span class=&#34;hljs-params&#34;&gt;s&lt;/span&gt;) =&amp;gt;&lt;/span&gt; &amp;#123;&lt;br&gt;                &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;br&gt;                  &lt;span class=&#34;hljs-string&#34;&gt;`更新成功: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;                    &lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;1000000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;                  )&amp;#125;&lt;/span&gt;`&lt;/span&gt;&lt;br&gt;                );&lt;br&gt;                &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;                  &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`更新成功: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;                    &lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;1000000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;                  )&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;                &amp;#125;);&lt;br&gt;              &amp;#125;)&lt;br&gt;              .&lt;span class=&#34;hljs-title function_&#34;&gt;catch&lt;/span&gt;(&lt;span class=&#34;hljs-function&#34;&gt;(&lt;span class=&#34;hljs-params&#34;&gt;e&lt;/span&gt;) =&amp;gt;&lt;/span&gt; &amp;#123;&lt;br&gt;                &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`更新失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;);&lt;br&gt;                &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;                  &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`更新失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;                &amp;#125;);&lt;br&gt;              &amp;#125;);&lt;br&gt;          &amp;#125;&lt;br&gt;          &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;br&gt;            &lt;span class=&#34;hljs-string&#34;&gt;`未变化: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;1000000&lt;/span&gt;)&amp;#125;&lt;/span&gt;`&lt;/span&gt;&lt;br&gt;          );&lt;br&gt;          &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;            &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`未变化: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;              &lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;1000000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;            )&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;          &amp;#125;);&lt;br&gt;        &amp;#125;)&lt;br&gt;        .&lt;span class=&#34;hljs-title function_&#34;&gt;catch&lt;/span&gt;(&lt;span class=&#34;hljs-function&#34;&gt;(&lt;span class=&#34;hljs-params&#34;&gt;e&lt;/span&gt;) =&amp;gt;&lt;/span&gt; &amp;#123;&lt;br&gt;          &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`获取自己的 gist 失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;);&lt;br&gt;          &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;            &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`获取自己的 gist 失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;          &amp;#125;);&lt;br&gt;        &amp;#125;);&lt;br&gt;    &amp;#125;)&lt;br&gt;    .&lt;span class=&#34;hljs-title function_&#34;&gt;catch&lt;/span&gt;(&lt;span class=&#34;hljs-function&#34;&gt;(&lt;span class=&#34;hljs-params&#34;&gt;e&lt;/span&gt;) =&amp;gt;&lt;/span&gt; &amp;#123;&lt;br&gt;      &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`获取别人的源失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;);&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`获取别人的源失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;      &amp;#125;);&lt;br&gt;    &amp;#125;);&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Corn Jobs Service Configuration:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs json&#34;&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;crons&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-punctuation&#34;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;      &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;/api/get&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;schedule&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;0 15 * * *&amp;quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-punctuation&#34;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;After pulling this file, if there are updates, the gist file will be automatically updated:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://gist.githubusercontent.com/Xheldon/73bf97cb5ac5db2f5237264556b20951/raw/ea44694028a38baefff04ea46c02795e448d76f0/IPTV.m3u&#34; target=&#34;_blank&#34;&gt; gist.githubusercontent.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this way, I only need to hardcode this gist address in TV+, and it will automatically update when the author updates the address.&lt;/p&gt;
&lt;h2 id=&#34;reference-links&#34;&gt;Reference Links&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.lishun.me/iptvhelper-guide&#34; target=&#34;_blank&#34;&gt; 单线融合IPTV到家庭局域网最简单的方法：路由+桥接混合模式&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.friskit.me/2020/05/31/bjunicom-network.html&#34; target=&#34;_blank&#34;&gt; 光纤入户光猫改桥接+内网转发IPTV=任意设备看电视直播 - Botian&#39;s Blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.haoyizebo.com/posts/6a0c2301/&#34; target=&#34;_blank&#34;&gt; 北京联通白嫖 IPTV&lt;/a&gt;&lt;/p&gt;</description>
            <pubDate>Mon, 30 Oct 2023 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/en/life/iptv-for-apple-tv-in-beijing.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/en/life/iptv-for-apple-tv-in-beijing.html</guid>
            
                <category>生活</category>
            
                <category>Apple</category>
            
                <category>折腾</category>
            
                <category>经验</category>
            
                <category>苹果</category>
            
                <category>网络</category>
            
                <category>路由器</category>
            
                <category>教程</category>
            
            
                <category>life</category>
            
        </item>
        
    </channel>
    </rss>